Commit 3a770845 authored by Lee Tickett's avatar Lee Tickett Committed by Jan Provaznik

Add requirements visibility access project settings

parent 0d9d2706
...@@ -67,6 +67,11 @@ export default { ...@@ -67,6 +67,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
requirementsAvailable: {
type: Boolean,
required: false,
default: false,
},
visibilityHelpPath: { visibilityHelpPath: {
type: String, type: String,
required: false, required: false,
...@@ -131,6 +136,7 @@ export default { ...@@ -131,6 +136,7 @@ export default {
snippetsAccessLevel: featureAccessLevel.EVERYONE, snippetsAccessLevel: featureAccessLevel.EVERYONE,
pagesAccessLevel: featureAccessLevel.EVERYONE, pagesAccessLevel: featureAccessLevel.EVERYONE,
metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS, metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
requirementsAccessLevel: featureAccessLevel.EVERYONE,
containerRegistryEnabled: true, containerRegistryEnabled: true,
lfsEnabled: true, lfsEnabled: true,
requestAccessEnabled: true, requestAccessEnabled: true,
...@@ -233,6 +239,10 @@ export default { ...@@ -233,6 +239,10 @@ export default {
featureAccessLevel.PROJECT_MEMBERS, featureAccessLevel.PROJECT_MEMBERS,
this.metricsDashboardAccessLevel, this.metricsDashboardAccessLevel,
); );
this.requirementsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.requirementsAccessLevel,
);
if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) { if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) {
// When from Internal->Private narrow access for only members // When from Internal->Private narrow access for only members
this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS; this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
...@@ -256,6 +266,9 @@ export default { ...@@ -256,6 +266,9 @@ export default {
this.pagesAccessLevel = featureAccessLevel.EVERYONE; this.pagesAccessLevel = featureAccessLevel.EVERYONE;
if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS) if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE; this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.requirementsAccessLevel = featureAccessLevel.EVERYONE;
this.highlightChanges(); this.highlightChanges();
} }
}, },
...@@ -470,6 +483,18 @@ export default { ...@@ -470,6 +483,18 @@ export default {
/> />
</project-setting-row> </project-setting-row>
</div> </div>
<project-setting-row
v-if="requirementsAvailable"
ref="requirements-settings"
:label="s__('ProjectSettings|Requirements')"
:help-text="s__('ProjectSettings|Requirements management system for this project')"
>
<project-feature-setting
v-model="requirementsAccessLevel"
:options="featureAccessLevelOptions"
name="project[project_feature_attributes][requirements_access_level]"
/>
</project-setting-row>
<project-setting-row <project-setting-row
ref="wiki-settings" ref="wiki-settings"
:label="s__('ProjectSettings|Wiki')" :label="s__('ProjectSettings|Wiki')"
......
...@@ -2,6 +2,7 @@ export default { ...@@ -2,6 +2,7 @@ export default {
data() { data() {
return { return {
packagesEnabled: false, packagesEnabled: false,
requirementsEnabled: false,
}; };
}, },
watch: { watch: {
......
...@@ -356,6 +356,20 @@ class ProjectsController < Projects::ApplicationController ...@@ -356,6 +356,20 @@ class ProjectsController < Projects::ApplicationController
.merge(import_url_params) .merge(import_url_params)
end end
def project_feature_attributes
%i[
builds_access_level
issues_access_level
forking_access_level
merge_requests_access_level
repository_access_level
snippets_access_level
wiki_access_level
pages_access_level
metrics_dashboard_access_level
]
end
def project_params_attributes def project_params_attributes
[ [
:allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline,
...@@ -391,22 +405,10 @@ class ProjectsController < Projects::ApplicationController ...@@ -391,22 +405,10 @@ class ProjectsController < Projects::ApplicationController
:initialize_with_readme, :initialize_with_readme,
:autoclose_referenced_issues, :autoclose_referenced_issues,
:suggestion_commit_message, :suggestion_commit_message,
project_feature_attributes: %i[
builds_access_level
issues_access_level
forking_access_level
merge_requests_access_level
repository_access_level
snippets_access_level
wiki_access_level
pages_access_level
metrics_dashboard_access_level
],
project_setting_attributes: %i[ project_setting_attributes: %i[
show_default_award_emojis show_default_award_emojis
] ]
] ] + [project_feature_attributes: project_feature_attributes]
end end
def project_params_create_attributes def project_params_create_attributes
......
...@@ -37,7 +37,8 @@ module Featurable ...@@ -37,7 +37,8 @@ module Featurable
class_methods do class_methods do
def set_available_features(available_features = []) def set_available_features(available_features = [])
@available_features = available_features @available_features ||= []
@available_features += available_features
class_eval do class_eval do
available_features.each do |feature| available_features.each do |feature|
......
...@@ -88,3 +88,5 @@ module ProjectFeaturesCompatibility ...@@ -88,3 +88,5 @@ module ProjectFeaturesCompatibility
project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend
end end
end end
ProjectsHelper.prepend_if_ee('EE::ProjectFeaturesCompatibility')
---
title: Add requirements visibility/access project settings
merge_request: 34420
author: Lee Tickett
type: added
# frozen_string_literal: true
class AddRequirementsAccessLevelToProjectFeatures < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :project_features, :requirements_access_level, :integer, default: 20, null: false
end
end
def down
with_lock_retries do
remove_column :project_features, :requirements_access_level, :integer
end
end
end
...@@ -13956,7 +13956,8 @@ CREATE TABLE public.project_features ( ...@@ -13956,7 +13956,8 @@ CREATE TABLE public.project_features (
repository_access_level integer DEFAULT 20 NOT NULL, repository_access_level integer DEFAULT 20 NOT NULL,
pages_access_level integer NOT NULL, pages_access_level integer NOT NULL,
forking_access_level integer, forking_access_level integer,
metrics_dashboard_access_level integer metrics_dashboard_access_level integer,
requirements_access_level integer DEFAULT 20 NOT NULL
); );
CREATE SEQUENCE public.project_features_id_seq CREATE SEQUENCE public.project_features_id_seq
...@@ -23427,6 +23428,7 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -23427,6 +23428,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200615121217 20200615121217
20200615123055 20200615123055
20200615193524 20200615193524
20200615203153
20200615232735 20200615232735
20200615234047 20200615234047
20200616145031 20200616145031
......
...@@ -1048,6 +1048,7 @@ POST /projects ...@@ -1048,6 +1048,7 @@ POST /projects
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` | | `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
| `requirements_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
| `emails_disabled` | boolean | no | Disable email notifications | | `emails_disabled` | boolean | no | Disable email notifications |
| `show_default_award_emojis` | boolean | no | Show default award emojis | | `show_default_award_emojis` | boolean | no | Show default award emojis |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
...@@ -1119,6 +1120,7 @@ POST /projects/user/:user_id ...@@ -1119,6 +1120,7 @@ POST /projects/user/:user_id
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` | | `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
| `requirements_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
| `emails_disabled` | boolean | no | Disable email notifications | | `emails_disabled` | boolean | no | Disable email notifications |
| `show_default_award_emojis` | boolean | no | Show default award emojis | | `show_default_award_emojis` | boolean | no | Show default award emojis |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
...@@ -1189,6 +1191,7 @@ PUT /projects/:id ...@@ -1189,6 +1191,7 @@ PUT /projects/:id
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` | | `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` | | `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
| `requirements_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
| `emails_disabled` | boolean | no | Disable email notifications | | `emails_disabled` | boolean | no | Disable email notifications |
| `show_default_award_emojis` | boolean | no | Show default award emojis | | `show_default_award_emojis` | boolean | no | Show default award emojis |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push | | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
......
...@@ -62,6 +62,7 @@ Use the switches to enable or disable the following features: ...@@ -62,6 +62,7 @@ Use the switches to enable or disable the following features:
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) | | **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) |
| **Pages** | ✓ | Allows you to [publish static websites](../pages/) | | **Pages** | ✓ | Allows you to [publish static websites](../pages/) |
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md) | **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md)
| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md)
Some features depend on others: Some features depend on others:
......
...@@ -2,6 +2,7 @@ export default { ...@@ -2,6 +2,7 @@ export default {
data() { data() {
return { return {
packagesEnabled: true, packagesEnabled: true,
requirementsEnabled: true,
}; };
}, },
watch: { watch: {
......
...@@ -49,6 +49,11 @@ module EE ...@@ -49,6 +49,11 @@ module EE
end end
end end
override :project_feature_attributes
def project_feature_attributes
super + [:requirements_access_level]
end
override :project_params_attributes override :project_params_attributes
def project_params_attributes def project_params_attributes
super + project_params_ee super + project_params_ee
......
...@@ -53,6 +53,10 @@ module EE ...@@ -53,6 +53,10 @@ module EE
nav_tabs << :project_insights nav_tabs << :project_insights
end end
if can?(current_user, :read_requirement, project)
nav_tabs << :requirements
end
nav_tabs nav_tabs
end end
...@@ -66,7 +70,8 @@ module EE ...@@ -66,7 +70,8 @@ module EE
override :project_permissions_settings override :project_permissions_settings
def project_permissions_settings(project) def project_permissions_settings(project)
super.merge( super.merge(
packagesEnabled: !!project.packages_enabled packagesEnabled: !!project.packages_enabled,
requirementsAccessLevel: project.requirements_access_level
) )
end end
...@@ -74,7 +79,8 @@ module EE ...@@ -74,7 +79,8 @@ module EE
def project_permissions_panel_data(project) def project_permissions_panel_data(project)
super.merge( super.merge(
packagesAvailable: ::Gitlab.config.packages.enabled && project.feature_available?(:packages), packagesAvailable: ::Gitlab.config.packages.enabled && project.feature_available?(:packages),
packagesHelpPath: help_page_path('user/packages/index') packagesHelpPath: help_page_path('user/packages/index'),
requirementsAvailable: project.feature_available?(:requirements)
) )
end end
......
# frozen_string_literal: true
module EE
module ProjectFeaturesCompatibility
extend ActiveSupport::Concern
# TODO: remove in API v5, replaced by *_access_level
def requirements_enabled=(value)
write_feature_attribute_boolean(:requirements_access_level, value)
end
def requirements_access_level=(value)
write_feature_attribute_string(:requirements_access_level, value)
end
end
end
...@@ -167,6 +167,8 @@ module EE ...@@ -167,6 +167,8 @@ module EE
delegate :merge_trains_enabled?, to: :ci_cd_settings delegate :merge_trains_enabled?, to: :ci_cd_settings
delegate :closest_gitlab_subscription, to: :namespace delegate :closest_gitlab_subscription, to: :namespace
delegate :requirements_access_level, to: :project_feature, allow_nil: true
validates :repository_size_limit, validates :repository_size_limit,
numericality: { only_integer: true, greater_than_or_equal_to: 0, allow_nil: true } numericality: { only_integer: true, greater_than_or_equal_to: 0, allow_nil: true }
validates :max_pages_size, validates :max_pages_size,
...@@ -184,6 +186,7 @@ module EE ...@@ -184,6 +186,7 @@ module EE
end end
default_value_for :packages_enabled, true default_value_for :packages_enabled, true
default_value_for :requirements_enabled, true
accepts_nested_attributes_for :tracing_setting, update_only: true, allow_destroy: true accepts_nested_attributes_for :tracing_setting, update_only: true, allow_destroy: true
accepts_nested_attributes_for :status_page_setting, update_only: true, allow_destroy: true accepts_nested_attributes_for :status_page_setting, update_only: true, allow_destroy: true
......
...@@ -4,11 +4,17 @@ module EE ...@@ -4,11 +4,17 @@ module EE
module ProjectFeature module ProjectFeature
extend ActiveSupport::Concern extend ActiveSupport::Concern
EE_FEATURES = %i(requirements).freeze
prepended do prepended do
set_available_features(available_features + EE_FEATURES)
# Ensure changes to project visibility settings go to elasticsearch # Ensure changes to project visibility settings go to elasticsearch
after_commit on: :update do after_commit on: :update do
project.maintain_elasticsearch_update if project.maintaining_elasticsearch? project.maintain_elasticsearch_update if project.maintaining_elasticsearch?
end end
default_value_for :requirements_access_level, value: Featurable::ENABLED, allows_nil: false
end end
end end
end end
...@@ -34,7 +34,7 @@ module EE ...@@ -34,7 +34,7 @@ module EE
condition(:iterations_available) { @subject.feature_available?(:iterations) } condition(:iterations_available) { @subject.feature_available?(:iterations) }
with_scope :subject with_scope :subject
condition(:requirements_available) { @subject.feature_available?(:requirements) } condition(:requirements_available) { @subject.feature_available?(:requirements) & feature_available?(:requirements) }
condition(:compliance_framework_available) { @subject.feature_available?(:compliance_framework, @user) } condition(:compliance_framework_available) { @subject.feature_available?(:compliance_framework, @user) }
......
- return unless Feature.enabled?(:requirements_management, project, default_enabled: true) - return unless Feature.enabled?(:requirements_management, project, default_enabled: true)
- return unless can?(current_user, :read_requirement, project) - return unless project_nav_tab? :requirements
= nav_link(path: 'requirements#index') do = nav_link(path: 'requirements#index') do
= link_to project_requirements_management_requirements_path(project), class: 'qa-project-requirements-link' do = link_to project_requirements_management_requirements_path(project), class: 'qa-project-requirements-link' do
......
...@@ -31,6 +31,9 @@ module EE ...@@ -31,6 +31,9 @@ module EE
expose :marked_for_deletion_on, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } do |project, _| expose :marked_for_deletion_on, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } do |project, _|
project.marked_for_deletion_at project.marked_for_deletion_at
end end
expose :requirements_enabled do |project, options|
project.feature_available?(:requirements, options[:current_user])
end
expose :compliance_frameworks do |project, _| expose :compliance_frameworks do |project, _|
[project.compliance_framework_setting&.framework].compact [project.compliance_framework_setting&.framework].compact
end end
......
...@@ -13,7 +13,8 @@ module EE ...@@ -13,7 +13,8 @@ module EE
:builds_access_level, :builds_access_level,
:repository_access_level, :repository_access_level,
:pages_access_level, :pages_access_level,
:metrics_dashboard_access_level].freeze :metrics_dashboard_access_level,
:requirements_access_level].freeze
def initialize(current_user, model, project) def initialize(current_user, model, project)
@project = project @project = project
......
...@@ -3,12 +3,14 @@ ...@@ -3,12 +3,14 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Projects::RequirementsManagement::RequirementsController do RSpec.describe Projects::RequirementsManagement::RequirementsController do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
subject { get :index, params: { namespace_id: project.namespace, project_id: project } } subject { get :index, params: { namespace_id: project.namespace, project_id: project } }
describe 'GET #index' do describe 'GET #index' do
context 'private project' do
let(:project) { create(:project) }
context 'with authorized user' do context 'with authorized user' do
before do before do
project.add_developer(user) project.add_developer(user)
...@@ -80,4 +82,74 @@ RSpec.describe Projects::RequirementsManagement::RequirementsController do ...@@ -80,4 +82,74 @@ RSpec.describe Projects::RequirementsManagement::RequirementsController do
end end
end end
end end
context 'public project' do
let(:project) { create(:project, :public) }
before do
stub_licensed_features(requirements: true)
end
context 'with requirements disabled' do
before do
project.project_feature.update!({ requirements_access_level: ::ProjectFeature::DISABLED })
project.add_developer(user)
sign_in(user)
end
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'with requirements visible to project memebers' do
before do
project.project_feature.update!({ requirements_access_level: ::ProjectFeature::PRIVATE })
end
context 'with authorized user' do
before do
project.add_developer(user)
sign_in(user)
end
it 'renders the index template' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
end
end
context 'with unauthorized user' do
before do
sign_in(user)
end
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'with requirements visible to everyone' do
before do
project.project_feature.update!({ requirements_access_level: ::ProjectFeature::ENABLED })
end
context 'with anonymous user' do
it 'renders the index template' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
end
end
end
end
end
end end
...@@ -76,6 +76,10 @@ FactoryBot.modify do ...@@ -76,6 +76,10 @@ FactoryBot.modify do
service_desk_enabled { true } service_desk_enabled { true }
end end
trait(:issues_enabled) do
issues_access_level { ProjectFeature::ENABLED }
end
trait :github_imported do trait :github_imported do
import_type { 'github' } import_type { 'github' }
end end
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'resource with requirement permissions' do RSpec.shared_examples 'resource with requirement permissions' do
include AdminModeHelper
let(:all_permissions) do let(:all_permissions) do
[:read_requirement, :create_requirement, :admin_requirement, [:read_requirement, :create_requirement, :admin_requirement,
:update_requirement, :destroy_requirement, :update_requirement, :destroy_requirement,
...@@ -77,6 +79,82 @@ RSpec.shared_examples 'resource with requirement permissions' do ...@@ -77,6 +79,82 @@ RSpec.shared_examples 'resource with requirement permissions' do
it { is_expected.to be_disallowed(*all_permissions) } it { is_expected.to be_disallowed(*all_permissions) }
end end
end end
context 'when access level is disabled' do
before do
parent = resource.is_a?(Project) ? resource : resource.resource_parent
parent.project_feature.update!(requirements_access_level: ProjectFeature::DISABLED)
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(*all_permissions) }
end
context 'with admin' do
let(:current_user) { admin }
it { is_expected.to be_disallowed(*all_permissions) }
end
end
context 'when access level is private' do
before do
parent = resource.is_a?(Project) ? resource : resource.resource_parent
parent.project_feature.update!(requirements_access_level: ProjectFeature::PRIVATE)
end
context 'with admin' do
let(:current_user) { admin }
it { is_expected.to be_disallowed(*all_permissions) }
context 'with ' do
before do
enable_admin_mode!(current_user)
end
it_behaves_like 'user with read only permissions'
end
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(*all_permissions) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it_behaves_like 'user with manage permissions'
end
context 'with developer' do
let(:current_user) { developer }
it_behaves_like 'user with manage permissions'
end
context 'with reporter' do
let(:current_user) { reporter }
it_behaves_like 'user with manage permissions'
end
context 'with guest' do
let(:current_user) { guest }
it_behaves_like 'user with read only permissions'
end
context 'with non member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(*all_permissions) }
end
end
end end
context 'when requirements feature is disabled' do context 'when requirements feature is disabled' do
......
...@@ -17782,6 +17782,12 @@ msgstr "" ...@@ -17782,6 +17782,12 @@ msgstr ""
msgid "ProjectSettings|Repository" msgid "ProjectSettings|Repository"
msgstr "" msgstr ""
msgid "ProjectSettings|Requirements"
msgstr ""
msgid "ProjectSettings|Requirements management system for this project"
msgstr ""
msgid "ProjectSettings|Share code pastes with others out of Git repository" msgid "ProjectSettings|Share code pastes with others out of Git repository"
msgstr "" msgstr ""
......
...@@ -533,6 +533,7 @@ Project: ...@@ -533,6 +533,7 @@ Project:
- merge_requests_enabled - merge_requests_enabled
- wiki_enabled - wiki_enabled
- snippets_enabled - snippets_enabled
- requirements_enabled
- visibility_level - visibility_level
- archived - archived
- created_at - created_at
...@@ -600,6 +601,7 @@ ProjectFeature: ...@@ -600,6 +601,7 @@ ProjectFeature:
- repository_access_level - repository_access_level
- pages_access_level - pages_access_level
- metrics_dashboard_access_level - metrics_dashboard_access_level
- requirements_access_level
- created_at - created_at
- updated_at - updated_at
ProtectedBranch::MergeAccessLevel: ProtectedBranch::MergeAccessLevel:
......
...@@ -80,6 +80,7 @@ RSpec.describe ProjectPolicy do ...@@ -80,6 +80,7 @@ RSpec.describe ProjectPolicy do
let(:additional_guest_permissions) { [] } let(:additional_guest_permissions) { [] }
let(:additional_reporter_permissions) { [] } let(:additional_reporter_permissions) { [] }
let(:additional_maintainer_permissions) { [] } let(:additional_maintainer_permissions) { [] }
let(:additional_owner_permissions) { [] }
let(:guest_permissions) { base_guest_permissions + additional_guest_permissions } let(:guest_permissions) { base_guest_permissions + additional_guest_permissions }
let(:reporter_permissions) { base_reporter_permissions + additional_reporter_permissions } let(:reporter_permissions) { base_reporter_permissions + additional_reporter_permissions }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment