From b3a736ed88a1db0391cd9881e70b987bab7d89d1 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 14 Feb 2020 15:09:08 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../show/user_sees_collaboration_links_spec.rb | 106 ++++++++----- .../visibility_levels/internal/group.json | 166 +++++++++++++++++++++ .../visibility_levels/private/group.json | 166 +++++++++++++++++++++ .../visibility_levels/public/group.json | 166 +++++++++++++++++++++ spec/fixtures/lsif.json.gz | Bin 739 -> 759 bytes spec/frontend/ide/mock_data.js | 1 + spec/frontend/ide/services/index_spec.js | 31 +++- spec/frontend/ide/stores/getters_spec.js | 36 +++++ .../table/__snapshots__/row_spec.js.snap | 2 + .../new_merge_request_option_spec.js | 53 +++++-- .../ide/components/nav_dropdown_button_spec.js | 104 ++++++++----- .../ide/components/nav_dropdown_spec.js | 30 ++++ .../ide/stores/actions/merge_request_spec.js | 18 ++- .../ide/stores/modules/commit/actions_spec.js | 5 +- .../import_export/group_tree_restorer_spec.rb | 27 ++++ .../gitlab/import_export/group_tree_saver_spec.rb | 2 +- spec/requests/api/group_import_spec.rb | 36 +++++ spec/requests/api/lsif_data_spec.rb | 6 +- spec/requests/api/merge_requests_spec.rb | 102 ++++++------- spec/requests/self_monitoring_project_spec.rb | 24 +-- spec/services/projects/lsif_data_service_spec.rb | 25 +++- spec/support/helpers/api_helpers.rb | 7 + .../requests/self_monitoring_shared_examples.rb | 18 --- .../projects/tree/_tree_header.html.haml_spec.rb | 13 +- 24 files changed, 956 insertions(+), 188 deletions(-) create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json create mode 100644 spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json (limited to 'spec') diff --git a/spec/features/projects/show/user_sees_collaboration_links_spec.rb b/spec/features/projects/show/user_sees_collaboration_links_spec.rb index ff133b58f89..63fcec4f9b3 100644 --- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb +++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb @@ -3,66 +3,96 @@ require 'spec_helper' describe 'Projects > Show > Collaboration links', :js do - let(:project) { create(:project, :repository) } + using RSpec::Parameterized::TableSyntax + + let(:project) { create(:project, :repository, :public) } let(:user) { create(:user) } before do - project.add_developer(user) sign_in(user) end - it 'shows all the expected links' do - visit project_path(project) + context 'with developer user' do + before do + project.add_developer(user) + end - # The navigation bar - page.within('.header-new') do - find('.qa-new-menu-toggle').click + it 'shows all the expected links' do + visit project_path(project) - aggregate_failures 'dropdown links in the navigation bar' do - expect(page).to have_link('New issue') - expect(page).to have_link('New merge request') - expect(page).to have_link('New snippet', href: new_project_snippet_path(project)) - end + # The navigation bar + page.within('.header-new') do + find('.qa-new-menu-toggle').click - find('.qa-new-menu-toggle').click - end + aggregate_failures 'dropdown links in the navigation bar' do + expect(page).to have_link('New issue') + expect(page).to have_link('New merge request') + expect(page).to have_link('New snippet', href: new_project_snippet_path(project)) + end - # The dropdown above the tree - page.within('.repo-breadcrumb') do - find('.qa-add-to-tree').click + find('.qa-new-menu-toggle').click + end - aggregate_failures 'dropdown links above the repo tree' do - expect(page).to have_link('New file') - expect(page).to have_link('Upload file') - expect(page).to have_link('New directory') - expect(page).to have_link('New branch') - expect(page).to have_link('New tag') + # The dropdown above the tree + page.within('.repo-breadcrumb') do + find('.qa-add-to-tree').click + + aggregate_failures 'dropdown links above the repo tree' do + expect(page).to have_link('New file') + expect(page).to have_link('Upload file') + expect(page).to have_link('New directory') + expect(page).to have_link('New branch') + expect(page).to have_link('New tag') + end end + + # The Web IDE + expect(page).to have_link('Web IDE') end - # The Web IDE - expect(page).to have_link('Web IDE') - end + it 'hides the links when the project is archived' do + project.update!(archived: true) - it 'hides the links when the project is archived' do - project.update!(archived: true) + visit project_path(project) - visit project_path(project) + page.within('.header-new') do + find('.qa-new-menu-toggle').click - page.within('.header-new') do - find('.qa-new-menu-toggle').click + aggregate_failures 'dropdown links' do + expect(page).not_to have_link('New issue') + expect(page).not_to have_link('New merge request') + expect(page).not_to have_link('New snippet', href: new_project_snippet_path(project)) + end - aggregate_failures 'dropdown links' do - expect(page).not_to have_link('New issue') - expect(page).not_to have_link('New merge request') - expect(page).not_to have_link('New snippet', href: new_project_snippet_path(project)) + find('.qa-new-menu-toggle').click end - find('.qa-new-menu-toggle').click + expect(page).not_to have_selector('.qa-add-to-tree') + + expect(page).not_to have_link('Web IDE') end + end - expect(page).not_to have_selector('.qa-add-to-tree') + context "Web IDE link" do + where(:merge_requests_access_level, :user_level, :expect_ide_link) do + ::ProjectFeature::DISABLED | :guest | false + ::ProjectFeature::DISABLED | :developer | true + ::ProjectFeature::PRIVATE | :guest | false + ::ProjectFeature::PRIVATE | :developer | true + ::ProjectFeature::ENABLED | :guest | true + ::ProjectFeature::ENABLED | :developer | true + end - expect(page).not_to have_link('Web IDE') + with_them do + before do + project.project_feature.update!({ merge_requests_access_level: merge_requests_access_level }) + project.add_user(user, user_level) + visit project_path(project) + end + + it "updates Web IDE link" do + expect(page.has_link?('Web IDE')).to be(expect_ide_link) + end + end end end diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json new file mode 100644 index 00000000000..f747088f87e --- /dev/null +++ b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json @@ -0,0 +1,166 @@ +{ + "id": 283, + "name": "internal", + "path": "internal", + "owner_id": null, + "created_at": "2020-02-12T16:56:34.924Z", + "updated_at": "2020-02-12T16:56:38.710Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 10, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": null, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null, + "children": [ + { + "id": 284, + "name": "public", + "path": "public", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 20, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + }, + { + "id": 285, + "name": "internal", + "path": "internal", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 10, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + }, + { + "id": 286, + "name": "private", + "path": "private", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 0, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + } + ] +} diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json new file mode 100644 index 00000000000..1328e596fa5 --- /dev/null +++ b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json @@ -0,0 +1,166 @@ +{ + "id": 283, + "name": "private", + "path": "private", + "owner_id": null, + "created_at": "2020-02-12T16:56:34.924Z", + "updated_at": "2020-02-12T16:56:38.710Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 0, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": null, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null, + "children": [ + { + "id": 284, + "name": "public", + "path": "public", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 20, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + }, + { + "id": 285, + "name": "internal", + "path": "internal", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 10, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + }, + { + "id": 286, + "name": "private", + "path": "private", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 0, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + } + ] +} diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json new file mode 100644 index 00000000000..29020e92004 --- /dev/null +++ b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json @@ -0,0 +1,166 @@ +{ + "id": 283, + "name": "public", + "path": "public", + "owner_id": null, + "created_at": "2020-02-12T16:56:34.924Z", + "updated_at": "2020-02-12T16:56:38.710Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 20, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": null, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null, + "children": [ + { + "id": 284, + "name": "public", + "path": "public", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 20, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + }, + { + "id": 285, + "name": "internal", + "path": "internal", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 10, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + }, + { + "id": 286, + "name": "private", + "path": "private", + "owner_id": null, + "created_at": "2020-02-12T17:33:00.575Z", + "updated_at": "2020-02-12T17:33:00.575Z", + "description": "", + "avatar": { + "url": null + }, + "membership_lock": false, + "share_with_group_lock": false, + "visibility_level": 0, + "request_access_enabled": true, + "ldap_sync_status": "ready", + "ldap_sync_error": null, + "ldap_sync_last_update_at": null, + "ldap_sync_last_successful_update_at": null, + "ldap_sync_last_sync_at": null, + "lfs_enabled": null, + "parent_id": 283, + "shared_runners_minutes_limit": null, + "repository_size_limit": null, + "require_two_factor_authentication": false, + "two_factor_grace_period": 48, + "plan_id": null, + "project_creation_level": 2, + "trial_ends_on": null, + "file_template_project_id": null, + "custom_project_templates_group_id": null, + "auto_devops_enabled": null, + "extra_shared_runners_minutes_limit": null, + "last_ci_minutes_notification_at": null, + "last_ci_minutes_usage_notification_level": null, + "subgroup_creation_level": 1, + "emails_disabled": null, + "max_pages_size": null, + "max_artifacts_size": null, + "mentions_disabled": null + } + ] +} diff --git a/spec/fixtures/lsif.json.gz b/spec/fixtures/lsif.json.gz index 275a87e738b..3f74588cd7e 100644 Binary files a/spec/fixtures/lsif.json.gz and b/spec/fixtures/lsif.json.gz differ diff --git a/spec/frontend/ide/mock_data.js b/spec/frontend/ide/mock_data.js index a1b57dca6bc..472516b6a2c 100644 --- a/spec/frontend/ide/mock_data.js +++ b/spec/frontend/ide/mock_data.js @@ -18,6 +18,7 @@ export const projectData = { }, mergeRequests: {}, merge_requests_enabled: true, + userPermissions: {}, default_branch: 'master', }; diff --git a/spec/frontend/ide/services/index_spec.js b/spec/frontend/ide/services/index_spec.js index 83a3cfe618b..55f174f4663 100644 --- a/spec/frontend/ide/services/index_spec.js +++ b/spec/frontend/ide/services/index_spec.js @@ -2,11 +2,17 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import services from '~/ide/services'; import Api from '~/api'; +import gqClient from '~/ide/services/gql'; import { escapeFileUrl } from '~/lib/utils/url_utility'; +import getUserPermissions from '~/ide/queries/getUserPermissions.query.graphql'; +import { projectData } from '../mock_data'; jest.mock('~/api'); +jest.mock('~/ide/services/gql'); -const TEST_PROJECT_ID = 'alice/wonderland'; +const TEST_NAMESPACE = 'alice'; +const TEST_PROJECT = 'wonderland'; +const TEST_PROJECT_ID = `${TEST_NAMESPACE}/${TEST_PROJECT}`; const TEST_BRANCH = 'master-patch-123'; const TEST_COMMIT_SHA = '123456789'; const TEST_FILE_PATH = 'README2.md'; @@ -111,4 +117,27 @@ describe('IDE services', () => { }, ); }); + + describe('getProjectData', () => { + it('combines gql and API requests', () => { + const gqlProjectData = { + userPermissions: { + bogus: true, + }, + }; + Api.project.mockReturnValue(Promise.resolve({ data: { ...projectData } })); + gqClient.query.mockReturnValue(Promise.resolve({ data: { project: gqlProjectData } })); + + return services.getProjectData(TEST_NAMESPACE, TEST_PROJECT).then(response => { + expect(response).toEqual({ data: { ...projectData, ...gqlProjectData } }); + expect(Api.project).toHaveBeenCalledWith(TEST_PROJECT_ID); + expect(gqClient.query).toHaveBeenCalledWith({ + query: getUserPermissions, + variables: { + projectPath: TEST_PROJECT_ID, + }, + }); + }); + }); + }); }); diff --git a/spec/frontend/ide/stores/getters_spec.js b/spec/frontend/ide/stores/getters_spec.js index 21c5e886738..011be95c1d2 100644 --- a/spec/frontend/ide/stores/getters_spec.js +++ b/spec/frontend/ide/stores/getters_spec.js @@ -2,6 +2,8 @@ import * as getters from '~/ide/stores/getters'; import { createStore } from '~/ide/stores'; import { file } from '../helpers'; +const TEST_PROJECT_ID = 'test_project'; + describe('IDE store getters', () => { let localState; let localStore; @@ -398,4 +400,38 @@ describe('IDE store getters', () => { }, ); }); + + describe('findProjectPermissions', () => { + it('returns false if project not found', () => { + expect(localStore.getters.findProjectPermissions(TEST_PROJECT_ID)).toEqual({}); + }); + + it('finds permission in given project', () => { + const userPermissions = { + readMergeRequest: true, + createMergeRequestsIn: false, + }; + + localState.projects[TEST_PROJECT_ID] = { userPermissions }; + + expect(localStore.getters.findProjectPermissions(TEST_PROJECT_ID)).toBe(userPermissions); + }); + }); + + describe.each` + getterName | permissionKey + ${'canReadMergeRequests'} | ${'readMergeRequest'} + ${'canCreateMergeRequests'} | ${'createMergeRequestIn'} + `('$getterName', ({ getterName, permissionKey }) => { + it.each([true, false])('finds permission for current project (%s)', val => { + localState.projects[TEST_PROJECT_ID] = { + userPermissions: { + [permissionKey]: val, + }, + }; + localState.currentProjectId = TEST_PROJECT_ID; + + expect(localStore.getters[getterName]).toBe(val); + }); + }); }); diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap index 49feae0884e..2d411fcce79 100644 --- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap +++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap @@ -15,6 +15,7 @@ exports[`Repository table row component renders table row 1`] = ` @@ -64,6 +65,7 @@ exports[`Repository table row component renders table row for path with special diff --git a/spec/javascripts/ide/components/commit_sidebar/new_merge_request_option_spec.js b/spec/javascripts/ide/components/commit_sidebar/new_merge_request_option_spec.js index 02caf689c50..7c0b4000229 100644 --- a/spec/javascripts/ide/components/commit_sidebar/new_merge_request_option_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/new_merge_request_option_spec.js @@ -1,13 +1,15 @@ import Vue from 'vue'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { projectData, branches } from 'spec/ide/mock_data'; -import { resetStore } from 'spec/ide/helpers'; import NewMergeRequestOption from '~/ide/components/commit_sidebar/new_merge_request_option.vue'; -import store from '~/ide/stores'; -import consts from '../../../../../app/assets/javascripts/ide/stores/modules/commit/constants'; +import { createStore } from '~/ide/stores'; +import { PERMISSION_CREATE_MR } from '~/ide/constants'; +import consts from '~/ide/stores/modules/commit/constants'; describe('create new MR checkbox', () => { + let store; let vm; + const setMR = () => { vm.$store.state.currentMergeRequestId = '1'; vm.$store.state.projects[store.state.currentProjectId].mergeRequests[ @@ -15,6 +17,10 @@ describe('create new MR checkbox', () => { ] = { foo: 'bar' }; }; + const setPermissions = permissions => { + store.state.projects[store.state.currentProjectId].userPermissions = permissions; + }; + const createComponent = ({ currentBranchId = 'master', createNewBranch = false } = {}) => { const Component = Vue.extend(NewMergeRequestOption); @@ -25,20 +31,29 @@ describe('create new MR checkbox', () => { : consts.COMMIT_TO_CURRENT_BRANCH; vm.$store.state.currentBranchId = currentBranchId; - vm.$store.state.currentProjectId = 'abcproject'; - const proj = JSON.parse(JSON.stringify(projectData)); - proj.branches[currentBranchId] = branches.find(branch => branch.name === currentBranchId); - - Vue.set(vm.$store.state.projects, 'abcproject', proj); + store.state.projects.abcproject.branches[currentBranchId] = branches.find( + branch => branch.name === currentBranchId, + ); return vm.$mount(); }; + const findInput = () => vm.$el.querySelector('input[type="checkbox"]'); + const findLabel = () => vm.$el.querySelector('.js-ide-commit-new-mr'); + + beforeEach(() => { + store = createStore(); + + store.state.currentProjectId = 'abcproject'; + + const proj = JSON.parse(JSON.stringify(projectData)); + proj.userPermissions[PERMISSION_CREATE_MR] = true; + Vue.set(store.state.projects, 'abcproject', proj); + }); + afterEach(() => { vm.$destroy(); - - resetStore(vm.$store); }); describe('for default branch', () => { @@ -160,6 +175,24 @@ describe('create new MR checkbox', () => { .then(done) .catch(done.fail); }); + + it('shows enablded checkbox', () => { + expect(findLabel().classList.contains('is-disabled')).toBe(false); + expect(findInput().disabled).toBe(false); + }); + }); + + describe('when user cannot create MR', () => { + beforeEach(() => { + setPermissions({ [PERMISSION_CREATE_MR]: false }); + + createComponent({ currentBranchId: 'regular' }); + }); + + it('disabled checkbox', () => { + expect(findLabel().classList.contains('is-disabled')).toBe(true); + expect(findInput().disabled).toBe(true); + }); }); it('dispatches toggleShouldCreateMR when clicking checkbox', () => { diff --git a/spec/javascripts/ide/components/nav_dropdown_button_spec.js b/spec/javascripts/ide/components/nav_dropdown_button_spec.js index 0d63869fba2..bbaf97164ea 100644 --- a/spec/javascripts/ide/components/nav_dropdown_button_spec.js +++ b/spec/javascripts/ide/components/nav_dropdown_button_spec.js @@ -2,62 +2,92 @@ import Vue from 'vue'; import { trimText } from 'spec/helpers/text_helper'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import NavDropdownButton from '~/ide/components/nav_dropdown_button.vue'; -import store from '~/ide/stores'; -import { resetStore } from '../helpers'; +import { createStore } from '~/ide/stores'; describe('NavDropdown', () => { const TEST_BRANCH_ID = 'lorem-ipsum-dolar'; const TEST_MR_ID = '12345'; - const Component = Vue.extend(NavDropdownButton); + let store; let vm; beforeEach(() => { - vm = mountComponentWithStore(Component, { store }); - - vm.$mount(); + store = createStore(); }); afterEach(() => { vm.$destroy(); - - resetStore(store); }); - it('renders empty placeholders, if state is falsey', () => { - expect(trimText(vm.$el.textContent)).toEqual('- -'); - }); + const createComponent = (props = {}) => { + vm = mountComponentWithStore(Vue.extend(NavDropdownButton), { props, store }); + vm.$mount(); + }; - it('renders branch name, if state has currentBranchId', done => { - vm.$store.state.currentBranchId = TEST_BRANCH_ID; + const findIcon = name => vm.$el.querySelector(`.ic-${name}`); + const findMRIcon = () => findIcon('merge-request'); + const findBranchIcon = () => findIcon('branch'); - vm.$nextTick() - .then(() => { - expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} -`); - }) - .then(done) - .catch(done.fail); - }); + describe('normal', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders empty placeholders, if state is falsey', () => { + expect(trimText(vm.$el.textContent)).toEqual('- -'); + }); - it('renders mr id, if state has currentMergeRequestId', done => { - vm.$store.state.currentMergeRequestId = TEST_MR_ID; + it('renders branch name, if state has currentBranchId', done => { + vm.$store.state.currentBranchId = TEST_BRANCH_ID; - vm.$nextTick() - .then(() => { - expect(trimText(vm.$el.textContent)).toEqual(`- !${TEST_MR_ID}`); - }) - .then(done) - .catch(done.fail); + vm.$nextTick() + .then(() => { + expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} -`); + }) + .then(done) + .catch(done.fail); + }); + + it('renders mr id, if state has currentMergeRequestId', done => { + vm.$store.state.currentMergeRequestId = TEST_MR_ID; + + vm.$nextTick() + .then(() => { + expect(trimText(vm.$el.textContent)).toEqual(`- !${TEST_MR_ID}`); + }) + .then(done) + .catch(done.fail); + }); + + it('renders branch and mr, if state has both', done => { + vm.$store.state.currentBranchId = TEST_BRANCH_ID; + vm.$store.state.currentMergeRequestId = TEST_MR_ID; + + vm.$nextTick() + .then(() => { + expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} !${TEST_MR_ID}`); + }) + .then(done) + .catch(done.fail); + }); + + it('shows icons', () => { + expect(findBranchIcon()).toBeTruthy(); + expect(findMRIcon()).toBeTruthy(); + }); }); - it('renders branch and mr, if state has both', done => { - vm.$store.state.currentBranchId = TEST_BRANCH_ID; - vm.$store.state.currentMergeRequestId = TEST_MR_ID; + describe('with showMergeRequests false', () => { + beforeEach(() => { + createComponent({ showMergeRequests: false }); + }); + + it('shows single empty placeholder, if state is falsey', () => { + expect(trimText(vm.$el.textContent)).toEqual('-'); + }); - vm.$nextTick() - .then(() => { - expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} !${TEST_MR_ID}`); - }) - .then(done) - .catch(done.fail); + it('shows only branch icon', () => { + expect(findBranchIcon()).toBeTruthy(); + expect(findMRIcon()).toBe(null); + }); }); }); diff --git a/spec/javascripts/ide/components/nav_dropdown_spec.js b/spec/javascripts/ide/components/nav_dropdown_spec.js index fe1d0ca371d..dfb4d03540f 100644 --- a/spec/javascripts/ide/components/nav_dropdown_spec.js +++ b/spec/javascripts/ide/components/nav_dropdown_spec.js @@ -3,6 +3,9 @@ import Vue from 'vue'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import store from '~/ide/stores'; import NavDropdown from '~/ide/components/nav_dropdown.vue'; +import { PERMISSION_READ_MR } from '~/ide/constants'; + +const TEST_PROJECT_ID = 'lorem-ipsum'; describe('IDE NavDropdown', () => { const Component = Vue.extend(NavDropdown); @@ -10,6 +13,12 @@ describe('IDE NavDropdown', () => { let $dropdown; beforeEach(() => { + store.state.currentProjectId = TEST_PROJECT_ID; + Vue.set(store.state.projects, TEST_PROJECT_ID, { + userPermissions: { + [PERMISSION_READ_MR]: true, + }, + }); vm = mountComponentWithStore(Component, { store }); $dropdown = $(vm.$el); @@ -21,6 +30,9 @@ describe('IDE NavDropdown', () => { vm.$destroy(); }); + const findIcon = name => vm.$el.querySelector(`.ic-${name}`); + const findMRIcon = () => findIcon('merge-request'); + it('renders nothing initially', () => { expect(vm.$el).not.toContainElement('.ide-nav-form'); }); @@ -47,4 +59,22 @@ describe('IDE NavDropdown', () => { .then(done) .catch(done.fail); }); + + it('renders merge request icon', () => { + expect(findMRIcon()).not.toBeNull(); + }); + + describe('when user cannot read merge requests', () => { + beforeEach(done => { + store.state.projects[TEST_PROJECT_ID].userPermissions = {}; + + vm.$nextTick() + .then(done) + .catch(done.fail); + }); + + it('does not render merge requests', () => { + expect(findMRIcon()).toBeNull(); + }); + }); }); diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js index 498e5142f0c..4917984cd42 100644 --- a/spec/javascripts/ide/stores/actions/merge_request_spec.js +++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js @@ -8,7 +8,7 @@ import actions, { openMergeRequest, } from '~/ide/stores/actions/merge_request'; import service from '~/ide/services'; -import { activityBarViews } from '~/ide/constants'; +import { activityBarViews, PERMISSION_READ_MR } from '~/ide/constants'; import { resetStore } from '../../helpers'; const TEST_PROJECT = 'abcproject'; @@ -23,6 +23,9 @@ describe('IDE store merge request actions', () => { store.state.projects[TEST_PROJECT] = { id: TEST_PROJECT_ID, mergeRequests: {}, + userPermissions: { + [PERMISSION_READ_MR]: true, + }, }; }); @@ -79,6 +82,19 @@ describe('IDE store merge request actions', () => { }) .catch(done.fail); }); + + it('does nothing if user cannot read MRs', done => { + store.state.projects[TEST_PROJECT].userPermissions[PERMISSION_READ_MR] = false; + + store + .dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' }) + .then(() => { + expect(service.getProjectMergeRequests).not.toHaveBeenCalled(); + expect(store.state.currentMergeRequestId).toBe(''); + }) + .then(done) + .catch(done.fail); + }); }); describe('no merge requests for branch available case', () => { diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js index 056822bcfe5..fb8cb300209 100644 --- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js @@ -7,7 +7,7 @@ import eventHub from '~/ide/eventhub'; import consts from '~/ide/stores/modules/commit/constants'; import * as mutationTypes from '~/ide/stores/modules/commit/mutation_types'; import * as actions from '~/ide/stores/modules/commit/actions'; -import { commitActionTypes } from '~/ide/constants'; +import { commitActionTypes, PERMISSION_CREATE_MR } from '~/ide/constants'; import testAction from '../../../../helpers/vuex_action_helper'; const TEST_COMMIT_SHA = '123456789'; @@ -313,6 +313,9 @@ describe('IDE commit module actions', () => { }, }, }, + userPermissions: { + [PERMISSION_CREATE_MR]: true, + }, }, }, }); diff --git a/spec/lib/gitlab/import_export/group_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group_tree_restorer_spec.rb index 9aa9471d155..d6bda7507c6 100644 --- a/spec/lib/gitlab/import_export/group_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/group_tree_restorer_spec.rb @@ -125,4 +125,31 @@ describe Gitlab::ImportExport::GroupTreeRestorer do end end end + + context 'group visibility levels' do + let(:user) { create(:user) } + let(:shared) { Gitlab::ImportExport::Shared.new(group) } + let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group, group_hash: nil) } + + before do + setup_import_export_config(filepath) + + group_tree_restorer.restore + end + + shared_examples 'with visibility level' do |visibility_level, expected_visibilities| + context "when visibility level is #{visibility_level}" do + let(:group) { create(:group, visibility_level) } + let(:filepath) { "group_exports/visibility_levels/#{visibility_level}" } + + it "imports all subgroups as #{visibility_level}" do + expect(group.children.map(&:visibility_level)).to eq(expected_visibilities) + end + end + end + + include_examples 'with visibility level', :public, [20, 10, 0] + include_examples 'with visibility level', :private, [0, 0, 0] + include_examples 'with visibility level', :internal, [10, 10, 0] + end end diff --git a/spec/lib/gitlab/import_export/group_tree_saver_spec.rb b/spec/lib/gitlab/import_export/group_tree_saver_spec.rb index d6aea8d1e69..d5ab8ca1e8d 100644 --- a/spec/lib/gitlab/import_export/group_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/group_tree_saver_spec.rb @@ -80,7 +80,7 @@ describe Gitlab::ImportExport::GroupTreeSaver do end it 'saves the correct json' do - expect(saved_group_json).to include({ 'description' => 'description', 'visibility_level' => 20 }) + expect(saved_group_json).to include({ 'description' => 'description' }) end it 'has milestones' do diff --git a/spec/requests/api/group_import_spec.rb b/spec/requests/api/group_import_spec.rb index 016ed6ff491..1594881677f 100644 --- a/spec/requests/api/group_import_spec.rb +++ b/spec/requests/api/group_import_spec.rb @@ -45,6 +45,14 @@ describe API::GroupImport do expect(response).to have_gitlab_http_status(202) end + it 'creates private group' do + expect { subject }.to change { Group.count }.by(1) + + group = Group.find_by(name: 'test-import-group') + + expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + context 'when importing to a parent group' do before do group.add_owner(user) @@ -59,6 +67,34 @@ describe API::GroupImport do expect(group.children.count).to eq(1) end + context 'when parent group is private or internal' do + let(:public_parent_group) { create(:group, :public) } + let(:internal_parent_group) { create(:group, :internal) } + + before do + public_parent_group.add_owner(user) + internal_parent_group.add_owner(user) + end + + it 'imports public group' do + params[:parent_id] = public_parent_group.id + + subject + + expect(response).to have_gitlab_http_status(202) + expect(public_parent_group.children.first.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) + end + + it 'imports internal group' do + params[:parent_id] = internal_parent_group.id + + subject + + expect(response).to have_gitlab_http_status(202) + expect(internal_parent_group.children.first.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) + end + end + context 'when parent group is invalid' do it 'returns 404 and does not create new group' do params[:parent_id] = 99999 diff --git a/spec/requests/api/lsif_data_spec.rb b/spec/requests/api/lsif_data_spec.rb index 2e0670ded95..214bc832cda 100644 --- a/spec/requests/api/lsif_data_spec.rb +++ b/spec/requests/api/lsif_data_spec.rb @@ -61,7 +61,11 @@ describe API::LsifData do 'end_line' => 8, 'start_char' => 13, 'start_line' => 8, - 'definition_url' => project_blob_path(project, "#{commit.id}/morestrings/reverse.go", anchor: 'L5') + 'definition_url' => project_blob_path(project, "#{commit.id}/morestrings/reverse.go", anchor: 'L5'), + 'hover' => [{ + 'language' => 'go', + 'value' => Gitlab::Highlight.highlight(nil, 'func Func2(i int) string', language: 'go') + }] }) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 00af0937dd7..58190beb38a 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -267,8 +267,7 @@ describe API::MergeRequests do it 'returns an array of merge requests with any label when filtering by any label' do get api(endpoint_path, user), params: { labels: [" #{label.title} ", " #{label2.title} "] } - expect_successful_response_with_paginated_array - expect(json_response.length).to eq(1) + expect_paginated_array_response([merge_request.id]) expect(json_response.first['labels']).to eq([label2.title, label.title]) expect(json_response.first['id']).to eq(merge_request.id) end @@ -276,8 +275,7 @@ describe API::MergeRequests do it 'returns an array of merge requests with any label when filtering by any label' do get api(endpoint_path, user), params: { labels: ["#{label.title} , #{label2.title}"] } - expect_successful_response_with_paginated_array - expect(json_response.length).to eq(1) + expect_paginated_array_response([merge_request.id]) expect(json_response.first['labels']).to eq([label2.title, label.title]) expect(json_response.first['id']).to eq(merge_request.id) end @@ -285,17 +283,16 @@ describe API::MergeRequests do it 'returns an array of merge requests with any label when filtering by any label' do get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_ANY } - expect_successful_response_with_paginated_array - expect(json_response.length).to eq(1) + expect_paginated_array_response([merge_request.id]) expect(json_response.first['id']).to eq(merge_request.id) end it 'returns an array of merge requests without a label when filtering by no label' do get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_NONE } - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_locked.id, merge_request_closed.id - ]) + ) end end @@ -315,7 +312,7 @@ describe API::MergeRequests do get api(path, user) - expect_paginated_array_response([mr2.id]) + expect_response_contain_exactly(mr2.id) end context 'with ordering' do @@ -413,9 +410,9 @@ describe API::MergeRequests do it 'returns merge requests with the given source branch' do get api(endpoint_path, user), params: { source_branch: merge_request_closed.source_branch, state: 'all' } - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_locked.id, merge_request_closed.id - ]) + ) end end @@ -423,9 +420,9 @@ describe API::MergeRequests do it 'returns merge requests with the given target branch' do get api(endpoint_path, user), params: { target_branch: merge_request_closed.target_branch, state: 'all' } - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_locked.id, merge_request_closed.id - ]) + ) end end end @@ -448,10 +445,10 @@ describe API::MergeRequests do it 'returns an array of all merge requests' do get api('/merge_requests', user), params: { scope: 'all' } - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_locked.id, merge_request_closed.id, merge_request.id - ]) + ) end it "returns authentication error without any scope" do @@ -487,9 +484,10 @@ describe API::MergeRequests do it 'returns an array of all merge requests except unauthorized ones' do get api('/merge_requests', user), params: { scope: :all } - expect_paginated_array_response([ - merge_request_merged.id, merge_request2.id, merge_request_locked.id, merge_request_closed.id, merge_request.id - ]) + expect_response_contain_exactly( + merge_request_merged.id, merge_request2.id, merge_request_locked.id, + merge_request_closed.id, merge_request.id + ) end it "returns an array of no merge_requests when wip=yes" do @@ -501,9 +499,10 @@ describe API::MergeRequests do it "returns an array of no merge_requests when wip=no" do get api("/merge_requests", user), params: { wip: 'no' } - expect_paginated_array_response([ - merge_request_merged.id, merge_request2.id, merge_request_locked.id, merge_request_closed.id, merge_request.id - ]) + expect_response_contain_exactly( + merge_request_merged.id, merge_request2.id, merge_request_locked.id, + merge_request_closed.id, merge_request.id + ) end it 'does not return unauthorized merge requests' do @@ -512,9 +511,10 @@ describe API::MergeRequests do get api('/merge_requests', user), params: { scope: :all } - expect_paginated_array_response([ - merge_request_merged.id, merge_request2.id, merge_request_locked.id, merge_request_closed.id, merge_request.id - ]) + expect_response_contain_exactly( + merge_request_merged.id, merge_request2.id, merge_request_locked.id, + merge_request_closed.id, merge_request.id + ) expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request3.id) end @@ -523,7 +523,7 @@ describe API::MergeRequests do get api('/merge_requests', user2) - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns an array of merge requests authored by the given user' do @@ -531,7 +531,7 @@ describe API::MergeRequests do get api('/merge_requests', user), params: { author_id: user2.id, scope: :all } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns an array of merge requests assigned to the given user' do @@ -539,7 +539,7 @@ describe API::MergeRequests do get api('/merge_requests', user), params: { assignee_id: user2.id, scope: :all } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns an array of merge requests with no assignee' do @@ -547,7 +547,7 @@ describe API::MergeRequests do get api('/merge_requests', user), params: { assignee_id: 'None', scope: :all } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns an array of merge requests with any assignee' do @@ -556,10 +556,10 @@ describe API::MergeRequests do get api('/merge_requests', user), params: { assignee_id: 'Any', scope: :all } - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request2.id, merge_request_locked.id, merge_request_closed.id, merge_request.id - ]) + ) end it 'returns an array of merge requests assigned to me' do @@ -567,7 +567,7 @@ describe API::MergeRequests do get api('/merge_requests', user2), params: { scope: 'assigned_to_me' } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns an array of merge requests assigned to me (kebab-case)' do @@ -575,7 +575,7 @@ describe API::MergeRequests do get api('/merge_requests', user2), params: { scope: 'assigned-to-me' } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns an array of merge requests created by me' do @@ -583,7 +583,7 @@ describe API::MergeRequests do get api('/merge_requests', user2), params: { scope: 'created_by_me' } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns an array of merge requests created by me (kebab-case)' do @@ -591,7 +591,7 @@ describe API::MergeRequests do get api('/merge_requests', user2), params: { scope: 'created-by-me' } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end it 'returns merge requests reacted by the authenticated user by the given emoji' do @@ -600,16 +600,16 @@ describe API::MergeRequests do get api('/merge_requests', user2), params: { my_reaction_emoji: award_emoji.name, scope: 'all' } - expect_paginated_array_response([merge_request3.id]) + expect_response_contain_exactly(merge_request3.id) end context 'source_branch param' do it 'returns merge requests with the given source branch' do get api('/merge_requests', user), params: { source_branch: merge_request_closed.source_branch, state: 'all' } - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_locked.id, merge_request_closed.id - ]) + ) end end @@ -617,9 +617,9 @@ describe API::MergeRequests do it 'returns merge requests with the given target branch' do get api('/merge_requests', user), params: { target_branch: merge_request_closed.target_branch, state: 'all' } - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_locked.id, merge_request_closed.id - ]) + ) end end @@ -628,7 +628,7 @@ describe API::MergeRequests do get api('/merge_requests?created_before=2000-01-02T00:00:00.060Z', user) - expect_paginated_array_response([merge_request2.id]) + expect_response_contain_exactly(merge_request2.id) end it 'returns merge requests created after a specific date' do @@ -636,7 +636,7 @@ describe API::MergeRequests do get api("/merge_requests?created_after=#{merge_request2.created_at}", user) - expect_paginated_array_response([merge_request2.id]) + expect_response_contain_exactly(merge_request2.id) end it 'returns merge requests updated before a specific date' do @@ -644,7 +644,7 @@ describe API::MergeRequests do get api('/merge_requests?updated_before=2000-01-02T00:00:00.060Z', user) - expect_paginated_array_response([merge_request2.id]) + expect_response_contain_exactly(merge_request2.id) end it 'returns merge requests updated after a specific date' do @@ -652,7 +652,7 @@ describe API::MergeRequests do get api("/merge_requests?updated_after=#{merge_request2.updated_at}", user) - expect_paginated_array_response([merge_request2.id]) + expect_response_contain_exactly(merge_request2.id) end context 'search params' do @@ -663,13 +663,13 @@ describe API::MergeRequests do it 'returns merge requests matching given search string for title' do get api("/merge_requests", user), params: { search: merge_request.title } - expect_paginated_array_response([merge_request.id]) + expect_response_contain_exactly(merge_request.id) end it 'returns merge requests matching given search string for title and scoped in title' do get api("/merge_requests", user), params: { search: merge_request.title, in: 'title' } - expect_paginated_array_response([merge_request.id]) + expect_response_contain_exactly(merge_request.id) end it 'returns an empty array if no merge request matches given search string for description and scoped in title' do @@ -681,7 +681,7 @@ describe API::MergeRequests do it 'returns merge requests for project matching given search string for description' do get api("/merge_requests", user), params: { project_id: project.id, search: merge_request.description } - expect_paginated_array_response([merge_request.id]) + expect_response_contain_exactly(merge_request.id) end end @@ -689,7 +689,7 @@ describe API::MergeRequests do it 'returns merge requests with the given state' do get api('/merge_requests', user), params: { state: 'locked' } - expect_paginated_array_response([merge_request_locked.id]) + expect_response_contain_exactly(merge_request_locked.id) end end end @@ -792,10 +792,10 @@ describe API::MergeRequests do it 'returns an array excluding merge_requests from archived projects' do get api(endpoint_path, user) - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_locked.id, merge_request_closed.id, merge_request.id - ]) + ) end context 'with non_archived param set as false' do @@ -804,10 +804,10 @@ describe API::MergeRequests do get api(path, user) - expect_paginated_array_response([ + expect_response_contain_exactly( merge_request_merged.id, merge_request_archived.id, merge_request_locked.id, merge_request_closed.id, merge_request.id - ]) + ) end end end diff --git a/spec/requests/self_monitoring_project_spec.rb b/spec/requests/self_monitoring_project_spec.rb index 5e46645e7a0..6a0258c349f 100644 --- a/spec/requests/self_monitoring_project_spec.rb +++ b/spec/requests/self_monitoring_project_spec.rb @@ -17,11 +17,7 @@ describe 'Self-Monitoring project requests' do login_as(admin) end - context 'with feature flag disabled' do - it_behaves_like 'not accessible if feature flag is disabled' - end - - context 'with feature flag enabled' do + context 'when the self monitoring project is created' do let(:status_api) { status_create_self_monitoring_project_admin_application_settings_path } it_behaves_like 'triggers async worker, returns sidekiq job_id with response accepted' @@ -45,11 +41,7 @@ describe 'Self-Monitoring project requests' do login_as(admin) end - context 'with feature flag disabled' do - it_behaves_like 'not accessible if feature flag is disabled' - end - - context 'with feature flag enabled' do + context 'when the self monitoring project is being created' do it_behaves_like 'handles invalid job_id' context 'when job is in progress' do @@ -129,11 +121,7 @@ describe 'Self-Monitoring project requests' do login_as(admin) end - context 'with feature flag disabled' do - it_behaves_like 'not accessible if feature flag is disabled' - end - - context 'with feature flag enabled' do + context 'when the self monitoring project is deleted' do let(:status_api) { status_delete_self_monitoring_project_admin_application_settings_path } it_behaves_like 'triggers async worker, returns sidekiq job_id with response accepted' @@ -157,11 +145,7 @@ describe 'Self-Monitoring project requests' do login_as(admin) end - context 'with feature flag disabled' do - it_behaves_like 'not accessible if feature flag is disabled' - end - - context 'with feature flag enabled' do + context 'when the self monitoring project is being deleted' do it_behaves_like 'handles invalid job_id' context 'when job is in progress' do diff --git a/spec/services/projects/lsif_data_service_spec.rb b/spec/services/projects/lsif_data_service_spec.rb index 29a99a96c41..93579869d1d 100644 --- a/spec/services/projects/lsif_data_service_spec.rb +++ b/spec/services/projects/lsif_data_service_spec.rb @@ -12,6 +12,10 @@ describe Projects::LsifDataService do let(:service) { described_class.new(artifact.file, project, params) } describe '#execute' do + def highlighted_value(value) + [{ language: 'go', value: Gitlab::Highlight.highlight(nil, value, language: 'go') }] + end + context 'fetched lsif file', :use_clean_rails_memory_store_caching do it 'is cached' do service.execute @@ -32,42 +36,48 @@ describe Projects::LsifDataService do end_line: 6, start_char: 5, start_line: 6, - definition_url: "#{path_prefix}/main.go#L7" + definition_url: "#{path_prefix}/main.go#L7", + hover: highlighted_value('func main()') }, { end_char: 36, end_line: 3, start_char: 1, start_line: 3, - definition_url: "#{path_prefix}/main.go#L4" + definition_url: "#{path_prefix}/main.go#L4", + hover: highlighted_value('package "github.com/user/hello/morestrings" ("github.com/user/hello/morestrings")') }, { end_char: 12, end_line: 7, start_char: 1, start_line: 7, - definition_url: "#{path_prefix}/main.go#L4" + definition_url: "#{path_prefix}/main.go#L4", + hover: highlighted_value('package "github.com/user/hello/morestrings" ("github.com/user/hello/morestrings")') }, { end_char: 20, end_line: 7, start_char: 13, start_line: 7, - definition_url: "#{path_prefix}/morestrings/reverse.go#L11" + definition_url: "#{path_prefix}/morestrings/reverse.go#L11", + hover: highlighted_value('func Reverse(s string) string') + [{ value: "This method reverses a string \n\n" }] }, { end_char: 12, end_line: 8, start_char: 1, start_line: 8, - definition_url: "#{path_prefix}/main.go#L4" + definition_url: "#{path_prefix}/main.go#L4", + hover: highlighted_value('package "github.com/user/hello/morestrings" ("github.com/user/hello/morestrings")') }, { end_char: 18, end_line: 8, start_char: 13, start_line: 8, - definition_url: "#{path_prefix}/morestrings/reverse.go#L5" + definition_url: "#{path_prefix}/morestrings/reverse.go#L5", + hover: highlighted_value('func Func2(i int) string') } ]) end @@ -82,7 +92,8 @@ describe Projects::LsifDataService do end_line: 11, start_char: 1, start_line: 11, - definition_url: "/#{project.full_path}/-/blob/#{commit_id}/morestrings/reverse.go#L12" + definition_url: "/#{project.full_path}/-/blob/#{commit_id}/morestrings/reverse.go#L12", + hover: highlighted_value('var a string') }) end end diff --git a/spec/support/helpers/api_helpers.rb b/spec/support/helpers/api_helpers.rb index 44c38df71b0..dc263d64bcc 100644 --- a/spec/support/helpers/api_helpers.rb +++ b/spec/support/helpers/api_helpers.rb @@ -58,6 +58,13 @@ module ApiHelpers expect(json_response.map { |item| item['id'] }).to eq(Array(items)) end + def expect_response_contain_exactly(*items) + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to be_an Array + expect(json_response.length).to eq(items.size) + expect(json_response.map { |item| item['id'] }).to contain_exactly(*items) + end + def stub_last_activity_update allow_any_instance_of(Users::ActivityService).to receive(:execute) end diff --git a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb b/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb index f6cb2555450..db11b1fe07d 100644 --- a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb +++ b/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb @@ -1,23 +1,5 @@ # frozen_string_literal: true -RSpec.shared_examples 'not accessible if feature flag is disabled' do - before do - stub_feature_flags(self_monitoring_project: false) - end - - it 'returns not_implemented' do - subject - - aggregate_failures do - expect(response).to have_gitlab_http_status(:not_implemented) - expect(json_response).to eq( - 'message' => _('Self-monitoring is not enabled on this GitLab server, contact your administrator.'), - 'documentation_url' => help_page_path('administration/monitoring/gitlab_self_monitoring_project/index') - ) - end - end -end - RSpec.shared_examples 'not accessible to non-admin users' do context 'with unauthenticated user' do it 'redirects to signin page' do diff --git a/spec/views/projects/tree/_tree_header.html.haml_spec.rb b/spec/views/projects/tree/_tree_header.html.haml_spec.rb index caf8c4d1969..69ad331f880 100644 --- a/spec/views/projects/tree/_tree_header.html.haml_spec.rb +++ b/spec/views/projects/tree/_tree_header.html.haml_spec.rb @@ -19,12 +19,12 @@ describe 'projects/tree/_tree_header' do allow(view).to receive(:can_collaborate_with_project?) { true } end - it 'does not render the WebIDE button when user cannot create fork or cannot open MR' do + it 'renders the WebIDE button when user can collaborate but not create fork or MR' do allow(view).to receive(:can?) { false } render - expect(rendered).not_to have_link('Web IDE') + expect(rendered).to have_link('Web IDE') end it 'renders the WebIDE button when user can create fork and can open MR in project' do @@ -43,4 +43,13 @@ describe 'projects/tree/_tree_header' do expect(rendered).to have_link('Web IDE', href: '#modal-confirm-fork') end + + it 'does not render the WebIDE button when user cannot collaborate or create mr' do + allow(view).to receive(:can?) { false } + allow(view).to receive(:can_collaborate_with_project?) { false } + + render + + expect(rendered).not_to have_link('Web IDE') + end end -- cgit v1.2.3