diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-15 09:12:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-15 09:12:42 +0300 |
commit | defacc074a4a576e15021ba264de745af982b45d (patch) | |
tree | 3695e700025e6501f3a61bd64f8a84eaa4b12ca4 /spec | |
parent | 1b5891ee8ce8adc6877c462ea292eaee3c4ab30b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
25 files changed, 110 insertions, 137 deletions
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 388d7c2266b..94d75ab8d7d 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -919,6 +919,7 @@ RSpec.describe ProjectsController do container_registry_access_level environments_access_level feature_flags_access_level + releases_access_level ] end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 98dfecb2888..95b72648cf5 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -39,6 +39,7 @@ FactoryBot.define do security_and_compliance_access_level { ProjectFeature::PRIVATE } environments_access_level { ProjectFeature::ENABLED } feature_flags_access_level { ProjectFeature::ENABLED } + releases_access_level { ProjectFeature::ENABLED } # we can't assign the delegated `#ci_cd_settings` attributes directly, as the # `#ci_cd_settings` relation needs to be created first diff --git a/spec/frontend/__helpers__/matchers/index.js b/spec/frontend/__helpers__/matchers/index.js index 73464135950..5da6676cdc1 100644 --- a/spec/frontend/__helpers__/matchers/index.js +++ b/spec/frontend/__helpers__/matchers/index.js @@ -3,4 +3,3 @@ export * from './to_have_tracking_attributes'; export * from './to_match_interpolated_text'; export * from './to_validate_json_schema'; export * from './to_match_expected_for_markdown'; -export * from './to_equal_graphql_fixture'; diff --git a/spec/frontend/__helpers__/matchers/to_equal_graphql_fixture.js b/spec/frontend/__helpers__/matchers/to_equal_graphql_fixture.js deleted file mode 100644 index 1a095de1deb..00000000000 --- a/spec/frontend/__helpers__/matchers/to_equal_graphql_fixture.js +++ /dev/null @@ -1,28 +0,0 @@ -import { isEqual } from 'lodash'; -import { stripTypenames } from 'helpers/graphql_helpers'; - -export function toEqualGraphqlFixture(received, match) { - let clearReceived; - let clearMatch; - - try { - clearReceived = JSON.parse(JSON.stringify(received)); - clearMatch = stripTypenames(match); - } catch (e) { - return { message: () => 'The comparator value is not an object', pass: false }; - } - const pass = isEqual(clearReceived, clearMatch); - // console.log(this.utils); - const message = pass - ? () => ` - Expected to not be: ${this.utils.printExpected(clearMatch)} - Received: ${this.utils.printReceived(clearReceived)} - ` - : () => - ` - Expected to be: ${this.utils.printExpected(clearMatch)} - Received: ${this.utils.printReceived(clearReceived)} - `; - - return { actual: received, message, pass }; -} diff --git a/spec/frontend/__helpers__/matchers/to_equal_graphql_fixture_spec.js b/spec/frontend/__helpers__/matchers/to_equal_graphql_fixture_spec.js deleted file mode 100644 index 00adc8cfc8d..00000000000 --- a/spec/frontend/__helpers__/matchers/to_equal_graphql_fixture_spec.js +++ /dev/null @@ -1,58 +0,0 @@ -describe('custom matcher toEqualGraphqlFixture', () => { - const key = 'value'; - const key2 = 'value2'; - - describe('positive assertion', () => { - it.each([ - [{}, {}], - [{ undef: undefined }, { undef: undefined }], - [{}, { __typename: 'MyType' }], - [{ key }, { key, __typename: 'MyType' }], - [ - { obj: { key } }, - { - obj: { - key, - __typename: 'MyNestedType', - }, - __typename: 'MyType', - }, - ], - [[{ key }], [{ key }]], - [ - [{ key }], - [ - { - key, - __typename: 'MyCollectionType', - }, - ], - ], - ])('%j equals %j', (received, match) => { - expect(received).toEqualGraphqlFixture(match); - }); - }); - - describe('negative assertion', () => { - it.each([ - [{ __typename: 'MyType' }, {}], - [{ key }, { key2, __typename: 'MyType' }], - [ - { key, key2 }, - { key2, __typename: 'MyType' }, - ], - [[{ key }, { key2 }], [{ key }]], - [ - [{ key, key2 }], - [ - { - key, - __typename: 'MyCollectionType', - }, - ], - ], - ])('%j does not equal %j', (received, match) => { - expect(received).not.toEqualGraphqlFixture(match); - }); - }); -}); diff --git a/spec/frontend/__helpers__/mock_apollo_helper.js b/spec/frontend/__helpers__/mock_apollo_helper.js index bae9f33be87..e0739df7086 100644 --- a/spec/frontend/__helpers__/mock_apollo_helper.js +++ b/spec/frontend/__helpers__/mock_apollo_helper.js @@ -8,7 +8,6 @@ export function createMockClient(handlers = [], resolvers = {}, cacheOptions = { const cache = new InMemoryCache({ possibleTypes, typePolicies, - addTypename: false, ...cacheOptions, }); diff --git a/spec/frontend/packages_and_registries/package_registry/mock_data.js b/spec/frontend/packages_and_registries/package_registry/mock_data.js index 483302d525f..22236424e6a 100644 --- a/spec/frontend/packages_and_registries/package_registry/mock_data.js +++ b/spec/frontend/packages_and_registries/package_registry/mock_data.js @@ -141,6 +141,7 @@ export const packageData = (extend) => ({ }); export const conanMetadata = () => ({ + __typename: 'ConanMetadata', id: 'conan-1', packageChannel: 'stable', packageUsername: 'gitlab-org+gitlab-test', @@ -148,9 +149,8 @@ export const conanMetadata = () => ({ recipePath: 'package-8/1.0.0/gitlab-org+gitlab-test/stable', }); -const conanMetadataQuery = () => ({ ...conanMetadata(), __typename: 'ConanMetadata' }); - export const composerMetadata = () => ({ + __typename: 'ComposerMetadata', targetSha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0', composerJson: { license: 'MIT', @@ -158,19 +158,14 @@ export const composerMetadata = () => ({ }, }); -const composerMetadataQuery = () => ({ - ...composerMetadata(), - __typename: 'ComposerMetadata', -}); - export const pypiMetadata = () => ({ + __typename: 'PypiMetadata', id: 'pypi-1', requiredPython: '1.0.0', }); -const pypiMetadataQuery = () => ({ ...pypiMetadata(), __typename: 'PypiMetadata' }); - export const mavenMetadata = () => ({ + __typename: 'MavenMetadata', id: 'maven-1', appName: 'appName', appGroup: 'appGroup', @@ -178,23 +173,20 @@ export const mavenMetadata = () => ({ path: 'path', }); -const mavenMetadataQuery = () => ({ ...mavenMetadata(), __typename: 'MavenMetadata' }); - export const nugetMetadata = () => ({ + __typename: 'NugetMetadata', id: 'nuget-1', iconUrl: 'iconUrl', licenseUrl: 'licenseUrl', projectUrl: 'projectUrl', }); -const nugetMetadataQuery = () => ({ ...nugetMetadata(), __typename: 'NugetMetadata' }); - const packageTypeMetadataQueryMapping = { - CONAN: conanMetadataQuery, - COMPOSER: composerMetadataQuery, - PYPI: pypiMetadataQuery, - MAVEN: mavenMetadataQuery, - NUGET: nugetMetadataQuery, + CONAN: conanMetadata, + COMPOSER: composerMetadata, + PYPI: pypiMetadata, + MAVEN: mavenMetadata, + NUGET: nugetMetadata, }; export const pagination = (extend) => ({ diff --git a/spec/frontend/packages_and_registries/settings/project/settings/mock_data.js b/spec/frontend/packages_and_registries/settings/project/settings/mock_data.js index d4b6c66ddeb..0696144215c 100644 --- a/spec/frontend/packages_and_registries/settings/project/settings/mock_data.js +++ b/spec/frontend/packages_and_registries/settings/project/settings/mock_data.js @@ -1,4 +1,5 @@ export const containerExpirationPolicyData = () => ({ + __typename: 'ContainerExpirationPolicy', cadence: 'EVERY_DAY', enabled: true, keepN: 'TEN_TAGS', @@ -13,7 +14,6 @@ export const expirationPolicyPayload = (override) => ({ project: { id: '1', containerExpirationPolicy: { - __typename: 'ContainerExpirationPolicy', ...containerExpirationPolicyData(), ...override, }, @@ -42,6 +42,7 @@ export const expirationPolicyMutationPayload = ({ override, errors = [] } = {}) }); export const packagesCleanupPolicyData = { + __typename: 'PackagesCleanupPolicy', keepNDuplicatedPackageFiles: 'ALL_PACKAGE_FILES', nextRunAt: '2020-11-19T07:37:03.941Z', }; @@ -51,7 +52,6 @@ export const packagesCleanupPolicyPayload = (override) => ({ project: { id: '1', packagesCleanupPolicy: { - __typename: 'PackagesCleanupPolicy', ...packagesCleanupPolicyData, ...override, }, diff --git a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js b/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js index 361a9aabf33..ffe3599ac64 100644 --- a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js +++ b/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js @@ -90,7 +90,7 @@ describe('AdminRunnerEditApp', () => { loading: false, runnerPath: mockRunnerPath, }); - expect(findRunnerUpdateForm().props('runner')).toEqualGraphqlFixture(mockRunner); + expect(findRunnerUpdateForm().props('runner')).toEqual(mockRunner); }); describe('When there is an error', () => { diff --git a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js index 1ae6eed806f..509681c5a77 100644 --- a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js +++ b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js @@ -256,7 +256,7 @@ describe('AdminRunnerShowApp', () => { await createComponent({ stubs }); expect(findJobCountBadge().text()).toBe('3'); - expect(findRunnersJobs().props('runner')).toEqualGraphqlFixture({ ...mockRunner, ...runner }); + expect(findRunnersJobs().props('runner')).toEqual({ ...mockRunner, ...runner }); }); }); }); diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js index f8f834c7ad4..9ba6924f5c7 100644 --- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js +++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js @@ -176,7 +176,7 @@ describe('AdminRunnersApp', () => { await createComponent(); expect(mockRunnersHandler).toHaveBeenCalledTimes(1); - expect(findRunnerList().props('runners')).toEqualGraphqlFixture(mockRunners); + expect(findRunnerList().props('runners')).toEqual(mockRunners); }); it('runner item links to the runner admin page', async () => { @@ -197,7 +197,7 @@ describe('AdminRunnersApp', () => { const runnerActions = wrapper.find('tr [data-testid="td-actions"]').find(RunnerActionsCell); const runner = mockRunners[0]; - expect(runnerActions.props()).toEqualGraphqlFixture({ + expect(runnerActions.props()).toEqual({ runner, editUrl: runner.editAdminUrl, }); @@ -482,7 +482,7 @@ describe('AdminRunnersApp', () => { }); it('passes the page info', () => { - expect(findRunnerPagination().props('pageInfo')).toEqualGraphqlFixture(pageInfo); + expect(findRunnerPagination().props('pageInfo')).toEqual(pageInfo); }); it('navigates to the next page', async () => { diff --git a/spec/frontend/runner/components/runner_jobs_spec.js b/spec/frontend/runner/components/runner_jobs_spec.js index 8e3715e45d2..4d38afb25ee 100644 --- a/spec/frontend/runner/components/runner_jobs_spec.js +++ b/spec/frontend/runner/components/runner_jobs_spec.js @@ -73,7 +73,7 @@ describe('RunnerJobs', () => { it('Shows jobs', () => { const jobs = findRunnerJobsTable().props('jobs'); - expect(jobs).toEqualGraphqlFixture(mockJobs); + expect(jobs).toEqual(mockJobs); }); describe('When "Next" page is clicked', () => { diff --git a/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js b/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js index 5d3552af322..cee1d436942 100644 --- a/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js +++ b/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js @@ -107,7 +107,7 @@ describe('GroupRunnerShowApp', () => { }); it('renders runner details component', () => { - expect(findRunnerDetails().props('runner')).toEqualGraphqlFixture(mockRunner); + expect(findRunnerDetails().props('runner')).toEqual(mockRunner); }); describe('when runner cannot be updated', () => { diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js index a288d088b5b..57d64202219 100644 --- a/spec/frontend/runner/group_runners/group_runners_app_spec.js +++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js @@ -168,7 +168,7 @@ describe('GroupRunnersApp', () => { await createComponent(); const runners = findRunnerList().props('runners'); - expect(runners).toEqualGraphqlFixture(mockGroupRunnersEdges.map(({ node }) => node)); + expect(runners).toEqual(mockGroupRunnersEdges.map(({ node }) => node)); }); it('requests the runners with group path and no other filters', async () => { @@ -382,7 +382,7 @@ describe('GroupRunnersApp', () => { }); it('passes the page info', () => { - expect(findRunnerPagination().props('pageInfo')).toEqualGraphqlFixture(pageInfo); + expect(findRunnerPagination().props('pageInfo')).toEqual(pageInfo); }); it('navigates to the next page', async () => { diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js index 5fd364afbe4..88015ed42a3 100644 --- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js +++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js @@ -148,6 +148,7 @@ describe('Sidebar assignees widget', () => { expect(findAssignees().props('users')).toEqual([ { + __typename: 'UserCore', id: 'gid://gitlab/User/2', avatarUrl: 'https://www.gravatar.com/avatar/a95e5b71488f4b9d69ce5ff58bfd28d6?s=80\u0026d=identicon', diff --git a/spec/frontend/sidebar/components/participants/sidebar_participants_widget_spec.js b/spec/frontend/sidebar/components/participants/sidebar_participants_widget_spec.js index 338ecf944f3..859e63b3df6 100644 --- a/spec/frontend/sidebar/components/participants/sidebar_participants_widget_spec.js +++ b/spec/frontend/sidebar/components/participants/sidebar_participants_widget_spec.js @@ -1,7 +1,6 @@ import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -import { stripTypenames } from 'helpers/graphql_helpers'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import Participants from '~/sidebar/components/participants/participants.vue'; @@ -67,11 +66,9 @@ describe('Sidebar Participants Widget', () => { }); it('passes participants to child component', () => { - const participantsWithoutTypename = stripTypenames( + expect(findParticipants().props('participants')).toEqual( epicParticipantsResponse().data.workspace.issuable.participants.nodes, ); - - expect(findParticipants().props('participants')).toEqual(participantsWithoutTypename); }); }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js index 86d1f21fd04..a6713b7e7e4 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js @@ -66,12 +66,14 @@ export const mockMilestones = [ export const mockCrmContacts = [ { + __typename: 'CustomerRelationsContact', id: 'gid://gitlab/CustomerRelations::Contact/1', firstName: 'John', lastName: 'Smith', email: 'john@smith.com', }, { + __typename: 'CustomerRelationsContact', id: 'gid://gitlab/CustomerRelations::Contact/2', firstName: 'Andy', lastName: 'Green', @@ -81,10 +83,12 @@ export const mockCrmContacts = [ export const mockCrmOrganizations = [ { + __typename: 'CustomerRelationsOrganization', id: 'gid://gitlab/CustomerRelations::Organization/1', name: 'First Org Ltd.', }, { + __typename: 'CustomerRelationsOrganization', id: 'gid://gitlab/CustomerRelations::Organization/2', name: 'Organizer S.p.a.', }, @@ -102,11 +106,9 @@ export const mockProjectCrmContactsQueryResponse = { __typename: 'CustomerRelationsContactConnection', nodes: [ { - __typename: 'CustomerRelationsContact', ...mockCrmContacts[0], }, { - __typename: 'CustomerRelationsContact', ...mockCrmContacts[1], }, ], @@ -128,11 +130,9 @@ export const mockProjectCrmOrganizationsQueryResponse = { __typename: 'CustomerRelationsOrganizationConnection', nodes: [ { - __typename: 'CustomerRelationsOrganization', ...mockCrmOrganizations[0], }, { - __typename: 'CustomerRelationsOrganization', ...mockCrmOrganizations[1], }, ], diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js index 1b27a294b90..cad401e0013 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js @@ -131,6 +131,7 @@ describe('LabelsSelectRoot', () => { expect(findDropdownValue().exists()).toBe(true); expect(findDropdownValue().props('selectedLabels')).toEqual([ { + __typename: 'Label', color: '#330066', description: null, id: 'gid://gitlab/ProjectLabel/1', diff --git a/spec/frontend/vue_shared/components/user_select_spec.js b/spec/frontend/vue_shared/components/user_select_spec.js index c7b84a0f73b..4188adc72a1 100644 --- a/spec/frontend/vue_shared/components/user_select_spec.js +++ b/spec/frontend/vue_shared/components/user_select_spec.js @@ -4,7 +4,6 @@ import { cloneDeep } from 'lodash'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; -import { stripTypenames } from 'helpers/graphql_helpers'; import waitForPromises from 'helpers/wait_for_promises'; import searchUsersQuery from '~/graphql_shared/queries/users_search.query.graphql'; import searchUsersQueryOnMR from '~/graphql_shared/queries/users_search_with_mr_permissions.graphql'; @@ -201,7 +200,7 @@ describe('User select dropdown', () => { }); await waitForPromises(); - expect(findUnselectedParticipantByIndex(0).props('user')).toEqual(stripTypenames(mockUser2)); + expect(findUnselectedParticipantByIndex(0).props('user')).toEqual(mockUser2); }); it('moves issuable author on top of unassigned list after current user, if author and current user are unassigned project members', async () => { @@ -216,12 +215,8 @@ describe('User select dropdown', () => { }); await waitForPromises(); - expect(findUnselectedParticipantByIndex(0).props('user')).toEqual( - stripTypenames(currentUser), - ); - expect(findUnselectedParticipantByIndex(1).props('user')).toMatchObject( - stripTypenames(issuableAuthor), - ); + expect(findUnselectedParticipantByIndex(0).props('user')).toEqual(currentUser); + expect(findUnselectedParticipantByIndex(1).props('user')).toMatchObject(issuableAuthor); }); it('displays author in a designated position if author is not assigned and not a project member', async () => { diff --git a/spec/frontend/work_items/components/work_item_assignees_spec.js b/spec/frontend/work_items/components/work_item_assignees_spec.js index 450d9cff963..febff4554e4 100644 --- a/spec/frontend/work_items/components/work_item_assignees_spec.js +++ b/spec/frontend/work_items/components/work_item_assignees_spec.js @@ -344,7 +344,7 @@ describe('WorkItemAssignees component', () => { it('adds current user to the top of dropdown items', () => { expect(findTokenSelector().props('dropdownItems')[0]).toEqual({ - ...stripTypenames(currentUserResponse.data.currentUser), + ...currentUserResponse.data.currentUser, class: expect.anything(), }); }); @@ -354,7 +354,7 @@ describe('WorkItemAssignees component', () => { await waitForPromises(); expect(findTokenSelector().props('dropdownItems')[0]).not.toEqual( - stripTypenames(currentUserResponse.data.currentUser), + currentUserResponse.data.currentUser, ); }); }); diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 214086a3823..04c066986b7 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -968,7 +968,8 @@ RSpec.describe ProjectsHelper do 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 + featureFlagsAccessLevel: project.project_feature.feature_flags_access_level, + releasesAccessLevel: project.project_feature.releases_access_level ) end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 72e13943bf5..5cbe05ccf53 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -585,6 +585,7 @@ ProjectFeature: - package_registry_access_level - environments_access_level - feature_flags_access_level +- releases_access_level - created_at - updated_at ProtectedBranch::MergeAccessLevel: diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb index 48c391451ed..b49b9ce8a2a 100644 --- a/spec/models/concerns/project_features_compatibility_spec.rb +++ b/spec/models/concerns/project_features_compatibility_spec.rb @@ -6,7 +6,9 @@ RSpec.describe ProjectFeaturesCompatibility do let(:project) { create(:project) } let(:features_enabled) { %w(issues wiki builds merge_requests snippets security_and_compliance) } let(:features) do - features_enabled + %w(repository pages operations container_registry package_registry environments feature_flags) + features_enabled + %w( + repository pages operations container_registry package_registry environments feature_flags releases + ) end # We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9f5ce48287f..98b202299a8 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -835,6 +835,7 @@ RSpec.describe Project, factory_default: :keep do it { is_expected.to delegate_method(:container_registry_access_level).to(:project_feature) } it { is_expected.to delegate_method(:environments_access_level).to(:project_feature) } it { is_expected.to delegate_method(:feature_flags_access_level).to(:project_feature) } + it { is_expected.to delegate_method(:releases_access_level).to(:project_feature) } describe 'read project settings' do %i( diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index c3817f67158..e8fdf9a8e25 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -2171,6 +2171,74 @@ RSpec.describe ProjectPolicy do end end + describe 'Releases feature' do + using RSpec::Parameterized::TableSyntax + + let(:guest_permissions) { [:read_release] } + + let(:developer_permissions) do + guest_permissions + [:create_release, :update_release, :destroy_release] + end + + let(:maintainer_permissions) do + developer_permissions + end + + where(:project_visibility, :access_level, :role, :allowed) do + :public | ProjectFeature::ENABLED | :maintainer | true + :public | ProjectFeature::ENABLED | :developer | true + :public | ProjectFeature::ENABLED | :guest | true + :public | ProjectFeature::ENABLED | :anonymous | true + :public | ProjectFeature::PRIVATE | :maintainer | true + :public | ProjectFeature::PRIVATE | :developer | true + :public | ProjectFeature::PRIVATE | :guest | true + :public | ProjectFeature::PRIVATE | :anonymous | false + :public | ProjectFeature::DISABLED | :maintainer | false + :public | ProjectFeature::DISABLED | :developer | false + :public | ProjectFeature::DISABLED | :guest | false + :public | ProjectFeature::DISABLED | :anonymous | false + :internal | ProjectFeature::ENABLED | :maintainer | true + :internal | ProjectFeature::ENABLED | :developer | true + :internal | ProjectFeature::ENABLED | :guest | true + :internal | ProjectFeature::ENABLED | :anonymous | false + :internal | ProjectFeature::PRIVATE | :maintainer | true + :internal | ProjectFeature::PRIVATE | :developer | true + :internal | ProjectFeature::PRIVATE | :guest | true + :internal | ProjectFeature::PRIVATE | :anonymous | false + :internal | ProjectFeature::DISABLED | :maintainer | false + :internal | ProjectFeature::DISABLED | :developer | false + :internal | ProjectFeature::DISABLED | :guest | false + :internal | ProjectFeature::DISABLED | :anonymous | false + :private | ProjectFeature::ENABLED | :maintainer | true + :private | ProjectFeature::ENABLED | :developer | true + :private | ProjectFeature::ENABLED | :guest | true + :private | ProjectFeature::ENABLED | :anonymous | false + :private | ProjectFeature::PRIVATE | :maintainer | true + :private | ProjectFeature::PRIVATE | :developer | true + :private | ProjectFeature::PRIVATE | :guest | true + :private | ProjectFeature::PRIVATE | :anonymous | false + :private | ProjectFeature::DISABLED | :maintainer | false + :private | ProjectFeature::DISABLED | :developer | false + :private | ProjectFeature::DISABLED | :guest | false + :private | ProjectFeature::DISABLED | :anonymous | false + end + + with_them do + let(:current_user) { user_subject(role) } + let(:project) { project_subject(project_visibility) } + + it 'allows/disallows the abilities based on the Releases access level' do + project.project_feature.update!(releases_access_level: access_level) + + if allowed + expect_allowed(*permissions_abilities(role)) + else + expect_disallowed(*permissions_abilities(role)) + end + end + end + end + describe 'access_security_and_compliance' do context 'when the "Security & Compliance" is enabled' do before do |