Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5366964a10
commit
b3a736ed88
|
|
@ -70,7 +70,7 @@ export default {
|
|||
:title="$options.currentBranchPermissionsTooltip"
|
||||
>
|
||||
<span
|
||||
class="ide-radio-label"
|
||||
class="ide-option-label"
|
||||
data-qa-selector="commit_to_current_branch_radio"
|
||||
v-html="commitToCurrentBranchText"
|
||||
></span>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,27 @@
|
|||
<script>
|
||||
import { createNamespacedHelpers } from 'vuex';
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
const {
|
||||
mapState: mapCommitState,
|
||||
mapActions: mapCommitActions,
|
||||
mapGetters: mapCommitGetters,
|
||||
} = createNamespacedHelpers('commit');
|
||||
const { mapActions: mapCommitActions, mapGetters: mapCommitGetters } = createNamespacedHelpers(
|
||||
'commit',
|
||||
);
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
computed: {
|
||||
...mapCommitState(['shouldCreateMR']),
|
||||
...mapCommitGetters(['shouldHideNewMrOption']),
|
||||
...mapCommitGetters(['shouldHideNewMrOption', 'shouldDisableNewMrOption', 'shouldCreateMR']),
|
||||
tooltipText() {
|
||||
if (this.shouldDisableNewMrOption) {
|
||||
return s__(
|
||||
'IDE|This option is disabled because you are not allowed to create merge requests in this project.',
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapCommitActions(['toggleShouldCreateMR']),
|
||||
|
|
@ -21,14 +32,19 @@ export default {
|
|||
<template>
|
||||
<fieldset v-if="!shouldHideNewMrOption">
|
||||
<hr class="my-2" />
|
||||
<label class="mb-0 js-ide-commit-new-mr">
|
||||
<label
|
||||
v-gl-tooltip="tooltipText"
|
||||
class="mb-0 js-ide-commit-new-mr"
|
||||
:class="{ 'is-disabled': shouldDisableNewMrOption }"
|
||||
>
|
||||
<input
|
||||
:disabled="shouldDisableNewMrOption"
|
||||
:checked="shouldCreateMR"
|
||||
type="checkbox"
|
||||
data-qa-selector="start_new_mr_checkbox"
|
||||
@change="toggleShouldCreateMR"
|
||||
/>
|
||||
<span class="prepend-left-10">
|
||||
<span class="prepend-left-10 ide-option-label">
|
||||
{{ __('Start a new merge request') }}
|
||||
</span>
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export default {
|
|||
@change="updateCommitAction($event.target.value)"
|
||||
/>
|
||||
<span class="prepend-left-10">
|
||||
<span v-if="label" class="ide-radio-label"> {{ label }} </span> <slot v-else></slot>
|
||||
<span v-if="label" class="ide-option-label"> {{ label }} </span> <slot v-else></slot>
|
||||
</span>
|
||||
</label>
|
||||
<div v-if="commitAction === value && showInput" class="ide-commit-new-branch">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import $ from 'jquery';
|
||||
import { mapGetters } from 'vuex';
|
||||
import NavForm from './nav_form.vue';
|
||||
import NavDropdownButton from './nav_dropdown_button.vue';
|
||||
|
||||
|
|
@ -13,6 +14,9 @@ export default {
|
|||
isVisibleDropdown: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['canReadMergeRequests']),
|
||||
},
|
||||
mounted() {
|
||||
this.addDropdownListeners();
|
||||
},
|
||||
|
|
@ -42,7 +46,9 @@ export default {
|
|||
|
||||
<template>
|
||||
<div ref="dropdown" class="btn-group ide-nav-dropdown dropdown">
|
||||
<nav-dropdown-button />
|
||||
<div class="dropdown-menu dropdown-menu-left p-0"><nav-form v-if="isVisibleDropdown" /></div>
|
||||
<nav-dropdown-button :show-merge-requests="canReadMergeRequests" />
|
||||
<div class="dropdown-menu dropdown-menu-left p-0">
|
||||
<nav-form v-if="isVisibleDropdown" :show-merge-requests="canReadMergeRequests" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@ export default {
|
|||
Icon,
|
||||
DropdownButton,
|
||||
},
|
||||
props: {
|
||||
showMergeRequests: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['currentBranchId', 'currentMergeRequestId']),
|
||||
mergeRequestLabel() {
|
||||
|
|
@ -25,10 +32,10 @@ export default {
|
|||
<template>
|
||||
<dropdown-button>
|
||||
<span class="row">
|
||||
<span class="col-7 text-truncate">
|
||||
<span class="col-auto text-truncate" :class="{ 'col-7': showMergeRequests }">
|
||||
<icon :size="16" :aria-label="__('Current Branch')" name="branch" /> {{ branchLabel }}
|
||||
</span>
|
||||
<span class="col-5 pl-0 text-truncate">
|
||||
<span v-if="showMergeRequests" class="col-5 pl-0 text-truncate">
|
||||
<icon :size="16" :aria-label="__('Merge Request')" name="merge-request" />
|
||||
{{ mergeRequestLabel }}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -11,12 +11,19 @@ export default {
|
|||
BranchesSearchList,
|
||||
MergeRequestSearchList,
|
||||
},
|
||||
props: {
|
||||
showMergeRequests: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ide-nav-form p-0">
|
||||
<tabs stop-propagation>
|
||||
<tabs v-if="showMergeRequests" stop-propagation>
|
||||
<tab active>
|
||||
<template slot="title">
|
||||
{{ __('Branches') }}
|
||||
|
|
@ -30,5 +37,6 @@ export default {
|
|||
<merge-request-search-list />
|
||||
</tab>
|
||||
</tabs>
|
||||
<branches-search-list v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ export const MAX_BODY_LENGTH = 72;
|
|||
export const FILE_VIEW_MODE_EDITOR = 'editor';
|
||||
export const FILE_VIEW_MODE_PREVIEW = 'preview';
|
||||
|
||||
export const PERMISSION_CREATE_MR = 'createMergeRequestIn';
|
||||
export const PERMISSION_READ_MR = 'readMergeRequest';
|
||||
|
||||
export const activityBarViews = {
|
||||
edit: 'ide-tree',
|
||||
commit: 'commit-section',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
query getUserPermissions($projectPath: ID!) {
|
||||
project(fullPath: $projectPath) {
|
||||
userPermissions {
|
||||
createMergeRequestIn,
|
||||
readMergeRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import createGqClient, { fetchPolicies } from '~/lib/graphql';
|
||||
|
||||
export default createGqClient(
|
||||
{},
|
||||
{
|
||||
fetchPolicy: fetchPolicies.NO_CACHE,
|
||||
},
|
||||
);
|
||||
|
|
@ -1,6 +1,18 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility';
|
||||
import Api from '~/api';
|
||||
import getUserPermissions from '../queries/getUserPermissions.query.graphql';
|
||||
import gqClient from './gql';
|
||||
|
||||
const fetchApiProjectData = projectPath => Api.project(projectPath).then(({ data }) => data);
|
||||
|
||||
const fetchGqlProjectData = projectPath =>
|
||||
gqClient
|
||||
.query({
|
||||
query: getUserPermissions,
|
||||
variables: { projectPath },
|
||||
})
|
||||
.then(({ data }) => data.project);
|
||||
|
||||
export default {
|
||||
getFileData(endpoint) {
|
||||
|
|
@ -47,7 +59,16 @@ export default {
|
|||
.then(({ data }) => data);
|
||||
},
|
||||
getProjectData(namespace, project) {
|
||||
return Api.project(`${namespace}/${project}`);
|
||||
const projectPath = `${namespace}/${project}`;
|
||||
|
||||
return Promise.all([fetchApiProjectData(projectPath), fetchGqlProjectData(projectPath)]).then(
|
||||
([apiProjectData, gqlProjectData]) => ({
|
||||
data: {
|
||||
...apiProjectData,
|
||||
...gqlProjectData,
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
getProjectMergeRequests(projectId, params = {}) {
|
||||
return Api.projectMergeRequests(projectId, params);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,17 @@ import flash from '~/flash';
|
|||
import { __ } from '~/locale';
|
||||
import service from '../../services';
|
||||
import * as types from '../mutation_types';
|
||||
import { activityBarViews } from '../../constants';
|
||||
import { activityBarViews, PERMISSION_READ_MR } from '../../constants';
|
||||
|
||||
export const getMergeRequestsForBranch = ({ commit, state }, { projectId, branchId } = {}) =>
|
||||
service
|
||||
export const getMergeRequestsForBranch = (
|
||||
{ commit, state, getters },
|
||||
{ projectId, branchId } = {},
|
||||
) => {
|
||||
if (!getters.findProjectPermissions(projectId)[PERMISSION_READ_MR]) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return service
|
||||
.getProjectMergeRequests(`${projectId}`, {
|
||||
source_branch: branchId,
|
||||
source_project_id: state.projects[projectId].id,
|
||||
|
|
@ -36,6 +43,7 @@ export const getMergeRequestsForBranch = ({ commit, state }, { projectId, branch
|
|||
);
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
export const getMergeRequestData = (
|
||||
{ commit, dispatch, state },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { getChangesCountForFiles, filePathMatches } from './utils';
|
||||
import { activityBarViews, packageJsonPath } from '../constants';
|
||||
import {
|
||||
activityBarViews,
|
||||
packageJsonPath,
|
||||
PERMISSION_READ_MR,
|
||||
PERMISSION_CREATE_MR,
|
||||
} from '../constants';
|
||||
|
||||
export const activeFile = state => state.openFiles.find(file => file.active) || null;
|
||||
|
||||
|
|
@ -141,5 +146,14 @@ export const getDiffInfo = (state, getters) => path => {
|
|||
};
|
||||
};
|
||||
|
||||
export const findProjectPermissions = (state, getters) => projectId =>
|
||||
getters.findProject(projectId)?.userPermissions || {};
|
||||
|
||||
export const canReadMergeRequests = (state, getters) =>
|
||||
Boolean(getters.findProjectPermissions(state.currentProjectId)[PERMISSION_READ_MR]);
|
||||
|
||||
export const canCreateMergeRequests = (state, getters) =>
|
||||
Boolean(getters.findProjectPermissions(state.currentProjectId)[PERMISSION_CREATE_MR]);
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
|
|||
commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true });
|
||||
}, 5000);
|
||||
|
||||
if (state.shouldCreateMR) {
|
||||
if (getters.shouldCreateMR) {
|
||||
const { currentProject } = rootGetters;
|
||||
const targetBranch = getters.isCreatingNewBranch
|
||||
? rootState.currentBranchId
|
||||
|
|
|
|||
|
|
@ -54,5 +54,11 @@ export const shouldHideNewMrOption = (_state, getters, _rootState, rootGetters)
|
|||
(!rootGetters.hasMergeRequest && rootGetters.isOnDefaultBranch)) &&
|
||||
rootGetters.canPushToBranch;
|
||||
|
||||
export const shouldDisableNewMrOption = (state, getters, rootState, rootGetters) =>
|
||||
!rootGetters.canCreateMergeRequests;
|
||||
|
||||
export const shouldCreateMR = (state, getters) =>
|
||||
state.shouldCreateMR && !getters.shouldDisableNewMrOption;
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ import projectSelect from '~/project_select';
|
|||
import selfMonitor from '~/self_monitor';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (gon.features && gon.features.selfMonitoringProject) {
|
||||
selfMonitor();
|
||||
}
|
||||
selfMonitor();
|
||||
// Initialize expandable settings panels
|
||||
initSettingsPanels();
|
||||
projectSelect();
|
||||
|
|
|
|||
|
|
@ -71,7 +71,12 @@ export default {
|
|||
<template>
|
||||
<div class="tree-content-holder">
|
||||
<div class="table-holder bordered-box">
|
||||
<table :aria-label="tableCaption" class="table tree-table qa-file-tree" aria-live="polite">
|
||||
<table
|
||||
:aria-label="tableCaption"
|
||||
class="table tree-table"
|
||||
aria-live="polite"
|
||||
data-qa-selector="file_tree_table"
|
||||
>
|
||||
<table-header v-once />
|
||||
<tbody>
|
||||
<parent-row
|
||||
|
|
|
|||
|
|
@ -139,7 +139,13 @@ export default {
|
|||
class="d-inline-block align-text-bottom fa-fw"
|
||||
/>
|
||||
<i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
|
||||
<component :is="linkComponent" :to="routerLinkTo" :href="url" class="str-truncated">
|
||||
<component
|
||||
:is="linkComponent"
|
||||
:to="routerLinkTo"
|
||||
:href="url"
|
||||
class="str-truncated"
|
||||
data-qa-selector="file_name_link"
|
||||
>
|
||||
{{ fullPath }}
|
||||
</component>
|
||||
<!-- eslint-disable-next-line @gitlab/vue-i18n/no-bare-strings -->
|
||||
|
|
|
|||
|
|
@ -688,7 +688,7 @@ $ide-commit-header-height: 48px;
|
|||
font-weight: normal;
|
||||
|
||||
&.is-disabled {
|
||||
.ide-radio-label {
|
||||
.ide-option-label {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,16 +11,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
before_action :set_application_setting
|
||||
|
||||
before_action :whitelist_query_limiting, only: [:usage_data]
|
||||
before_action :validate_self_monitoring_feature_flag_enabled, only: [
|
||||
:create_self_monitoring_project,
|
||||
:status_create_self_monitoring_project,
|
||||
:delete_self_monitoring_project,
|
||||
:status_delete_self_monitoring_project
|
||||
]
|
||||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:self_monitoring_project)
|
||||
end
|
||||
|
||||
VALID_SETTING_PANELS = %w(general integrations repository
|
||||
ci_cd reporting metrics_and_profiling
|
||||
|
|
@ -163,10 +153,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def validate_self_monitoring_feature_flag_enabled
|
||||
self_monitoring_project_not_implemented unless Feature.enabled?(:self_monitoring_project)
|
||||
end
|
||||
|
||||
def self_monitoring_data
|
||||
{
|
||||
project_id: @application_setting.self_monitoring_project_id,
|
||||
|
|
@ -174,16 +160,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def self_monitoring_project_not_implemented
|
||||
render(
|
||||
status: :not_implemented,
|
||||
json: {
|
||||
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
|
||||
|
||||
def set_application_setting
|
||||
@application_setting = ApplicationSetting.current_without_cache
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
module Projects
|
||||
class LsifDataService
|
||||
attr_reader :file, :project, :path, :commit_id,
|
||||
:docs, :doc_ranges, :ranges, :def_refs
|
||||
:docs, :doc_ranges, :ranges, :def_refs, :hover_refs
|
||||
|
||||
CACHE_EXPIRE_IN = 1.hour
|
||||
|
||||
|
|
@ -26,7 +26,8 @@ module Projects
|
|||
end_line: line_data.last,
|
||||
start_char: column_data.first,
|
||||
end_char: column_data.last,
|
||||
definition_url: definition_url_for(def_refs[ref_id])
|
||||
definition_url: definition_url_for(def_refs[ref_id]),
|
||||
hover: highlighted_hover(hover_refs[ref_id])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -54,6 +55,7 @@ module Projects
|
|||
@doc_ranges = data['doc_ranges']
|
||||
@ranges = data['ranges']
|
||||
@def_refs = data['def_refs']
|
||||
@hover_refs = data['hover_refs']
|
||||
end
|
||||
|
||||
def doc_id
|
||||
|
|
@ -86,5 +88,16 @@ module Projects
|
|||
|
||||
Gitlab::Routing.url_helpers.project_blob_path(project, definition_ref_path, anchor: line_anchor)
|
||||
end
|
||||
|
||||
def highlighted_hover(hovers)
|
||||
hovers&.map do |hover|
|
||||
# Documentation for a method which is added as comments on top of the method
|
||||
# is stored as a raw string value in LSIF file
|
||||
next { value: hover } unless hover.is_a?(Hash)
|
||||
|
||||
value = Gitlab::Highlight.highlight(nil, hover['value'], language: hover['language'])
|
||||
{ language: hover['language'], value: value }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@
|
|||
.settings-content
|
||||
= render 'performance_bar'
|
||||
|
||||
- if Feature.enabled?(:self_monitoring_project)
|
||||
.js-self-monitoring-settings{ data: self_monitoring_project_data }
|
||||
.js-self-monitoring-settings{ data: self_monitoring_project_data }
|
||||
|
||||
%section.settings.as-usage.no-animate#js-usage-settings{ class: ('expanded' if expanded_by_default?) }
|
||||
.settings-header#usage-statistics
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64)
|
||||
.d-flex.flex-column.flex-wrap.align-items-baseline
|
||||
.d-inline-flex.align-items-baseline
|
||||
%h1.home-panel-title.prepend-top-8.append-bottom-5.qa-project-name
|
||||
%h1.home-panel-title.prepend-top-8.append-bottom-5{ data: { qa_selector: 'project_name_content' } }
|
||||
= @project.name
|
||||
%span.visibility-icon.text-secondary.prepend-left-4.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
|
||||
= visibility_level_icon(@project.visibility_level, fw: false, options: {class: 'icon'})
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
- source = visible_fork_source(@project)
|
||||
- if source
|
||||
#{ s_('ForkedFromProjectPath|Forked from') }
|
||||
= link_to source.full_name, project_path(source)
|
||||
= link_to source.full_name, project_path(source), data: { qa_selector: 'forked_from_link' }
|
||||
- else
|
||||
= s_('ForkedFromProjectPath|Forked from an inaccessible project')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.tree-content-holder.js-tree-content{ data: tree_content_data(@logs_path, @project, @path) }
|
||||
.table-holder.bordered-box
|
||||
%table.table#tree-slider{ class: "table_#{@hex_path} tree-table qa-file-tree" }
|
||||
%table.table#tree-slider{ class: "table_#{@hex_path} tree-table" }
|
||||
%thead
|
||||
%tr
|
||||
%th= s_('ProjectFileTree|Name')
|
||||
|
|
|
|||
|
|
@ -84,17 +84,16 @@
|
|||
|
||||
= render 'projects/find_file_link'
|
||||
|
||||
- if can_create_mr_from_fork
|
||||
- if can_collaborate || current_user&.already_forked?(@project)
|
||||
- if vue_file_list_enabled?
|
||||
#js-tree-web-ide-link.d-inline-block
|
||||
- else
|
||||
= link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
|
||||
= _('Web IDE')
|
||||
- if can_collaborate || current_user&.already_forked?(@project)
|
||||
- if vue_file_list_enabled?
|
||||
#js-tree-web-ide-link.d-inline-block
|
||||
- else
|
||||
= link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do
|
||||
= link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do
|
||||
= _('Web IDE')
|
||||
= render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path)
|
||||
- elsif can_create_mr_from_fork
|
||||
= link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do
|
||||
= _('Web IDE')
|
||||
= render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path)
|
||||
|
||||
- if show_xcode_link?(@project)
|
||||
.project-action-button.project-xcode.inline<
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable Web IDE on projects without Merge Requests
|
||||
merge_request: 24508
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: When a namespace GitLab Subscription expires, disable SSO enforcement
|
||||
merge_request: 21135
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Use closest allowed visibility level on group creation when importing groups
|
||||
using Group Import/Export
|
||||
merge_request: 25026
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Separate entities into own class files
|
||||
merge_request: 24985
|
||||
author: Rajendra Kadam
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Separate Application and Blob entities into own class files
|
||||
merge_request: 24997
|
||||
author: Rajendra Kadam
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove self monitoring feature flag
|
||||
merge_request: 23631
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -72,6 +72,7 @@ projects:
|
|||
effects if the package is included multiple times.
|
||||
- Use `go fmt` before committing ([Gofmt](https://golang.org/cmd/gofmt/) is a
|
||||
tool that automatically formats Go source code).
|
||||
- Place private methods below the first caller method in the source file.
|
||||
|
||||
### Automatic linting
|
||||
|
||||
|
|
|
|||
|
|
@ -199,50 +199,6 @@ module API
|
|||
end.compact
|
||||
end
|
||||
end
|
||||
|
||||
class UserAgentDetail < Grape::Entity
|
||||
expose :user_agent
|
||||
expose :ip_address
|
||||
expose :submitted, as: :akismet_submitted
|
||||
end
|
||||
|
||||
class CustomAttribute < Grape::Entity
|
||||
expose :key
|
||||
expose :value
|
||||
end
|
||||
|
||||
class PagesDomainCertificateExpiration < Grape::Entity
|
||||
expose :expired?, as: :expired
|
||||
expose :expiration
|
||||
end
|
||||
|
||||
class Application < Grape::Entity
|
||||
expose :id
|
||||
expose :uid, as: :application_id
|
||||
expose :name, as: :application_name
|
||||
expose :redirect_uri, as: :callback_url
|
||||
expose :confidential
|
||||
end
|
||||
|
||||
# Use with care, this exposes the secret
|
||||
class ApplicationWithSecret < Application
|
||||
expose :secret
|
||||
end
|
||||
|
||||
class Blob < Grape::Entity
|
||||
expose :basename
|
||||
expose :data
|
||||
expose :path
|
||||
# TODO: :filename was renamed to :path but both still return the full path,
|
||||
# in the future we can only return the filename here without the leading
|
||||
# directory path.
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/34521
|
||||
expose :filename, &:path
|
||||
expose :id
|
||||
expose :ref
|
||||
expose :startline
|
||||
expose :project_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class Application < Grape::Entity
|
||||
expose :id
|
||||
expose :uid, as: :application_id
|
||||
expose :name, as: :application_name
|
||||
expose :redirect_uri, as: :callback_url
|
||||
expose :confidential
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
# Use with care, this exposes the secret
|
||||
class ApplicationWithSecret < Entities::Application
|
||||
expose :secret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class Blob < Grape::Entity
|
||||
expose :basename
|
||||
expose :data
|
||||
expose :path
|
||||
# TODO: :filename was renamed to :path but both still return the full path,
|
||||
# in the future we can only return the filename here without the leading
|
||||
# directory path.
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/34521
|
||||
expose :filename, &:path
|
||||
expose :id
|
||||
expose :ref
|
||||
expose :startline
|
||||
expose :project_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class CustomAttribute < Grape::Entity
|
||||
expose :key
|
||||
expose :value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class PagesDomainCertificateExpiration < Grape::Entity
|
||||
expose :expired?, as: :expired
|
||||
expose :expiration
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class UserAgentDetail < Grape::Entity
|
||||
expose :user_agent
|
||||
expose :ip_address
|
||||
expose :submitted, as: :akismet_submitted
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,15 +5,25 @@ module API
|
|||
MAXIMUM_FILE_SIZE = 50.megabytes.freeze
|
||||
|
||||
helpers do
|
||||
def authorize_create_group!
|
||||
parent_group = find_group!(params[:parent_id]) if params[:parent_id].present?
|
||||
def parent_group
|
||||
find_group!(params[:parent_id]) if params[:parent_id].present?
|
||||
end
|
||||
|
||||
def authorize_create_group!
|
||||
if parent_group
|
||||
authorize! :create_subgroup, parent_group
|
||||
else
|
||||
authorize! :create_group
|
||||
end
|
||||
end
|
||||
|
||||
def closest_allowed_visibility_level
|
||||
if parent_group
|
||||
Gitlab::VisibilityLevel.closest_allowed_level(parent_group.visibility_level)
|
||||
else
|
||||
Gitlab::VisibilityLevel::PRIVATE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
|
|
@ -59,6 +69,7 @@ module API
|
|||
path: params[:path],
|
||||
name: params[:name],
|
||||
parent_id: params[:parent_id],
|
||||
visibility_level: closest_allowed_visibility_level,
|
||||
import_export_upload: ImportExportUpload.new(import_file: uploaded_file)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ excluded_attributes:
|
|||
- :runners_token
|
||||
- :runners_token_encrypted
|
||||
- :saml_discovery_token
|
||||
- :visibility_level
|
||||
|
||||
methods:
|
||||
labels:
|
||||
|
|
|
|||
|
|
@ -74,12 +74,23 @@ module Gitlab
|
|||
group_params = {
|
||||
name: group_hash['name'],
|
||||
path: group_hash['path'],
|
||||
parent_id: parent_group&.id
|
||||
parent_id: parent_group&.id,
|
||||
visibility_level: sub_group_visibility_level(group_hash, parent_group)
|
||||
}
|
||||
|
||||
::Groups::CreateService.new(@user, group_params).execute
|
||||
end
|
||||
|
||||
def sub_group_visibility_level(group_hash, parent_group)
|
||||
original_visibility_level = group_hash['visibility_level'] || Gitlab::VisibilityLevel::PRIVATE
|
||||
|
||||
if parent_group && parent_group.visibility_level < original_visibility_level
|
||||
Gitlab::VisibilityLevel.closest_allowed_level(parent_group.visibility_level)
|
||||
else
|
||||
original_visibility_level
|
||||
end
|
||||
end
|
||||
|
||||
def members_mapper
|
||||
@members_mapper ||= Gitlab::ImportExport::MembersMapper.new(exported_members: @group_members, user: @user, importable: @group)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10130,6 +10130,9 @@ msgstr ""
|
|||
msgid "IDE|Successful commit"
|
||||
msgstr ""
|
||||
|
||||
msgid "IDE|This option is disabled because you are not allowed to create merge requests in this project."
|
||||
msgstr ""
|
||||
|
||||
msgid "IDE|This option is disabled because you don't have write permissions for the current branch."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17112,9 +17115,6 @@ msgstr ""
|
|||
msgid "Self monitoring project does not exist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Self-monitoring is not enabled on this GitLab server, contact your administrator."
|
||||
msgstr ""
|
||||
|
||||
msgid "Self-monitoring project does not exist. Please check logs for any error messages"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,14 @@ module QA
|
|||
include Page::Component::ClonePanel
|
||||
include Page::Project::SubMenus::Settings
|
||||
|
||||
view 'app/assets/javascripts/repository/components/table/row.vue' do
|
||||
element :file_name_link
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/repository/components/table/index.vue' do
|
||||
element :file_tree_table
|
||||
end
|
||||
|
||||
view 'app/views/layouts/header/_new_dropdown.haml' do
|
||||
element :new_menu_toggle
|
||||
element :new_issue_link, "link_to _('New issue'), new_project_issue_path(@project)" # rubocop:disable QA/ElementWithPattern
|
||||
|
|
@ -17,7 +25,8 @@ module QA
|
|||
end
|
||||
|
||||
view 'app/views/projects/_home_panel.html.haml' do
|
||||
element :project_name
|
||||
element :forked_from_link
|
||||
element :project_name_content
|
||||
end
|
||||
|
||||
view 'app/views/projects/_files.html.haml' do
|
||||
|
|
@ -37,10 +46,6 @@ module QA
|
|||
element :quick_actions
|
||||
end
|
||||
|
||||
view 'app/views/projects/tree/_tree_content.html.haml' do
|
||||
element :file_tree
|
||||
end
|
||||
|
||||
view 'app/views/projects/tree/_tree_header.html.haml' do
|
||||
element :add_to_tree
|
||||
element :new_file_option
|
||||
|
|
@ -79,14 +84,18 @@ module QA
|
|||
click_on 'Fork'
|
||||
end
|
||||
|
||||
def forked_from?(parent_project_name)
|
||||
has_element?(:forked_from_link, text: parent_project_name)
|
||||
end
|
||||
|
||||
def click_file(filename)
|
||||
within_element(:file_tree) do
|
||||
within_element(:file_tree_table) do
|
||||
click_on filename
|
||||
end
|
||||
end
|
||||
|
||||
def click_commit(commit_msg)
|
||||
within_element(:file_tree) do
|
||||
within_element(:file_tree_table) do
|
||||
click_on commit_msg
|
||||
end
|
||||
end
|
||||
|
|
@ -96,6 +105,16 @@ module QA
|
|||
click_link 'New issue'
|
||||
end
|
||||
|
||||
def has_file?(name)
|
||||
within_element(:file_tree_table) do
|
||||
has_element?(:file_name_link, text: name)
|
||||
end
|
||||
end
|
||||
|
||||
def has_name?(name)
|
||||
has_element?(:project_name_content, text: name)
|
||||
end
|
||||
|
||||
def last_commit_content
|
||||
find_element(:commit_content).text
|
||||
end
|
||||
|
|
@ -113,7 +132,7 @@ module QA
|
|||
end
|
||||
|
||||
def project_name
|
||||
find('.qa-project-name').text
|
||||
find_element(:project_name_content).text
|
||||
end
|
||||
|
||||
def switch_to_branch(branch_name)
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ module QA
|
|||
module ApiFabricator
|
||||
include Capybara::DSL
|
||||
|
||||
ResourceNotFoundError = Class.new(RuntimeError)
|
||||
ResourceFabricationFailedError = Class.new(RuntimeError)
|
||||
ResourceURLMissingError = Class.new(RuntimeError)
|
||||
ResourceNotDeletedError = Class.new(RuntimeError)
|
||||
ResourceNotFoundError = Class.new(RuntimeError)
|
||||
ResourceQueryError = Class.new(RuntimeError)
|
||||
ResourceUpdateFailedError = Class.new(RuntimeError)
|
||||
ResourceURLMissingError = Class.new(RuntimeError)
|
||||
|
||||
attr_reader :api_resource, :api_response
|
||||
attr_writer :api_client
|
||||
|
|
|
|||
|
|
@ -3,19 +3,24 @@
|
|||
module QA
|
||||
module Resource
|
||||
class Fork < Base
|
||||
attribute :name do
|
||||
upstream.name
|
||||
end
|
||||
|
||||
attribute :project do
|
||||
Resource::Project.fabricate! do |resource|
|
||||
resource.name = upstream.project.name
|
||||
resource.path_with_namespace = "#{user.name}/#{upstream.project.name}"
|
||||
Resource::Project.fabricate_via_api! do |resource|
|
||||
resource.add_name_uuid = false
|
||||
resource.name = name
|
||||
resource.path_with_namespace = "#{user.username}/#{name}"
|
||||
end
|
||||
end
|
||||
|
||||
attribute :upstream do
|
||||
Repository::ProjectPush.fabricate!
|
||||
Repository::ProjectPush.fabricate!.project
|
||||
end
|
||||
|
||||
attribute :user do
|
||||
User.fabricate! do |resource|
|
||||
User.fabricate_via_api! do |resource|
|
||||
if Runtime::Env.forker?
|
||||
resource.username = Runtime::Env.forker_username
|
||||
resource.password = Runtime::Env.forker_password
|
||||
|
|
@ -33,7 +38,7 @@ module QA
|
|||
login.sign_in_using_credentials(user: user)
|
||||
end
|
||||
|
||||
upstream.project.visit!
|
||||
upstream.visit!
|
||||
|
||||
Page::Project::Show.perform(&:fork_project)
|
||||
|
||||
|
|
@ -47,6 +52,41 @@ module QA
|
|||
|
||||
populate(:project)
|
||||
end
|
||||
|
||||
def fabricate_via_api!
|
||||
populate(:upstream, :user)
|
||||
|
||||
Runtime::Logger.debug("Forking project #{upstream.name} to namespace #{user.username}...")
|
||||
super
|
||||
wait_until_forked
|
||||
|
||||
populate(:project)
|
||||
end
|
||||
|
||||
def api_get_path
|
||||
"/projects/#{CGI.escape(path_with_namespace)}"
|
||||
end
|
||||
|
||||
def api_post_path
|
||||
"/projects/#{upstream.id}/fork"
|
||||
end
|
||||
|
||||
def api_post_body
|
||||
{
|
||||
namespace: user.username,
|
||||
name: name,
|
||||
path: name
|
||||
}
|
||||
end
|
||||
|
||||
def wait_until_forked
|
||||
Runtime::Logger.debug("Waiting for the fork process to complete...")
|
||||
forked = wait_until do
|
||||
project.import_status == "finished"
|
||||
end
|
||||
|
||||
raise "Timed out while waiting for the fork process to complete." unless forked
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module QA
|
|||
attr_accessor :fork_branch
|
||||
|
||||
attribute :fork do
|
||||
Fork.fabricate!
|
||||
Fork.fabricate_via_browser_ui!
|
||||
end
|
||||
|
||||
attribute :push do
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ module QA
|
|||
"#{api_get_path}/runners"
|
||||
end
|
||||
|
||||
def api_put_path
|
||||
"/projects/#{id}"
|
||||
end
|
||||
|
||||
def api_post_path
|
||||
'/projects'
|
||||
end
|
||||
|
|
@ -115,6 +119,35 @@ module QA
|
|||
post_body
|
||||
end
|
||||
|
||||
def change_repository_storage(new_storage)
|
||||
put_body = { repository_storage: new_storage }
|
||||
response = put Runtime::API::Request.new(api_client, api_put_path).url, put_body
|
||||
|
||||
unless response.code == HTTP_STATUS_OK
|
||||
raise ResourceUpdateFailedError, "Could not change repository storage to #{new_storage}. Request returned (#{response.code}): `#{response}`."
|
||||
end
|
||||
|
||||
wait_until do
|
||||
reload!
|
||||
|
||||
api_response[:repository_storage] == new_storage
|
||||
end
|
||||
end
|
||||
|
||||
def import_status
|
||||
response = get Runtime::API::Request.new(api_client, "/projects/#{id}/import").url
|
||||
|
||||
unless response.code == HTTP_STATUS_OK
|
||||
raise ResourceQueryError, "Could not get import status. Request returned (#{response.code}): `#{response}`."
|
||||
end
|
||||
|
||||
result = parse_body(response)
|
||||
|
||||
Runtime::Logger.error("Import failed: #{result[:import_error]}") if result[:import_status] == "failed"
|
||||
|
||||
result[:import_status]
|
||||
end
|
||||
|
||||
def runners(tag_list: nil)
|
||||
response = get Runtime::API::Request.new(api_client, "#{api_runners_path}?tag_list=#{tag_list.compact.join(',')}").url
|
||||
parse_body(response)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ module QA
|
|||
SUPPORTED_FEATURES
|
||||
end
|
||||
|
||||
def additional_repository_storage
|
||||
ENV['QA_ADDITIONAL_REPOSITORY_STORAGE']
|
||||
end
|
||||
|
||||
def admin_password
|
||||
ENV['GITLAB_ADMIN_PASSWORD']
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module QA
|
|||
it 'user forks a project, submits a merge request and maintainer merges it' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
merge_request = Resource::MergeRequestFromFork.fabricate! do |merge_request|
|
||||
merge_request = Resource::MergeRequestFromFork.fabricate_via_browser_ui! do |merge_request|
|
||||
merge_request.fork_branch = 'feature-branch'
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context 'Create' do
|
||||
describe 'Gitaly repository storage', :orchestrated, :repository_storage, :requires_admin, quarantine: { type: :new } do
|
||||
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
|
||||
let(:parent_project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.name = 'parent-project'
|
||||
project.initialize_with_readme = true
|
||||
end
|
||||
end
|
||||
let(:fork_project) do
|
||||
Resource::Fork.fabricate_via_api! do |fork|
|
||||
fork.user = user
|
||||
fork.upstream = parent_project
|
||||
end.project
|
||||
end
|
||||
|
||||
before do
|
||||
parent_project.add_member(user)
|
||||
end
|
||||
|
||||
it 'creates a 2nd fork after moving the parent project' do
|
||||
Flow::Login.sign_in(as: user)
|
||||
|
||||
fork_project.visit!
|
||||
|
||||
parent_project.change_repository_storage(QA::Runtime::Env.additional_repository_storage)
|
||||
|
||||
second_fork_project = Resource::Fork.fabricate_via_api! do |fork|
|
||||
fork.name = "second-fork"
|
||||
fork.user = user
|
||||
fork.upstream = parent_project
|
||||
end.project
|
||||
|
||||
Resource::Repository::ProjectPush.fabricate! do |push|
|
||||
push.project = second_fork_project
|
||||
push.file_name = 'new_file'
|
||||
push.file_content = '# This is a new file'
|
||||
push.commit_message = 'Add new file'
|
||||
push.new_branch = false
|
||||
end.project.visit!
|
||||
|
||||
Page::Project::Show.perform do |show|
|
||||
expect(show).to have_file('new_file')
|
||||
expect(show).to have_name(second_fork_project.name)
|
||||
expect(show).to be_forked_from(parent_project.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -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)
|
||||
|
||||
# The navigation bar
|
||||
page.within('.header-new') do
|
||||
find('.qa-new-menu-toggle').click
|
||||
|
||||
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
|
||||
|
||||
find('.qa-new-menu-toggle').click
|
||||
context 'with developer user' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
# The dropdown above the tree
|
||||
page.within('.repo-breadcrumb') do
|
||||
find('.qa-add-to-tree').click
|
||||
it 'shows all the expected links' do
|
||||
visit project_path(project)
|
||||
|
||||
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 navigation bar
|
||||
page.within('.header-new') do
|
||||
find('.qa-new-menu-toggle').click
|
||||
|
||||
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
|
||||
|
||||
find('.qa-new-menu-toggle').click
|
||||
end
|
||||
|
||||
# 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')
|
||||
it 'hides the links when the project is archived' do
|
||||
project.update!(archived: true)
|
||||
|
||||
visit project_path(project)
|
||||
|
||||
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
|
||||
|
||||
find('.qa-new-menu-toggle').click
|
||||
end
|
||||
|
||||
expect(page).not_to have_selector('.qa-add-to-tree')
|
||||
|
||||
expect(page).not_to have_link('Web IDE')
|
||||
end
|
||||
end
|
||||
|
||||
it 'hides the links when the project is archived' do
|
||||
project.update!(archived: true)
|
||||
|
||||
visit project_path(project)
|
||||
|
||||
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
|
||||
|
||||
find('.qa-new-menu-toggle').click
|
||||
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_selector('.qa-add-to-tree')
|
||||
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
|
||||
|
||||
expect(page).not_to have_link('Web IDE')
|
||||
it "updates Web IDE link" do
|
||||
expect(page.has_link?('Web IDE')).to be(expect_ide_link)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
166
spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json
vendored
Normal file
166
spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
166
spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json
vendored
Normal file
166
spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
166
spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json
vendored
Normal file
166
spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
|
@ -18,6 +18,7 @@ export const projectData = {
|
|||
},
|
||||
mergeRequests: {},
|
||||
merge_requests_enabled: true,
|
||||
userPermissions: {},
|
||||
default_branch: 'master',
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ exports[`Repository table row component renders table row 1`] = `
|
|||
|
||||
<a
|
||||
class="str-truncated"
|
||||
data-qa-selector="file_name_link"
|
||||
href="https://test.com"
|
||||
>
|
||||
|
||||
|
|
@ -64,6 +65,7 @@ exports[`Repository table row component renders table row for path with special
|
|||
|
||||
<a
|
||||
class="str-truncated"
|
||||
data-qa-selector="file_name_link"
|
||||
href="https://test.com"
|
||||
>
|
||||
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
const findIcon = name => vm.$el.querySelector(`.ic-${name}`);
|
||||
const findMRIcon = () => findIcon('merge-request');
|
||||
const findBranchIcon = () => findIcon('branch');
|
||||
|
||||
describe('normal', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders empty placeholders, if state is falsey', () => {
|
||||
expect(trimText(vm.$el.textContent)).toEqual('- -');
|
||||
});
|
||||
|
||||
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_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 name, if state has currentBranchId', done => {
|
||||
vm.$store.state.currentBranchId = TEST_BRANCH_ID;
|
||||
describe('with showMergeRequests false', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ showMergeRequests: false });
|
||||
});
|
||||
|
||||
vm.$nextTick()
|
||||
.then(() => {
|
||||
expect(trimText(vm.$el.textContent)).toEqual(`${TEST_BRANCH_ID} -`);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
it('shows single empty placeholder, 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;
|
||||
|
||||
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 only branch icon', () => {
|
||||
expect(findBranchIcon()).toBeTruthy();
|
||||
expect(findMRIcon()).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue