Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5820d448c1
commit
85ea3dd4f4
|
@ -1,10 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlTable, GlButton, GlBadge, GlTooltipDirective } from '@gitlab/ui';
|
import { GlTable, GlButton, GlBadge, GlTooltipDirective, GlAvatarLink, GlAvatar } from '@gitlab/ui';
|
||||||
import { s__ } from '~/locale';
|
import { s__ } from '~/locale';
|
||||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||||
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
|
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
|
||||||
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
i18n: {
|
i18n: {
|
||||||
|
@ -21,7 +20,8 @@ export default {
|
||||||
GlBadge,
|
GlBadge,
|
||||||
ClipboardButton,
|
ClipboardButton,
|
||||||
TooltipOnTruncate,
|
TooltipOnTruncate,
|
||||||
UserAvatarLink,
|
GlAvatarLink,
|
||||||
|
GlAvatar,
|
||||||
TimeAgoTooltip,
|
TimeAgoTooltip,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
|
@ -102,13 +102,14 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
<template #cell(owner)="{ item }">
|
<template #cell(owner)="{ item }">
|
||||||
<span class="trigger-owner sr-only">{{ item.owner.name }}</span>
|
<span class="trigger-owner sr-only">{{ item.owner.name }}</span>
|
||||||
<user-avatar-link
|
<gl-avatar-link
|
||||||
v-if="item.owner"
|
v-if="item.owner"
|
||||||
:link-href="item.owner.path"
|
v-gl-tooltip
|
||||||
:img-src="item.owner.avatarUrl"
|
:href="item.owner.path"
|
||||||
:tooltip-text="item.owner.name"
|
:title="item.owner.name"
|
||||||
:img-alt="item.owner.name"
|
>
|
||||||
/>
|
<gl-avatar :size="24" :src="item.owner.avatarUrl" />
|
||||||
|
</gl-avatar-link>
|
||||||
</template>
|
</template>
|
||||||
<template #cell(lastUsed)="{ item }">
|
<template #cell(lastUsed)="{ item }">
|
||||||
<time-ago-tooltip v-if="item.lastUsed" :time="item.lastUsed" />
|
<time-ago-tooltip v-if="item.lastUsed" :time="item.lastUsed" />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlIcon } from '@gitlab/ui';
|
import { GlIcon } from '@gitlab/ui';
|
||||||
import CiIcon from '../../../../vue_shared/components/ci_icon.vue';
|
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -4,6 +4,8 @@ import $ from 'jquery';
|
||||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||||
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
|
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
|
||||||
import createFlash from '~/flash';
|
import createFlash from '~/flash';
|
||||||
|
import { isPositiveInteger } from '~/lib/utils/number_utils';
|
||||||
|
import { getParameterByName, setUrlParams, updateHistory } from '~/lib/utils/url_utility';
|
||||||
import { __, s__, sprintf } from '~/locale';
|
import { __, s__, sprintf } from '~/locale';
|
||||||
import TaskList from '~/task_list';
|
import TaskList from '~/task_list';
|
||||||
import Tracking from '~/tracking';
|
import Tracking from '~/tracking';
|
||||||
|
@ -65,13 +67,17 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const workItemId = getParameterByName('work_item_id');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
preAnimation: false,
|
preAnimation: false,
|
||||||
pulseAnimation: false,
|
pulseAnimation: false,
|
||||||
initialUpdate: true,
|
initialUpdate: true,
|
||||||
taskButtons: [],
|
taskButtons: [],
|
||||||
activeTask: {},
|
activeTask: {},
|
||||||
workItemId: null,
|
workItemId: isPositiveInteger(workItemId)
|
||||||
|
? convertToGraphQLId(TYPE_WORK_ITEM, workItemId)
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -184,6 +190,7 @@ export default {
|
||||||
taskLink.addEventListener('click', (e) => {
|
taskLink.addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.workItemId = convertToGraphQLId(TYPE_WORK_ITEM, issue);
|
this.workItemId = convertToGraphQLId(TYPE_WORK_ITEM, issue);
|
||||||
|
this.updateWorkItemIdUrlQuery(issue);
|
||||||
this.track('viewed_work_item_from_modal', {
|
this.track('viewed_work_item_from_modal', {
|
||||||
category: 'workItems:show',
|
category: 'workItems:show',
|
||||||
label: 'work_item_view',
|
label: 'work_item_view',
|
||||||
|
@ -232,12 +239,19 @@ export default {
|
||||||
this.$refs.modal.hide();
|
this.$refs.modal.hide();
|
||||||
},
|
},
|
||||||
closeWorkItemDetailModal() {
|
closeWorkItemDetailModal() {
|
||||||
this.workItemId = null;
|
this.workItemId = undefined;
|
||||||
|
this.updateWorkItemIdUrlQuery(undefined);
|
||||||
},
|
},
|
||||||
handleCreateTask(description) {
|
handleCreateTask(description) {
|
||||||
this.$emit('updateDescription', description);
|
this.$emit('updateDescription', description);
|
||||||
this.closeCreateTaskModal();
|
this.closeCreateTaskModal();
|
||||||
},
|
},
|
||||||
|
updateWorkItemIdUrlQuery(workItemId) {
|
||||||
|
updateHistory({
|
||||||
|
url: setUrlParams({ work_item_id: workItemId }),
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
safeHtmlConfig: { ADD_TAGS: ['gl-emoji', 'copy-code'] },
|
safeHtmlConfig: { ADD_TAGS: ['gl-emoji', 'copy-code'] },
|
||||||
};
|
};
|
||||||
|
@ -281,7 +295,7 @@ export default {
|
||||||
body-class="gl-p-0!"
|
body-class="gl-p-0!"
|
||||||
>
|
>
|
||||||
<create-work-item
|
<create-work-item
|
||||||
:is-modal="true"
|
is-modal
|
||||||
:initial-title="activeTask.title"
|
:initial-title="activeTask.title"
|
||||||
:issue-gid="issueGid"
|
:issue-gid="issueGid"
|
||||||
:lock-version="lockVersion"
|
:lock-version="lockVersion"
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default {
|
||||||
:link-href="author.path"
|
:link-href="author.path"
|
||||||
:img-alt="author.name"
|
:img-alt="author.name"
|
||||||
:img-src="author.avatar_url"
|
:img-src="author.avatar_url"
|
||||||
:img-size="26"
|
:img-size="24"
|
||||||
:tooltip-text="author.name"
|
:tooltip-text="author.name"
|
||||||
tooltip-placement="bottom"
|
tooltip-placement="bottom"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import initFilePickers from '~/file_pickers';
|
import initFilePickers from '~/file_pickers';
|
||||||
import BindInOut from '../../../../behaviors/bind_in_out';
|
import BindInOut from '~/behaviors/bind_in_out';
|
||||||
import Group from '../../../../group';
|
import Group from '~/group';
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
BindInOut.initAll();
|
BindInOut.initAll();
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
import { GlButton } from '@gitlab/ui';
|
import { GlButton } from '@gitlab/ui';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils';
|
import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils';
|
||||||
|
import Translate from '~/vue_shared/translate';
|
||||||
import Translate from '../../../../../vue_shared/translate';
|
|
||||||
|
|
||||||
Vue.use(Translate);
|
Vue.use(Translate);
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ import Vue from 'vue';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import RefSelector from '~/ref/components/ref_selector.vue';
|
import RefSelector from '~/ref/components/ref_selector.vue';
|
||||||
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
|
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
|
||||||
import setupNativeFormVariableList from '../../../../ci_variable_list/native_form_variable_list';
|
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
|
||||||
import GlFieldErrors from '../../../../gl_field_errors';
|
import GlFieldErrors from '~/gl_field_errors';
|
||||||
import Translate from '../../../../vue_shared/translate';
|
import Translate from '~/vue_shared/translate';
|
||||||
import intervalPatternInput from './components/interval_pattern_input.vue';
|
import intervalPatternInput from './components/interval_pattern_input.vue';
|
||||||
import TimezoneDropdown from './components/timezone_dropdown';
|
import TimezoneDropdown from './components/timezone_dropdown';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import GLForm from '../../../../gl_form';
|
import GLForm from '~/gl_form';
|
||||||
import RefSelectDropdown from '../../../../ref_select_dropdown';
|
import RefSelectDropdown from '~/ref_select_dropdown';
|
||||||
import ZenMode from '../../../../zen_mode';
|
import ZenMode from '~/zen_mode';
|
||||||
|
|
||||||
new ZenMode(); // eslint-disable-line no-new
|
new ZenMode(); // eslint-disable-line no-new
|
||||||
new GLForm($('.tag-form')); // eslint-disable-line no-new
|
new GLForm($('.tag-form')); // eslint-disable-line no-new
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import initTree from 'ee_else_ce/repository';
|
import initTree from 'ee_else_ce/repository';
|
||||||
import initBlob from '~/blob_edit/blob_bundle';
|
import initBlob from '~/blob_edit/blob_bundle';
|
||||||
import ShortcutsNavigation from '../../../../behaviors/shortcuts/shortcuts_navigation';
|
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
|
||||||
import NewCommitForm from '../../../../new_commit_form';
|
import NewCommitForm from '~/new_commit_form';
|
||||||
|
|
||||||
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
|
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
|
||||||
initBlob();
|
initBlob();
|
||||||
|
|
|
@ -26,7 +26,12 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { runnerInstallHelpPage, registrationToken } = el.dataset;
|
const {
|
||||||
|
runnerInstallHelpPage,
|
||||||
|
registrationToken,
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
|
} = el.dataset;
|
||||||
|
|
||||||
const { cacheConfig, typeDefs, localMutations } = createLocalState();
|
const { cacheConfig, typeDefs, localMutations } = createLocalState();
|
||||||
|
|
||||||
|
@ -40,6 +45,8 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
|
||||||
provide: {
|
provide: {
|
||||||
runnerInstallHelpPage,
|
runnerInstallHelpPage,
|
||||||
localMutations,
|
localMutations,
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
},
|
},
|
||||||
render(h) {
|
render(h) {
|
||||||
return h(AdminRunnersApp, {
|
return h(AdminRunnersApp, {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||||
import checkedRunnerIdsQuery from '../graphql/list/checked_runner_ids.query.graphql';
|
import checkedRunnerIdsQuery from '../graphql/list/checked_runner_ids.query.graphql';
|
||||||
import { formatJobCount, tableField } from '../utils';
|
import { formatJobCount, tableField } from '../utils';
|
||||||
import RunnerSummaryCell from './cells/runner_summary_cell.vue';
|
import RunnerSummaryCell from './cells/runner_summary_cell.vue';
|
||||||
|
import RunnerStatusPopover from './runner_status_popover.vue';
|
||||||
import RunnerStatusCell from './cells/runner_status_cell.vue';
|
import RunnerStatusCell from './cells/runner_status_cell.vue';
|
||||||
import RunnerTags from './runner_tags.vue';
|
import RunnerTags from './runner_tags.vue';
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ export default {
|
||||||
GlSkeletonLoader,
|
GlSkeletonLoader,
|
||||||
TooltipOnTruncate,
|
TooltipOnTruncate,
|
||||||
TimeAgo,
|
TimeAgo,
|
||||||
|
RunnerStatusPopover,
|
||||||
RunnerSummaryCell,
|
RunnerSummaryCell,
|
||||||
RunnerTags,
|
RunnerTags,
|
||||||
RunnerStatusCell,
|
RunnerStatusCell,
|
||||||
|
@ -136,6 +138,11 @@ export default {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #head(status)="{ label }">
|
||||||
|
{{ label }}
|
||||||
|
<runner-status-popover />
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #cell(status)="{ item }">
|
<template #cell(status)="{ item }">
|
||||||
<runner-status-cell :runner="item" />
|
<runner-status-cell :runner="item" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<script>
|
||||||
|
import { GlSprintf } from '@gitlab/ui';
|
||||||
|
import { duration } from '~/lib/utils/datetime/timeago_utility';
|
||||||
|
import HelpPopover from '~/vue_shared/components/help_popover.vue';
|
||||||
|
import {
|
||||||
|
I18N_STATUS_POPOVER_TITLE,
|
||||||
|
I18N_STATUS_POPOVER_NEVER_CONTACTED,
|
||||||
|
I18N_STATUS_POPOVER_NEVER_CONTACTED_DESCRIPTION,
|
||||||
|
I18N_STATUS_POPOVER_ONLINE,
|
||||||
|
I18N_STATUS_POPOVER_ONLINE_DESCRIPTION,
|
||||||
|
I18N_STATUS_POPOVER_OFFLINE,
|
||||||
|
I18N_STATUS_POPOVER_OFFLINE_DESCRIPTION,
|
||||||
|
I18N_STATUS_POPOVER_STALE,
|
||||||
|
I18N_STATUS_POPOVER_STALE_DESCRIPTION,
|
||||||
|
} from '~/runner/constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RunnerStatusPopover',
|
||||||
|
components: {
|
||||||
|
GlSprintf,
|
||||||
|
HelpPopover,
|
||||||
|
},
|
||||||
|
inject: ['onlineContactTimeoutSecs', 'staleTimeoutSecs'],
|
||||||
|
computed: {
|
||||||
|
onlineContactTimeoutDuration() {
|
||||||
|
return duration(this.onlineContactTimeoutSecs * 1000);
|
||||||
|
},
|
||||||
|
staleTimeoutDuration() {
|
||||||
|
return duration(this.staleTimeoutSecs * 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
I18N_STATUS_POPOVER_TITLE,
|
||||||
|
I18N_STATUS_POPOVER_NEVER_CONTACTED,
|
||||||
|
I18N_STATUS_POPOVER_NEVER_CONTACTED_DESCRIPTION,
|
||||||
|
I18N_STATUS_POPOVER_ONLINE,
|
||||||
|
I18N_STATUS_POPOVER_ONLINE_DESCRIPTION,
|
||||||
|
I18N_STATUS_POPOVER_OFFLINE,
|
||||||
|
I18N_STATUS_POPOVER_OFFLINE_DESCRIPTION,
|
||||||
|
I18N_STATUS_POPOVER_STALE,
|
||||||
|
I18N_STATUS_POPOVER_STALE_DESCRIPTION,
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<help-popover>
|
||||||
|
<template #title>{{ $options.I18N_STATUS_POPOVER_TITLE }}</template>
|
||||||
|
|
||||||
|
<p class="gl-mb-0">
|
||||||
|
<strong>{{ $options.I18N_STATUS_POPOVER_NEVER_CONTACTED }}</strong>
|
||||||
|
<gl-sprintf :message="$options.I18N_STATUS_POPOVER_NEVER_CONTACTED_DESCRIPTION">
|
||||||
|
<template #code="{ content }">
|
||||||
|
<code>{{ content }}</code>
|
||||||
|
</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</p>
|
||||||
|
<p class="gl-mb-0">
|
||||||
|
<strong>{{ $options.I18N_STATUS_POPOVER_ONLINE }}</strong>
|
||||||
|
<gl-sprintf :message="$options.I18N_STATUS_POPOVER_ONLINE_DESCRIPTION">
|
||||||
|
<template #elapsedTime>{{ onlineContactTimeoutDuration }}</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</p>
|
||||||
|
<p class="gl-mb-0">
|
||||||
|
<strong>{{ $options.I18N_STATUS_POPOVER_OFFLINE }}</strong>
|
||||||
|
<gl-sprintf :message="$options.I18N_STATUS_POPOVER_OFFLINE_DESCRIPTION">
|
||||||
|
<template #elapsedTime>{{ onlineContactTimeoutDuration }}</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</p>
|
||||||
|
<p class="gl-mb-0">
|
||||||
|
<strong>{{ $options.I18N_STATUS_POPOVER_STALE }}</strong>
|
||||||
|
<gl-sprintf :message="$options.I18N_STATUS_POPOVER_STALE_DESCRIPTION">
|
||||||
|
<template #elapsedTime>{{ staleTimeoutDuration }}</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</p>
|
||||||
|
</help-popover>
|
||||||
|
</template>
|
|
@ -21,6 +21,26 @@ export const I18N_GROUP_RUNNER_DESCRIPTION = s__(
|
||||||
);
|
);
|
||||||
export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects');
|
export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects');
|
||||||
|
|
||||||
|
// Status help popover
|
||||||
|
export const I18N_STATUS_POPOVER_TITLE = s__('Runners|Runner statuses');
|
||||||
|
|
||||||
|
export const I18N_STATUS_POPOVER_NEVER_CONTACTED = s__('Runners|Never contacted:');
|
||||||
|
export const I18N_STATUS_POPOVER_NEVER_CONTACTED_DESCRIPTION = s__(
|
||||||
|
'Runners|Runner has never contacted GitLab (when you register a runner, use %{codeStart}gitlab-runner run%{codeEnd} to bring it online)',
|
||||||
|
);
|
||||||
|
export const I18N_STATUS_POPOVER_ONLINE = s__('Runners|Online:');
|
||||||
|
export const I18N_STATUS_POPOVER_ONLINE_DESCRIPTION = s__(
|
||||||
|
'Runners|Runner has contacted GitLab within the last %{elapsedTime}',
|
||||||
|
);
|
||||||
|
export const I18N_STATUS_POPOVER_OFFLINE = s__('Runners|Offline:');
|
||||||
|
export const I18N_STATUS_POPOVER_OFFLINE_DESCRIPTION = s__(
|
||||||
|
'Runners|Runner has not contacted GitLab in more than %{elapsedTime}',
|
||||||
|
);
|
||||||
|
export const I18N_STATUS_POPOVER_STALE = s__('Runners|Stale:');
|
||||||
|
export const I18N_STATUS_POPOVER_STALE_DESCRIPTION = s__(
|
||||||
|
'Runners|Runner has not contacted GitLab in more than %{elapsedTime}',
|
||||||
|
);
|
||||||
|
|
||||||
// Status tooltips
|
// Status tooltips
|
||||||
export const I18N_ONLINE_TIMEAGO_TOOLTIP = s__(
|
export const I18N_ONLINE_TIMEAGO_TOOLTIP = s__(
|
||||||
'Runners|Runner is online; last contact was %{timeAgo}',
|
'Runners|Runner is online; last contact was %{timeAgo}',
|
||||||
|
@ -63,7 +83,7 @@ export const I18N_LOCKED_RUNNER_DESCRIPTION = s__(
|
||||||
|
|
||||||
export const I18N_ASSIGNED_PROJECTS = s__('Runners|Assigned Projects (%{projectCount})');
|
export const I18N_ASSIGNED_PROJECTS = s__('Runners|Assigned Projects (%{projectCount})');
|
||||||
export const I18N_NONE = __('None');
|
export const I18N_NONE = __('None');
|
||||||
export const I18N_NO_JOBS_FOUND = s__('Runner|This runner has not run any jobs.');
|
export const I18N_NO_JOBS_FOUND = s__('Runners|This runner has not run any jobs.');
|
||||||
|
|
||||||
// Styles
|
// Styles
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ export const initGroupRunners = (selector = '#js-group-runners') => {
|
||||||
groupId,
|
groupId,
|
||||||
groupFullPath,
|
groupFullPath,
|
||||||
groupRunnersLimitedCount,
|
groupRunnersLimitedCount,
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
} = el.dataset;
|
} = el.dataset;
|
||||||
|
|
||||||
const apolloProvider = new VueApollo({
|
const apolloProvider = new VueApollo({
|
||||||
|
@ -32,6 +34,8 @@ export const initGroupRunners = (selector = '#js-group-runners') => {
|
||||||
provide: {
|
provide: {
|
||||||
runnerInstallHelpPage,
|
runnerInstallHelpPage,
|
||||||
groupId,
|
groupId,
|
||||||
|
onlineContactTimeoutSecs: parseInt(onlineContactTimeoutSecs, 10),
|
||||||
|
staleTimeoutSecs: parseInt(staleTimeoutSecs, 10),
|
||||||
},
|
},
|
||||||
render(h) {
|
render(h) {
|
||||||
return h(GroupRunnersApp, {
|
return h(GroupRunnersApp, {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlIcon } from '@gitlab/ui';
|
import { GlIcon } from '@gitlab/ui';
|
||||||
import { numberToHumanSize } from '../../../../lib/utils/number_utils';
|
import { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -26,11 +26,15 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.gl-mb-5 {
|
||||||
.md {
|
@include gl-mb-5;
|
||||||
padding: 1rem 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
.gl-mt-5 {
|
||||||
|
@include gl-mt-5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
hr {
|
hr {
|
||||||
border: 1px solid #e1e1e1;
|
border: 1px solid #e1e1e1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Diffs
|
||||||
|
class BaseComponent < ViewComponent::Base
|
||||||
|
# To make converting the partials to components easier,
|
||||||
|
# we delegate all missing methods to the helpers,
|
||||||
|
# where they probably are.
|
||||||
|
delegate_missing_to :helpers
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
.js-diff-stats-dropdown{ data: { changed: @changed, added: @added, deleted: @removed, files: diff_files_data } }
|
|
@ -0,0 +1,67 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Diffs
|
||||||
|
class StatsComponent < BaseComponent
|
||||||
|
attr_reader :diff_files
|
||||||
|
|
||||||
|
def initialize(diff_files:)
|
||||||
|
@diff_files = diff_files
|
||||||
|
@changed ||= diff_files.size
|
||||||
|
@added ||= diff_files.sum(&:added_lines)
|
||||||
|
@removed ||= diff_files.sum(&:removed_lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
def diff_files_data
|
||||||
|
diffs_map = @diff_files.map do |f|
|
||||||
|
{
|
||||||
|
href: "##{helpers.hexdigest(f.file_path)}",
|
||||||
|
title: f.new_path,
|
||||||
|
name: f.file_path,
|
||||||
|
path: diff_file_path_text(f),
|
||||||
|
icon: diff_file_changed_icon(f),
|
||||||
|
iconColor: "#{diff_file_changed_icon_color(f)}",
|
||||||
|
added: f.added_lines,
|
||||||
|
removed: f.removed_lines
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
Gitlab::Json.dump(diffs_map)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Disabled undercoverage reports for this method
|
||||||
|
# as it returns a false positive on the last line,
|
||||||
|
# which is covered in the tests
|
||||||
|
#
|
||||||
|
# Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/357381
|
||||||
|
#
|
||||||
|
# :nocov:
|
||||||
|
def diff_file_path_text(diff_file, max: 60)
|
||||||
|
path = diff_file.new_path
|
||||||
|
|
||||||
|
return path unless path.size > max && max > 3
|
||||||
|
|
||||||
|
"...#{path[-(max - 3)..]}"
|
||||||
|
end
|
||||||
|
# :nocov:
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def diff_file_changed_icon(diff_file)
|
||||||
|
if diff_file.deleted_file?
|
||||||
|
"file-deletion"
|
||||||
|
elsif diff_file.new_file?
|
||||||
|
"file-addition"
|
||||||
|
else
|
||||||
|
"file-modified"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def diff_file_changed_icon_color(diff_file)
|
||||||
|
if diff_file.deleted_file?
|
||||||
|
"danger"
|
||||||
|
elsif diff_file.new_file?
|
||||||
|
"success"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
.gl-alert{ role: 'alert', class: ["gl-alert-#{@variant}", @alert_class], data: @alert_data }
|
||||||
|
= sprite_icon(icon, css_class: icon_classes)
|
||||||
|
- if @dismissible
|
||||||
|
%button.btn.gl-dismiss-btn.btn-default.btn-sm.gl-button.btn-default-tertiary.btn-icon.js-close{ type: 'button',
|
||||||
|
aria: { label: _('Dismiss') },
|
||||||
|
class: @close_button_class,
|
||||||
|
data: @close_button_data }
|
||||||
|
= sprite_icon('close')
|
||||||
|
.gl-alert-content{ role: 'alert' }
|
||||||
|
- if @title
|
||||||
|
%h4.gl-alert-title
|
||||||
|
= @title
|
||||||
|
= content
|
|
@ -0,0 +1,45 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Renders a GlAlert root element
|
||||||
|
module Pajamas
|
||||||
|
class AlertComponent < Pajamas::Component
|
||||||
|
# @param [String] title
|
||||||
|
# @param [Symbol] variant
|
||||||
|
# @param [Boolean] dismissible
|
||||||
|
# @param [String] alert_class
|
||||||
|
# @param [Hash] alert_data
|
||||||
|
# @param [String] close_button_class
|
||||||
|
# @param [Hash] close_button_data
|
||||||
|
def initialize(
|
||||||
|
title: nil, variant: :info, dismissible: true,
|
||||||
|
alert_class: nil, alert_data: {}, close_button_class: nil, close_button_data: {})
|
||||||
|
@title = title
|
||||||
|
@variant = variant
|
||||||
|
@dismissible = dismissible
|
||||||
|
@alert_class = alert_class
|
||||||
|
@alert_data = alert_data
|
||||||
|
@close_button_class = close_button_class
|
||||||
|
@close_button_data = close_button_data
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
delegate :sprite_icon, to: :helpers
|
||||||
|
|
||||||
|
ICONS = {
|
||||||
|
info: 'information-o',
|
||||||
|
warning: 'warning',
|
||||||
|
success: 'check-circle',
|
||||||
|
danger: 'error',
|
||||||
|
tip: 'bulb'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
def icon
|
||||||
|
ICONS[@variant]
|
||||||
|
end
|
||||||
|
|
||||||
|
def icon_classes
|
||||||
|
"gl-alert-icon#{' gl-alert-icon-no-title' if @title.nil?}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,7 +9,6 @@ module Projects
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@package = project.packages.find(params[:id])
|
@package = project.packages.find(params[:id])
|
||||||
@package_files = @package.installable_package_files.recent
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -63,7 +63,9 @@ module Ci
|
||||||
# Runner install help page is external, located at
|
# Runner install help page is external, located at
|
||||||
# https://gitlab.com/gitlab-org/gitlab-runner
|
# https://gitlab.com/gitlab-org/gitlab-runner
|
||||||
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
|
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
|
||||||
registration_token: Gitlab::CurrentSettings.runners_registration_token
|
registration_token: Gitlab::CurrentSettings.runners_registration_token,
|
||||||
|
online_contact_timeout_secs: ::Ci::Runner::ONLINE_CONTACT_TIMEOUT.to_i,
|
||||||
|
stale_timeout_secs: ::Ci::Runner::STALE_TIMEOUT.to_i
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -83,7 +85,9 @@ module Ci
|
||||||
registration_token: group.runners_token,
|
registration_token: group.runners_token,
|
||||||
group_id: group.id,
|
group_id: group.id,
|
||||||
group_full_path: group.full_path,
|
group_full_path: group.full_path,
|
||||||
runner_install_help_page: 'https://docs.gitlab.com/runner/install/'
|
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
|
||||||
|
online_contact_timeout_secs: ::Ci::Runner::ONLINE_CONTACT_TIMEOUT.to_i,
|
||||||
|
stale_timeout_secs: ::Ci::Runner::STALE_TIMEOUT.to_i
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -195,24 +195,6 @@ module DiffHelper
|
||||||
!diff_file.deleted_file? && @merge_request && @merge_request.source_project
|
!diff_file.deleted_file? && @merge_request && @merge_request.source_project
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff_file_changed_icon(diff_file)
|
|
||||||
if diff_file.deleted_file?
|
|
||||||
"file-deletion"
|
|
||||||
elsif diff_file.new_file?
|
|
||||||
"file-addition"
|
|
||||||
else
|
|
||||||
"file-modified"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def diff_file_changed_icon_color(diff_file)
|
|
||||||
if diff_file.deleted_file?
|
|
||||||
"danger"
|
|
||||||
elsif diff_file.new_file?
|
|
||||||
"success"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_overflow_warning?(diffs_collection)
|
def render_overflow_warning?(diffs_collection)
|
||||||
diffs_collection.overflow?.tap do |overflown|
|
diffs_collection.overflow?.tap do |overflown|
|
||||||
log_overflow_limits(diff_files: diffs_collection.raw_diff_files, collection_overflow: overflown)
|
log_overflow_limits(diff_files: diffs_collection.raw_diff_files, collection_overflow: overflown)
|
||||||
|
@ -272,23 +254,6 @@ module DiffHelper
|
||||||
toggle_whitespace_link(url, options)
|
toggle_whitespace_link(url, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff_files_data(diff_files)
|
|
||||||
diffs_map = diff_files.map do |f|
|
|
||||||
{
|
|
||||||
href: "##{hexdigest(f.file_path)}",
|
|
||||||
title: f.new_path,
|
|
||||||
name: f.file_path,
|
|
||||||
path: diff_file_path_text(f),
|
|
||||||
icon: diff_file_changed_icon(f),
|
|
||||||
iconColor: "#{diff_file_changed_icon_color(f)}",
|
|
||||||
added: f.added_lines,
|
|
||||||
removed: f.removed_lines
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
diffs_map.to_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def hide_whitespace?
|
def hide_whitespace?
|
||||||
params[:w] == '1'
|
params[:w] == '1'
|
||||||
end
|
end
|
||||||
|
@ -302,14 +267,6 @@ module DiffHelper
|
||||||
link_to "#{hide_whitespace? ? 'Show' : 'Hide'} whitespace changes", url, class: options[:class]
|
link_to "#{hide_whitespace? ? 'Show' : 'Hide'} whitespace changes", url, class: options[:class]
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff_file_path_text(diff_file, max: 60)
|
|
||||||
path = diff_file.new_path
|
|
||||||
|
|
||||||
return path unless path.size > max && max > 3
|
|
||||||
|
|
||||||
"...#{path[-(max - 3)..]}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def code_navigation_path(diffs)
|
def code_navigation_path(diffs)
|
||||||
Gitlab::CodeNavigationPath.new(merge_request.project, merge_request.diff_head_sha)
|
Gitlab::CodeNavigationPath.new(merge_request.project, merge_request.diff_head_sha)
|
||||||
end
|
end
|
||||||
|
|
|
@ -524,6 +524,10 @@ module Issuable
|
||||||
labels.order('title ASC').pluck(:title)
|
labels.order('title ASC').pluck(:title)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def labels_hook_attrs
|
||||||
|
labels.map(&:hook_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
# Convert this Issuable class name to a format usable by Ability definitions
|
# Convert this Issuable class name to a format usable by Ability definitions
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
|
|
|
@ -386,10 +386,6 @@ class Issue < ApplicationRecord
|
||||||
resource_parent.root_namespace&.issue_repositioning_disabled?
|
resource_parent.root_namespace&.issue_repositioning_disabled?
|
||||||
end
|
end
|
||||||
|
|
||||||
def hook_attrs
|
|
||||||
Gitlab::HookData::IssueBuilder.new(self).build
|
|
||||||
end
|
|
||||||
|
|
||||||
# `from` argument can be a Namespace or Project.
|
# `from` argument can be a Namespace or Project.
|
||||||
def to_reference(from = nil, full: false)
|
def to_reference(from = nil, full: false)
|
||||||
reference = "#{self.class.reference_prefix}#{iid}"
|
reference = "#{self.class.reference_prefix}#{iid}"
|
||||||
|
@ -530,10 +526,6 @@ class Issue < ApplicationRecord
|
||||||
::MergeRequestsClosingIssues.count_for_issue(self.id, user)
|
::MergeRequestsClosingIssues.count_for_issue(self.id, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def labels_hook_attrs
|
|
||||||
labels.map(&:hook_attrs)
|
|
||||||
end
|
|
||||||
|
|
||||||
def previous_updated_at
|
def previous_updated_at
|
||||||
previous_changes['updated_at']&.first || updated_at
|
previous_changes['updated_at']&.first || updated_at
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,9 +58,7 @@ class ProjectImportState < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
after_transition any => :failed do |state, _|
|
after_transition any => :failed do |state, _|
|
||||||
if Feature.enabled?(:remove_import_data_on_failure, state.project, default_enabled: :yaml)
|
state.project.remove_import_data
|
||||||
state.project.remove_import_data
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
after_transition started: :finished do |state, _|
|
after_transition started: :finished do |state, _|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
- return unless show_registration_enabled_user_callout?
|
- return unless show_registration_enabled_user_callout?
|
||||||
|
|
||||||
= render 'shared/global_alert',
|
= render Pajamas::AlertComponent.new(title: _('Anyone can register for an account.'),
|
||||||
title: _('Anyone can register for an account.'),
|
|
||||||
variant: :warning,
|
variant: :warning,
|
||||||
alert_class: 'js-registration-enabled-callout',
|
alert_class: 'js-registration-enabled-callout',
|
||||||
alert_data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT, dismiss_endpoint: callouts_path },
|
alert_data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT,
|
||||||
close_button_data: { testid: 'close-registration-enabled-callout' } do
|
dismiss_endpoint: callouts_path },
|
||||||
|
close_button_data: { testid: 'close-registration-enabled-callout' }) do
|
||||||
.gl-alert-body
|
.gl-alert-body
|
||||||
= _('Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable.')
|
= _('Only allow anyone to register for accounts on GitLab instances that you intend to be used by anyone. Allowing anyone to register makes GitLab instances more vulnerable.')
|
||||||
.gl-alert-actions
|
.gl-alert-actions
|
||||||
|
|
|
@ -3,7 +3,12 @@
|
||||||
- banner_info = storage_enforcement_banner_info(namespace)
|
- banner_info = storage_enforcement_banner_info(namespace)
|
||||||
- return unless banner_info.present?
|
- return unless banner_info.present?
|
||||||
|
|
||||||
= render 'shared/global_alert', variant: :warning, alert_class: 'js-storage-enforcement-banner', alert_data: { feature_id: banner_info[:callouts_feature_name], dismiss_endpoint: banner_info[:callouts_path], group_id: namespace.id, defer_links: "true" } do
|
= render Pajamas::AlertComponent.new(variant: :warning,
|
||||||
|
alert_class: 'js-storage-enforcement-banner',
|
||||||
|
alert_data: { feature_id: banner_info[:callouts_feature_name],
|
||||||
|
dismiss_endpoint: banner_info[:callouts_path],
|
||||||
|
group_id: namespace.id,
|
||||||
|
defer_links: "true" }) do
|
||||||
.gl-alert-body
|
.gl-alert-body
|
||||||
= banner_info[:text]
|
= banner_info[:text]
|
||||||
= banner_info[:learn_more_link]
|
= banner_info[:learn_more_link]
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
= content_for :head do
|
= content_for :head do
|
||||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||||
|
|
||||||
%table.code
|
%table.code.gl-mb-5
|
||||||
= render partial: "projects/diffs/email_line",
|
= render partial: "projects/diffs/email_line",
|
||||||
collection: discussion.truncated_diff_lines(diff_limit: diff_limit),
|
collection: discussion.truncated_diff_lines(diff_limit: diff_limit),
|
||||||
as: :line,
|
as: :line,
|
||||||
|
|
|
@ -2,18 +2,17 @@
|
||||||
= html_escape(_('%{user} created a merge request: %{mr_link}')) % { user: link_to(@merge_request.author_name, user_url(@merge_request.author)),
|
= html_escape(_('%{user} created a merge request: %{mr_link}')) % { user: link_to(@merge_request.author_name, user_url(@merge_request.author)),
|
||||||
mr_link: merge_request_reference_link(@merge_request) }
|
mr_link: merge_request_reference_link(@merge_request) }
|
||||||
|
|
||||||
%p
|
.branch
|
||||||
.branch
|
= merge_path_description(@merge_request, 'to')
|
||||||
= merge_path_description(@merge_request, 'to')
|
.author
|
||||||
.author
|
Author: #{@merge_request.author_name}
|
||||||
Author: #{@merge_request.author_name}
|
.assignee
|
||||||
.assignee
|
= assignees_label(@merge_request)
|
||||||
= assignees_label(@merge_request)
|
.reviewer
|
||||||
.reviewer
|
= reviewers_label(@merge_request)
|
||||||
= reviewers_label(@merge_request)
|
.approvers
|
||||||
.approvers
|
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
|
||||||
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
|
|
||||||
|
|
||||||
- if @merge_request.description
|
- if @merge_request.description
|
||||||
.md
|
.md.gl-mt-5
|
||||||
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
|
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- if Gitlab::CurrentSettings.email_author_in_body
|
- if Gitlab::CurrentSettings.email_author_in_body
|
||||||
%div
|
.gl-mb-5
|
||||||
= _("%{author_link} wrote:").html_safe % { author_link: link_to(@note.author_name, user_url(@note.author)) }
|
= _("%{author_link} wrote:").html_safe % { author_link: link_to(@note.author_name, user_url(@note.author)) }
|
||||||
.md
|
.md
|
||||||
= markdown(@note.note, pipeline: :email, author: @note.author, issuable_reference_expansion_enabled: true)
|
= markdown(@note.note, pipeline: :email, author: @note.author, issuable_reference_expansion_enabled: true)
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
.btn-group.gl-ml-3
|
.btn-group.gl-ml-3
|
||||||
= inline_diff_btn
|
= inline_diff_btn
|
||||||
= parallel_diff_btn
|
= parallel_diff_btn
|
||||||
= render 'projects/diffs/stats', diff_files: diff_files
|
= render Diffs::StatsComponent.new(diff_files: diff_files)
|
||||||
|
|
||||||
- if render_overflow_warning?(diffs)
|
- if render_overflow_warning?(diffs)
|
||||||
= render 'projects/diffs/warning', diff_files: diffs
|
= render 'projects/diffs/warning', diff_files: diffs
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
.js-diff-stats-dropdown{ data: { changed: diff_files.size, added: diff_files.sum(&:added_lines), deleted: diff_files.sum(&:removed_lines), files: diff_files_data(diff_files) } }
|
|
|
@ -7,7 +7,7 @@
|
||||||
= render 'shared/issuable/merge_request_assignees', issuable: issuable, count: render_count
|
= render 'shared/issuable/merge_request_assignees', issuable: issuable, count: render_count
|
||||||
- else
|
- else
|
||||||
- issuable.assignees.take(render_count).each do |assignee| # rubocop: disable CodeReuse/ActiveRecord
|
- issuable.assignees.take(render_count).each do |assignee| # rubocop: disable CodeReuse/ActiveRecord
|
||||||
= link_to_member(@project, assignee, name: false, title: s_("MrList|Assigned to %{name}, go to their profile.") % { name: assignee.name})
|
= link_to_member(@project, assignee, name: false, title: s_("MrList|Assigned to %{name}") % { name: assignee.name})
|
||||||
|
|
||||||
- if more_assignees_count > 0
|
- if more_assignees_count > 0
|
||||||
%span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old', qa_selector: 'avatar_counter_content' }, title: _("+%{more_assignees_count} more assignees") % { more_assignees_count: more_assignees_count} }
|
%span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old', qa_selector: 'avatar_counter_content' }, title: _("+%{more_assignees_count} more assignees") % { more_assignees_count: more_assignees_count} }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- issuable.merge_request_assignees.take(count).each do |merge_request_assignee| # rubocop: disable CodeReuse/ActiveRecord
|
- issuable.merge_request_assignees.take(count).each do |merge_request_assignee| # rubocop: disable CodeReuse/ActiveRecord
|
||||||
- assignee = merge_request_assignee.assignee
|
- assignee = merge_request_assignee.assignee
|
||||||
- assignee_tooltip = ( merge_request_assignee.attention_requested? ? s_("MrList|Attention requested from assignee %{name}, go to their profile.") : s_("MrList|Assigned to %{name}, go to their profile.") ) % { name: assignee.name}
|
- assignee_tooltip = ( merge_request_assignee.attention_requested? ? s_("MrList|Attention requested from assignee %{name}") : s_("MrList|Assigned to %{name}") ) % { name: assignee.name}
|
||||||
|
|
||||||
= link_to_member(@project, assignee, name: false, title: assignee_tooltip, extra_class: "gl-flex-direction-row-reverse") do
|
= link_to_member(@project, assignee, name: false, title: assignee_tooltip, extra_class: "gl-flex-direction-row-reverse") do
|
||||||
- if merge_request_assignee.attention_requested?
|
- if merge_request_assignee.attention_requested?
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- issuable.merge_request_reviewers.take(count).each do |merge_request_reviewer| # rubocop: disable CodeReuse/ActiveRecord
|
- issuable.merge_request_reviewers.take(count).each do |merge_request_reviewer| # rubocop: disable CodeReuse/ActiveRecord
|
||||||
- reviewer = merge_request_reviewer.reviewer
|
- reviewer = merge_request_reviewer.reviewer
|
||||||
- reviewer_tooltip = ( merge_request_reviewer.attention_requested? ? s_("MrList|Attention requested from reviewer %{name}, go to their profile.") : s_("MrList|Review requested from %{name}, go to their profile.") ) % { name: reviewer.name}
|
- reviewer_tooltip = ( merge_request_reviewer.attention_requested? ? s_("MrList|Attention requested from reviewer %{name}") : s_("MrList|Review requested from %{name}") ) % { name: reviewer.name}
|
||||||
|
|
||||||
= link_to_member(@project, reviewer, name: false, title: reviewer_tooltip, extra_class: "gl-flex-direction-row-reverse") do
|
= link_to_member(@project, reviewer, name: false, title: reviewer_tooltip, extra_class: "gl-flex-direction-row-reverse") do
|
||||||
- if merge_request_reviewer.attention_requested?
|
- if merge_request_reviewer.attention_requested?
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
= render 'shared/issuable/merge_request_reviewers', issuable: issuable, count: render_count
|
= render 'shared/issuable/merge_request_reviewers', issuable: issuable, count: render_count
|
||||||
- else
|
- else
|
||||||
- issuable.reviewers.take(render_count).each do |reviewer| # rubocop: disable CodeReuse/ActiveRecord
|
- issuable.reviewers.take(render_count).each do |reviewer| # rubocop: disable CodeReuse/ActiveRecord
|
||||||
= link_to_member(@project, reviewer, name: false, title: s_("MrList|Review requested from %{name}, go to their profile.") % { name: reviewer.name})
|
= link_to_member(@project, reviewer, name: false, title: s_("MrList|Review requested from %{name}") % { name: reviewer.name})
|
||||||
|
|
||||||
- if more_reviewers_count > 0
|
- if more_reviewers_count > 0
|
||||||
%span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old' }, title: _("+%{more_reviewers_count} more reviewers") % { more_reviewers_count: more_reviewers_count} }
|
%span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old' }, title: _("+%{more_reviewers_count} more reviewers") % { more_reviewers_count: more_reviewers_count} }
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
---
|
---
|
||||||
name: remove_import_data_on_failure
|
name: vulnerability_report_page_size_selector
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80074
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82438
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352156
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/356888
|
||||||
milestone: '14.8'
|
milestone: '14.10'
|
||||||
type: development
|
type: development
|
||||||
group: group::source code
|
group: group::threat insights
|
||||||
default_enabled: true
|
default_enabled: false
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
PUMA_EXTERNAL_METRICS_SERVER = Gitlab::Utils.to_boolean(ENV['PUMA_EXTERNAL_METRICS_SERVER'])
|
require Rails.root.join('metrics_server', 'metrics_server')
|
||||||
require Rails.root.join('metrics_server', 'metrics_server') if PUMA_EXTERNAL_METRICS_SERVER
|
|
||||||
|
|
||||||
# Keep separate directories for separate processes
|
# Keep separate directories for separate processes
|
||||||
def metrics_temp_dir
|
def metrics_temp_dir
|
||||||
|
@ -20,11 +19,17 @@ def prometheus_metrics_dir
|
||||||
ENV['prometheus_multiproc_dir'] || metrics_temp_dir
|
ENV['prometheus_multiproc_dir'] || metrics_temp_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
def puma_metrics_server_process?
|
def puma_master?
|
||||||
Prometheus::PidProvider.worker_id == 'puma_master'
|
Prometheus::PidProvider.worker_id == 'puma_master'
|
||||||
end
|
end
|
||||||
|
|
||||||
if puma_metrics_server_process?
|
# Whether a dedicated process should run that serves Rails application metrics, as opposed
|
||||||
|
# to using a Rails controller.
|
||||||
|
def puma_dedicated_metrics_server?
|
||||||
|
Settings.monitoring.web_exporter.enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
if puma_master?
|
||||||
# The following is necessary to ensure stale Prometheus metrics don't accumulate over time.
|
# The following is necessary to ensure stale Prometheus metrics don't accumulate over time.
|
||||||
# It needs to be done as early as possible to ensure new metrics aren't being deleted.
|
# It needs to be done as early as possible to ensure new metrics aren't being deleted.
|
||||||
#
|
#
|
||||||
|
@ -77,11 +82,7 @@ Gitlab::Cluster::LifecycleEvents.on_master_start do
|
||||||
if Gitlab::Runtime.puma?
|
if Gitlab::Runtime.puma?
|
||||||
Gitlab::Metrics::Samplers::PumaSampler.instance.start
|
Gitlab::Metrics::Samplers::PumaSampler.instance.start
|
||||||
|
|
||||||
if PUMA_EXTERNAL_METRICS_SERVER && Settings.monitoring.web_exporter.enabled
|
MetricsServer.start_for_puma if puma_dedicated_metrics_server?
|
||||||
MetricsServer.start_for_puma
|
|
||||||
else
|
|
||||||
Gitlab::Metrics::Exporter::WebExporter.instance.start
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Gitlab::Ci::Parsers.instrument!
|
Gitlab::Ci::Parsers.instrument!
|
||||||
|
@ -100,11 +101,7 @@ Gitlab::Cluster::LifecycleEvents.on_worker_start do
|
||||||
if Gitlab::Runtime.puma?
|
if Gitlab::Runtime.puma?
|
||||||
# Since we are observing a metrics server from the Puma primary, we would inherit
|
# Since we are observing a metrics server from the Puma primary, we would inherit
|
||||||
# this supervision thread after forking into workers, so we need to explicitly stop it here.
|
# this supervision thread after forking into workers, so we need to explicitly stop it here.
|
||||||
if PUMA_EXTERNAL_METRICS_SERVER
|
::MetricsServer::PumaProcessSupervisor.instance.stop if puma_dedicated_metrics_server?
|
||||||
::MetricsServer::PumaProcessSupervisor.instance.stop
|
|
||||||
else
|
|
||||||
Gitlab::Metrics::Exporter::WebExporter.instance.stop
|
|
||||||
end
|
|
||||||
|
|
||||||
Gitlab::Metrics::Samplers::ActionCableSampler.instance(logger: logger).start
|
Gitlab::Metrics::Samplers::ActionCableSampler.instance(logger: logger).start
|
||||||
end
|
end
|
||||||
|
@ -119,15 +116,11 @@ rescue IOError => e
|
||||||
Gitlab::Metrics.error_detected!
|
Gitlab::Metrics.error_detected!
|
||||||
end
|
end
|
||||||
|
|
||||||
if Gitlab::Runtime.puma?
|
if Gitlab::Runtime.puma? && puma_dedicated_metrics_server?
|
||||||
Gitlab::Cluster::LifecycleEvents.on_before_graceful_shutdown do
|
Gitlab::Cluster::LifecycleEvents.on_before_graceful_shutdown do
|
||||||
# We need to ensure that before we re-exec or shutdown server
|
# We need to ensure that before we re-exec or shutdown server
|
||||||
# we also stop the metrics server
|
# we also stop the metrics server
|
||||||
if PUMA_EXTERNAL_METRICS_SERVER
|
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
||||||
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
|
||||||
else
|
|
||||||
Gitlab::Metrics::Exporter::WebExporter.instance.stop
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Gitlab::Cluster::LifecycleEvents.on_before_master_restart do
|
Gitlab::Cluster::LifecycleEvents.on_before_master_restart do
|
||||||
|
@ -136,10 +129,6 @@ if Gitlab::Runtime.puma?
|
||||||
#
|
#
|
||||||
# We do it again, for being extra safe,
|
# We do it again, for being extra safe,
|
||||||
# but it should not be needed
|
# but it should not be needed
|
||||||
if PUMA_EXTERNAL_METRICS_SERVER
|
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
||||||
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
|
||||||
else
|
|
||||||
Gitlab::Metrics::Exporter::WebExporter.instance.stop
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 49 KiB |
Binary file not shown.
After Width: | Height: | Size: 158 KiB |
|
@ -256,12 +256,11 @@ Send all questions and requests related to the GitLab for Startups program to `s
|
||||||
|
|
||||||
### Support for Community Programs
|
### Support for Community Programs
|
||||||
|
|
||||||
Because these Community Programs are free of cost, regular Priority Support is not included. However, it can be purchased at a 95% discount in some cases.
|
Because these Community Programs are free of cost, regular Priority Support is not included.
|
||||||
If interested, email the relevant community program team: `education@gitlab.com`, `opensource@gitlab.com`, or `startups@gitlab.com`.
|
|
||||||
|
|
||||||
As a community member, you can follow this diagram to find support:
|
As a community member, you can follow this diagram to find support:
|
||||||
|
|
||||||
![Support diagram](img/support-diagram.png)
|
![Support diagram](img/support_diagram_c.png)
|
||||||
|
|
||||||
## Contact Support
|
## Contact Support
|
||||||
|
|
||||||
|
|
|
@ -278,8 +278,8 @@ To view the activity feed in Atom format, select the
|
||||||
[Feature flag `invite_members_group_modal`](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) removed.
|
[Feature flag `invite_members_group_modal`](https://gitlab.com/gitlab-org/gitlab/-/issues/352526) removed.
|
||||||
|
|
||||||
Similar to how you [share a project with a group](../project/members/share_project_with_groups.md),
|
Similar to how you [share a project with a group](../project/members/share_project_with_groups.md),
|
||||||
you can share a group with another group. Members get direct access
|
you can share a group with another group. To invite a group, you must be a member of it. Members get direct access
|
||||||
to the shared group. This includes members who inherited group membership from a parent group.
|
to the shared group. This includes members who inherited group membership from a parent group.
|
||||||
|
|
||||||
To share a given group, for example, `Frontend` with another group, for example,
|
To share a given group, for example, `Frontend` with another group, for example,
|
||||||
`Engineering`:
|
`Engineering`:
|
||||||
|
|
|
@ -538,6 +538,32 @@ Payload example:
|
||||||
"iid": 1,
|
"iid": 1,
|
||||||
"description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.",
|
"description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.",
|
||||||
"position": 0,
|
"position": 0,
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"id": 25,
|
||||||
|
"title": "Afterpod",
|
||||||
|
"color": "#3e8068",
|
||||||
|
"project_id": null,
|
||||||
|
"created_at": "2019-06-05T14:32:20.211Z",
|
||||||
|
"updated_at": "2019-06-05T14:32:20.211Z",
|
||||||
|
"template": false,
|
||||||
|
"description": null,
|
||||||
|
"type": "GroupLabel",
|
||||||
|
"group_id": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 86,
|
||||||
|
"title": "Element",
|
||||||
|
"color": "#231afe",
|
||||||
|
"project_id": 4,
|
||||||
|
"created_at": "2019-06-05T14:32:20.637Z",
|
||||||
|
"updated_at": "2019-06-05T14:32:20.637Z",
|
||||||
|
"template": false,
|
||||||
|
"description": null,
|
||||||
|
"type": "ProjectLabel",
|
||||||
|
"group_id": null
|
||||||
|
}
|
||||||
|
],
|
||||||
"source":{
|
"source":{
|
||||||
"name":"Gitlab Test",
|
"name":"Gitlab Test",
|
||||||
"description":"Aut reprehenderit ut est.",
|
"description":"Aut reprehenderit ut est.",
|
||||||
|
|
|
@ -43,10 +43,9 @@ module Gitlab
|
||||||
if note.for_commit?
|
if note.for_commit?
|
||||||
data[:commit] = build_data_for_commit(project, user, note)
|
data[:commit] = build_data_for_commit(project, user, note)
|
||||||
elsif note.for_issue?
|
elsif note.for_issue?
|
||||||
data[:issue] = note.noteable.hook_attrs
|
data[:issue] = Gitlab::HookData::IssueBuilder.new(note.noteable).build
|
||||||
data[:issue][:labels] = note.noteable.labels_hook_attrs
|
|
||||||
elsif note.for_merge_request?
|
elsif note.for_merge_request?
|
||||||
data[:merge_request] = note.noteable.hook_attrs
|
data[:merge_request] = Gitlab::HookData::MergeRequestBuilder.new(note.noteable).build
|
||||||
elsif note.for_snippet?
|
elsif note.for_snippet?
|
||||||
data[:snippet] = note.noteable.hook_attrs
|
data[:snippet] = note.noteable.hook_attrs
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,7 +13,7 @@ module Gitlab
|
||||||
event_type: event_type,
|
event_type: event_type,
|
||||||
user: user.hook_attrs,
|
user: user.hook_attrs,
|
||||||
project: issuable.project.hook_attrs,
|
project: issuable.project.hook_attrs,
|
||||||
object_attributes: issuable.hook_attrs,
|
object_attributes: issuable_builder.new(issuable).build,
|
||||||
labels: issuable.labels.map(&:hook_attrs),
|
labels: issuable.labels.map(&:hook_attrs),
|
||||||
changes: final_changes(changes.slice(*safe_keys)),
|
changes: final_changes(changes.slice(*safe_keys)),
|
||||||
# DEPRECATED
|
# DEPRECATED
|
||||||
|
|
|
@ -60,6 +60,7 @@ module Gitlab
|
||||||
human_time_estimate: merge_request.human_time_estimate,
|
human_time_estimate: merge_request.human_time_estimate,
|
||||||
assignee_ids: merge_request.assignee_ids,
|
assignee_ids: merge_request.assignee_ids,
|
||||||
assignee_id: merge_request.assignee_ids.first, # This key is deprecated
|
assignee_id: merge_request.assignee_ids.first, # This key is deprecated
|
||||||
|
labels: merge_request.labels_hook_attrs,
|
||||||
state: merge_request.state, # This key is deprecated
|
state: merge_request.state, # This key is deprecated
|
||||||
blocking_discussions_resolved: merge_request.mergeable_discussions_state?
|
blocking_discussions_resolved: merge_request.mergeable_discussions_state?
|
||||||
}
|
}
|
||||||
|
|
|
@ -5686,6 +5686,12 @@ msgstr ""
|
||||||
msgid "BillingPlans|While GitLab is ending availability of the Bronze plan, you can still renew your Bronze subscription one additional time before %{eoa_bronze_plan_end_date}. We are also offering a limited time free upgrade to our Premium Plan (up to 25 users)! Learn more about the changes and offers in our %{announcement_link}."
|
msgid "BillingPlans|While GitLab is ending availability of the Bronze plan, you can still renew your Bronze subscription one additional time before %{eoa_bronze_plan_end_date}. We are also offering a limited time free upgrade to our Premium Plan (up to 25 users)! Learn more about the changes and offers in our %{announcement_link}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "BillingPlans|You don't have any groups. You'll need to %{create_group_link_start}create one%{create_group_link_end} and %{move_link_start}move this project to it%{move_link_end}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "BillingPlans|You'll have to %{move_link_start}move this project%{move_link_end} to one of your groups."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "BillingPlans|Your GitLab.com %{plan} trial will %{strong_open}expire after %{expiration_date}%{strong_close}. You can retain access to the %{plan} features by upgrading below."
|
msgid "BillingPlans|Your GitLab.com %{plan} trial will %{strong_open}expire after %{expiration_date}%{strong_close}. You can retain access to the %{plan} features by upgrading below."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -24566,16 +24572,16 @@ msgstr ""
|
||||||
msgid "MrDeploymentActions|Stop environment"
|
msgid "MrDeploymentActions|Stop environment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "MrList|Assigned to %{name}, go to their profile."
|
msgid "MrList|Assigned to %{name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "MrList|Attention requested from assignee %{name}, go to their profile."
|
msgid "MrList|Attention requested from assignee %{name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "MrList|Attention requested from reviewer %{name}, go to their profile."
|
msgid "MrList|Attention requested from reviewer %{name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "MrList|Review requested from %{name}, go to their profile."
|
msgid "MrList|Review requested from %{name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Multi-project"
|
msgid "Multi-project"
|
||||||
|
@ -32367,6 +32373,9 @@ msgstr ""
|
||||||
msgid "Runners|Never contacted"
|
msgid "Runners|Never contacted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Never contacted:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|New group runners view"
|
msgid "Runners|New group runners view"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -32388,12 +32397,18 @@ msgstr ""
|
||||||
msgid "Runners|Offline runners"
|
msgid "Runners|Offline runners"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Offline:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|Online"
|
msgid "Runners|Online"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|Online runners"
|
msgid "Runners|Online runners"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Online:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|Pause from accepting jobs"
|
msgid "Runners|Pause from accepting jobs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -32462,9 +32477,18 @@ msgstr ""
|
||||||
msgid "Runners|Runner cannot be deleted, please contact your administrator"
|
msgid "Runners|Runner cannot be deleted, please contact your administrator"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Runner has contacted GitLab within the last %{elapsedTime}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Runner has never contacted GitLab (when you register a runner, use %{codeStart}gitlab-runner run%{codeEnd} to bring it online)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|Runner has never contacted this instance"
|
msgid "Runners|Runner has never contacted this instance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Runner has not contacted GitLab in more than %{elapsedTime}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|Runner is locked and available for currently assigned projects only. Only administrators can change the assigned projects."
|
msgid "Runners|Runner is locked and available for currently assigned projects only. Only administrators can change the assigned projects."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -32492,6 +32516,9 @@ msgstr ""
|
||||||
msgid "Runners|Runner registration"
|
msgid "Runners|Runner registration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Runner statuses"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|Runner unassigned from project."
|
msgid "Runners|Runner unassigned from project."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -32522,6 +32549,9 @@ msgstr ""
|
||||||
msgid "Runners|Stale runners"
|
msgid "Runners|Stale runners"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|Stale:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|Status"
|
msgid "Runners|Status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -32540,6 +32570,9 @@ msgstr ""
|
||||||
msgid "Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
|
msgid "Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runners|This runner has not run any jobs."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runners|This runner is associated with specific projects."
|
msgid "Runners|This runner is associated with specific projects."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -32618,9 +32651,6 @@ msgstr ""
|
||||||
msgid "Runners|stale"
|
msgid "Runners|stale"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Runner|This runner has not run any jobs."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Running"
|
msgid "Running"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -33792,6 +33822,9 @@ msgstr ""
|
||||||
msgid "SecurityReports|Severity"
|
msgid "SecurityReports|Severity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "SecurityReports|Show %{pageSize} items"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "SecurityReports|Sometimes a scanner can't determine a finding's severity. Those findings may still be a potential source of risk though. Please review these manually."
|
msgid "SecurityReports|Sometimes a scanner can't determine a finding's severity. Those findings may still be a potential source of risk though. Please review these manually."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
RSpec.describe Diffs::StatsComponent, type: :component do
|
||||||
|
include RepoHelpers
|
||||||
|
|
||||||
|
subject(:component) do
|
||||||
|
described_class.new(diff_files: diff_files)
|
||||||
|
end
|
||||||
|
|
||||||
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
|
let_it_be(:repository) { project.repository }
|
||||||
|
let_it_be(:commit) { project.commit(sample_commit.id) }
|
||||||
|
let_it_be(:diffs) { commit.raw_diffs }
|
||||||
|
let_it_be(:diff) { diffs.first }
|
||||||
|
let_it_be(:diff_refs) { commit.diff_refs }
|
||||||
|
let_it_be(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) }
|
||||||
|
let_it_be(:diff_files) { [diff_file] }
|
||||||
|
|
||||||
|
describe "rendered component" do
|
||||||
|
subject { rendered_component }
|
||||||
|
|
||||||
|
let(:element) { page.find(".js-diff-stats-dropdown") }
|
||||||
|
|
||||||
|
before do
|
||||||
|
render_inline component
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to have_selector(".js-diff-stats-dropdown") }
|
||||||
|
|
||||||
|
it "renders the data attributes" do
|
||||||
|
expect(element["data-changed"]).to eq("1")
|
||||||
|
expect(element["data-added"]).to eq("10")
|
||||||
|
expect(element["data-deleted"]).to eq("3")
|
||||||
|
|
||||||
|
expect(Gitlab::Json.parse(element["data-files"])).to eq([{
|
||||||
|
"href" => "##{Digest::SHA1.hexdigest(diff_file.file_path)}",
|
||||||
|
"title" => diff_file.new_path,
|
||||||
|
"name" => diff_file.file_path,
|
||||||
|
"path" => diff_file.file_path,
|
||||||
|
"icon" => "file-modified",
|
||||||
|
"iconColor" => "",
|
||||||
|
"added" => diff_file.added_lines,
|
||||||
|
"removed" => diff_file.removed_lines
|
||||||
|
}])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#diff_file_path_text" do
|
||||||
|
it "returns full path by default" do
|
||||||
|
expect(subject.diff_file_path_text(diff_file)).to eq(diff_file.new_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns truncated path" do
|
||||||
|
expect(subject.diff_file_path_text(diff_file, max: 10)).to eq("...open.rb")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the path if max is oddly small" do
|
||||||
|
expect(subject.diff_file_path_text(diff_file, max: 3)).to eq(diff_file.new_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the path if max is oddly large" do
|
||||||
|
expect(subject.diff_file_path_text(diff_file, max: 100)).to eq(diff_file.new_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,104 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
RSpec.describe Pajamas::AlertComponent, :aggregate_failures, type: :component do
|
||||||
|
context 'with content' do
|
||||||
|
before do
|
||||||
|
render_inline(described_class.new) { '_content_' }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has content' do
|
||||||
|
expect(rendered_component).to have_text('_content_')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with defaults' do
|
||||||
|
before do
|
||||||
|
render_inline described_class.new
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not set a title' do
|
||||||
|
expect(rendered_component).not_to have_selector('.gl-alert-title')
|
||||||
|
expect(rendered_component).to have_selector('.gl-alert-icon-no-title')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders the default variant' do
|
||||||
|
expect(rendered_component).to have_selector('.gl-alert-info')
|
||||||
|
expect(rendered_component).to have_selector("[data-testid='information-o-icon']")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders a dismiss button' do
|
||||||
|
expect(rendered_component).to have_selector('.gl-dismiss-btn.js-close')
|
||||||
|
expect(rendered_component).to have_selector("[data-testid='close-icon']")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with custom options' do
|
||||||
|
context 'with simple options' do
|
||||||
|
context 'without dismissible content' do
|
||||||
|
before do
|
||||||
|
render_inline described_class.new(
|
||||||
|
title: '_title_',
|
||||||
|
dismissible: false,
|
||||||
|
alert_class: '_alert_class_',
|
||||||
|
alert_data: {
|
||||||
|
feature_id: '_feature_id_',
|
||||||
|
dismiss_endpoint: '_dismiss_endpoint_'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the title' do
|
||||||
|
expect(rendered_component).to have_selector('.gl-alert-title')
|
||||||
|
expect(rendered_component).to have_content('_title_')
|
||||||
|
expect(rendered_component).not_to have_selector('.gl-alert-icon-no-title')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets to not be dismissible' do
|
||||||
|
expect(rendered_component).not_to have_selector('.gl-dismiss-btn.js-close')
|
||||||
|
expect(rendered_component).not_to have_selector("[data-testid='close-icon']")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the alert_class' do
|
||||||
|
expect(rendered_component).to have_selector('._alert_class_')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the alert_data' do
|
||||||
|
expect(rendered_component).to have_selector('[data-feature-id="_feature_id_"][data-dismiss-endpoint="_dismiss_endpoint_"]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with dismissible content' do
|
||||||
|
before do
|
||||||
|
render_inline described_class.new(
|
||||||
|
close_button_class: '_close_button_class_',
|
||||||
|
close_button_data: {
|
||||||
|
testid: '_close_button_testid_'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders a dismiss button and data' do
|
||||||
|
expect(rendered_component).to have_selector('.gl-dismiss-btn.js-close._close_button_class_')
|
||||||
|
expect(rendered_component).to have_selector("[data-testid='close-icon']")
|
||||||
|
expect(rendered_component).to have_selector('[data-testid="_close_button_testid_"]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with setting variant type' do
|
||||||
|
where(:variant) { [:warning, :success, :danger, :tip] }
|
||||||
|
|
||||||
|
before do
|
||||||
|
render_inline described_class.new(variant: variant)
|
||||||
|
end
|
||||||
|
|
||||||
|
with_them do
|
||||||
|
it 'renders the variant' do
|
||||||
|
expect(rendered_component).to have_selector(".gl-alert-#{variant}")
|
||||||
|
expect(rendered_component).to have_selector("[data-testid='#{described_class::ICONS[variant]}-icon']")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -41,17 +41,5 @@ RSpec.describe Projects::Packages::InfrastructureRegistryController do
|
||||||
|
|
||||||
it_behaves_like 'returning response status', :not_found
|
it_behaves_like 'returning response status', :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with package file pending destruction' do
|
|
||||||
let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: terraform_module) }
|
|
||||||
|
|
||||||
let(:terraform_module_package_file) { terraform_module.package_files.first }
|
|
||||||
|
|
||||||
it 'does not return them' do
|
|
||||||
subject
|
|
||||||
|
|
||||||
expect(assigns(:package_files)).to contain_exactly(terraform_module_package_file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,7 +70,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
|
||||||
it 'updates merge request with assignee' do
|
it 'updates merge request with assignee' do
|
||||||
change_assignee(user.name)
|
change_assignee(user.name)
|
||||||
|
|
||||||
expect(find('.issuable-meta a.author-link')[:title]).to eq "Attention requested from assignee #{user.name}, go to their profile."
|
expect(find('.issuable-meta a.author-link')[:title]).to eq "Attention requested from assignee #{user.name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,13 @@ import $ from 'jquery';
|
||||||
import { nextTick } from 'vue';
|
import { nextTick } from 'vue';
|
||||||
import '~/behaviors/markdown/render_gfm';
|
import '~/behaviors/markdown/render_gfm';
|
||||||
import { GlTooltip, GlModal } from '@gitlab/ui';
|
import { GlTooltip, GlModal } from '@gitlab/ui';
|
||||||
|
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||||
import { stubComponent } from 'helpers/stub_component';
|
import { stubComponent } from 'helpers/stub_component';
|
||||||
import { TEST_HOST } from 'helpers/test_constants';
|
import { TEST_HOST } from 'helpers/test_constants';
|
||||||
import { mockTracking } from 'helpers/tracking_helper';
|
import { mockTracking } from 'helpers/tracking_helper';
|
||||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||||
import Description from '~/issues/show/components/description.vue';
|
import Description from '~/issues/show/components/description.vue';
|
||||||
|
import { updateHistory } from '~/lib/utils/url_utility';
|
||||||
import TaskList from '~/task_list';
|
import TaskList from '~/task_list';
|
||||||
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
|
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
|
||||||
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
|
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
|
||||||
|
@ -17,6 +19,10 @@ import {
|
||||||
} from '../mock_data/mock_data';
|
} from '../mock_data/mock_data';
|
||||||
|
|
||||||
jest.mock('~/flash');
|
jest.mock('~/flash');
|
||||||
|
jest.mock('~/lib/utils/url_utility', () => ({
|
||||||
|
...jest.requireActual('~/lib/utils/url_utility'),
|
||||||
|
updateHistory: jest.fn(),
|
||||||
|
}));
|
||||||
jest.mock('~/task_list');
|
jest.mock('~/task_list');
|
||||||
|
|
||||||
const showModal = jest.fn();
|
const showModal = jest.fn();
|
||||||
|
@ -55,6 +61,8 @@ describe('Description component', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
setWindowLocation(TEST_HOST);
|
||||||
|
|
||||||
if (!document.querySelector('.issuable-meta')) {
|
if (!document.querySelector('.issuable-meta')) {
|
||||||
const metaData = document.createElement('div');
|
const metaData = document.createElement('div');
|
||||||
metaData.classList.add('issuable-meta');
|
metaData.classList.add('issuable-meta');
|
||||||
|
@ -285,47 +293,85 @@ describe('Description component', () => {
|
||||||
describe('work items detail', () => {
|
describe('work items detail', () => {
|
||||||
const findTaskLink = () => wrapper.find('a.gfm-issue');
|
const findTaskLink = () => wrapper.find('a.gfm-issue');
|
||||||
|
|
||||||
beforeEach(() => {
|
describe('when opening and closing', () => {
|
||||||
createComponent({
|
beforeEach(() => {
|
||||||
props: {
|
createComponent({
|
||||||
descriptionHtml: descriptionHtmlWithTask,
|
props: {
|
||||||
},
|
descriptionHtml: descriptionHtmlWithTask,
|
||||||
provide: {
|
},
|
||||||
glFeatures: { workItems: true },
|
provide: {
|
||||||
},
|
glFeatures: { workItems: true },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return nextTick();
|
||||||
});
|
});
|
||||||
return nextTick();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('opens when task button is clicked', async () => {
|
it('opens when task button is clicked', async () => {
|
||||||
expect(findWorkItemDetailModal().props('visible')).toBe(false);
|
expect(findWorkItemDetailModal().props('visible')).toBe(false);
|
||||||
|
|
||||||
await findTaskLink().trigger('click');
|
await findTaskLink().trigger('click');
|
||||||
|
|
||||||
expect(findWorkItemDetailModal().props('visible')).toBe(true);
|
expect(findWorkItemDetailModal().props('visible')).toBe(true);
|
||||||
});
|
expect(updateHistory).toHaveBeenCalledWith({
|
||||||
|
url: `${TEST_HOST}/?work_item_id=2`,
|
||||||
it('closes from an open state', async () => {
|
replace: true,
|
||||||
await findTaskLink().trigger('click');
|
});
|
||||||
|
|
||||||
expect(findWorkItemDetailModal().props('visible')).toBe(true);
|
|
||||||
|
|
||||||
findWorkItemDetailModal().vm.$emit('close');
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(findWorkItemDetailModal().props('visible')).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tracks when opened', async () => {
|
|
||||||
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
|
||||||
|
|
||||||
await findTaskLink().trigger('click');
|
|
||||||
|
|
||||||
expect(trackingSpy).toHaveBeenCalledWith('workItems:show', 'viewed_work_item_from_modal', {
|
|
||||||
category: 'workItems:show',
|
|
||||||
label: 'work_item_view',
|
|
||||||
property: 'type_task',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('closes from an open state', async () => {
|
||||||
|
await findTaskLink().trigger('click');
|
||||||
|
|
||||||
|
expect(findWorkItemDetailModal().props('visible')).toBe(true);
|
||||||
|
|
||||||
|
findWorkItemDetailModal().vm.$emit('close');
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(findWorkItemDetailModal().props('visible')).toBe(false);
|
||||||
|
expect(updateHistory).toHaveBeenLastCalledWith({
|
||||||
|
url: `${TEST_HOST}/`,
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tracks when opened', async () => {
|
||||||
|
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||||
|
|
||||||
|
await findTaskLink().trigger('click');
|
||||||
|
|
||||||
|
expect(trackingSpy).toHaveBeenCalledWith(
|
||||||
|
'workItems:show',
|
||||||
|
'viewed_work_item_from_modal',
|
||||||
|
{
|
||||||
|
category: 'workItems:show',
|
||||||
|
label: 'work_item_view',
|
||||||
|
property: 'type_task',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when url query `work_item_id` exists', () => {
|
||||||
|
it.each`
|
||||||
|
behavior | workItemId | visible
|
||||||
|
${'opens'} | ${'123'} | ${true}
|
||||||
|
${'does not open'} | ${'123e'} | ${false}
|
||||||
|
${'does not open'} | ${'12e3'} | ${false}
|
||||||
|
${'does not open'} | ${'1e23'} | ${false}
|
||||||
|
${'does not open'} | ${'x'} | ${false}
|
||||||
|
${'does not open'} | ${'undefined'} | ${false}
|
||||||
|
`(
|
||||||
|
'$behavior when url contains `work_item_id=$workItemId`',
|
||||||
|
async ({ workItemId, visible }) => {
|
||||||
|
setWindowLocation(`?work_item_id=${workItemId}`);
|
||||||
|
|
||||||
|
createComponent({
|
||||||
|
props: { descriptionHtml: descriptionHtmlWithTask },
|
||||||
|
provide: { glFeatures: { workItems: true } },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(findWorkItemDetailModal().props('visible')).toBe(visible);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import SignInLegacyButton from '~/jira_connect/subscriptions/components/sign_in_
|
||||||
import SignInOauthButton from '~/jira_connect/subscriptions/components/sign_in_oauth_button.vue';
|
import SignInOauthButton from '~/jira_connect/subscriptions/components/sign_in_oauth_button.vue';
|
||||||
import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
|
import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
|
||||||
import createStore from '~/jira_connect/subscriptions/store';
|
import createStore from '~/jira_connect/subscriptions/store';
|
||||||
import { I18N_DEFAULT_SIGN_IN_BUTTON_TEXT } from '../../../../../app/assets/javascripts/jira_connect/subscriptions/constants';
|
import { I18N_DEFAULT_SIGN_IN_BUTTON_TEXT } from '~/jira_connect/subscriptions/constants';
|
||||||
|
|
||||||
jest.mock('~/jira_connect/subscriptions/utils');
|
jest.mock('~/jira_connect/subscriptions/utils');
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,13 @@ import adminRunnersCountQuery from '~/runner/graphql/list/admin_runners_count.qu
|
||||||
import { captureException } from '~/runner/sentry_utils';
|
import { captureException } from '~/runner/sentry_utils';
|
||||||
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
||||||
|
|
||||||
import { runnersData, runnersCountData, runnersDataPaginated } from '../mock_data';
|
import {
|
||||||
|
runnersData,
|
||||||
|
runnersCountData,
|
||||||
|
runnersDataPaginated,
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
|
} from '../mock_data';
|
||||||
|
|
||||||
const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN';
|
const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN';
|
||||||
const mockRunners = runnersData.data.runners.nodes;
|
const mockRunners = runnersData.data.runners.nodes;
|
||||||
|
@ -94,6 +100,8 @@ describe('AdminRunnersApp', () => {
|
||||||
},
|
},
|
||||||
provide: {
|
provide: {
|
||||||
localMutations,
|
localMutations,
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
...provide,
|
...provide,
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`RunnerStatusPopover renders complete text 1`] = `"Never contacted: Runner has never contacted GitLab (when you register a runner, use gitlab-runner run to bring it online) Online: Runner has contacted GitLab within the last 2 hours Offline: Runner has not contacted GitLab in more than 2 hours Stale: Runner has not contacted GitLab in more than 2 months"`;
|
|
@ -6,7 +6,8 @@ import {
|
||||||
} from 'helpers/vue_test_utils_helper';
|
} from 'helpers/vue_test_utils_helper';
|
||||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||||
import RunnerList from '~/runner/components/runner_list.vue';
|
import RunnerList from '~/runner/components/runner_list.vue';
|
||||||
import { runnersData } from '../mock_data';
|
import RunnerStatusPopover from '~/runner/components/runner_status_popover.vue';
|
||||||
|
import { runnersData, onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
|
||||||
|
|
||||||
const mockRunners = runnersData.data.runners.nodes;
|
const mockRunners = runnersData.data.runners.nodes;
|
||||||
const mockActiveRunnersCount = mockRunners.length;
|
const mockActiveRunnersCount = mockRunners.length;
|
||||||
|
@ -28,21 +29,34 @@ describe('RunnerList', () => {
|
||||||
activeRunnersCount: mockActiveRunnersCount,
|
activeRunnersCount: mockActiveRunnersCount,
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
|
provide: {
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
|
},
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
createComponent({}, mountExtended);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.destroy();
|
wrapper.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays headers', () => {
|
it('Displays headers', () => {
|
||||||
|
createComponent(
|
||||||
|
{
|
||||||
|
stubs: {
|
||||||
|
RunnerStatusPopover: {
|
||||||
|
template: '<div/>',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mountExtended,
|
||||||
|
);
|
||||||
|
|
||||||
const headerLabels = findHeaders().wrappers.map((w) => w.text());
|
const headerLabels = findHeaders().wrappers.map((w) => w.text());
|
||||||
|
|
||||||
|
expect(findHeaders().at(0).findComponent(RunnerStatusPopover).exists()).toBe(true);
|
||||||
|
|
||||||
expect(headerLabels).toEqual([
|
expect(headerLabels).toEqual([
|
||||||
'Status',
|
'Status',
|
||||||
'Runner',
|
'Runner',
|
||||||
|
@ -61,6 +75,8 @@ describe('RunnerList', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays a list of runners', () => {
|
it('Displays a list of runners', () => {
|
||||||
|
createComponent({}, mountExtended);
|
||||||
|
|
||||||
expect(findRows()).toHaveLength(4);
|
expect(findRows()).toHaveLength(4);
|
||||||
|
|
||||||
expect(findSkeletonLoader().exists()).toBe(false);
|
expect(findSkeletonLoader().exists()).toBe(false);
|
||||||
|
@ -69,6 +85,8 @@ describe('RunnerList', () => {
|
||||||
it('Displays details of a runner', () => {
|
it('Displays details of a runner', () => {
|
||||||
const { id, description, version, shortSha } = mockRunners[0];
|
const { id, description, version, shortSha } = mockRunners[0];
|
||||||
|
|
||||||
|
createComponent({}, mountExtended);
|
||||||
|
|
||||||
// Badges
|
// Badges
|
||||||
expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText(
|
expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText(
|
||||||
'never contacted paused',
|
'never contacted paused',
|
||||||
|
@ -183,6 +201,8 @@ describe('RunnerList', () => {
|
||||||
const { id, shortSha } = mockRunners[0];
|
const { id, shortSha } = mockRunners[0];
|
||||||
const numericId = getIdFromGraphQLId(id);
|
const numericId = getIdFromGraphQLId(id);
|
||||||
|
|
||||||
|
createComponent({}, mountExtended);
|
||||||
|
|
||||||
expect(findCell({ fieldKey: 'summary' }).text()).toContain(`#${numericId} (${shortSha})`);
|
expect(findCell({ fieldKey: 'summary' }).text()).toContain(`#${numericId} (${shortSha})`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { GlSprintf } from '@gitlab/ui';
|
||||||
|
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||||
|
import RunnerStatusPopover from '~/runner/components/runner_status_popover.vue';
|
||||||
|
import HelpPopover from '~/vue_shared/components/help_popover.vue';
|
||||||
|
import { onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
|
||||||
|
|
||||||
|
describe('RunnerStatusPopover', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const createComponent = ({ provide = {} } = {}) => {
|
||||||
|
wrapper = shallowMountExtended(RunnerStatusPopover, {
|
||||||
|
provide: {
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
|
...provide,
|
||||||
|
},
|
||||||
|
stubs: {
|
||||||
|
GlSprintf,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const findHelpPopover = () => wrapper.findComponent(HelpPopover);
|
||||||
|
|
||||||
|
it('renders popoover', () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(findHelpPopover().exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders complete text', () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(findHelpPopover().text()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -38,7 +38,13 @@ import getGroupRunnersCountQuery from '~/runner/graphql/list/group_runners_count
|
||||||
import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue';
|
import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue';
|
||||||
import { captureException } from '~/runner/sentry_utils';
|
import { captureException } from '~/runner/sentry_utils';
|
||||||
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
||||||
import { groupRunnersData, groupRunnersDataPaginated, groupRunnersCountData } from '../mock_data';
|
import {
|
||||||
|
groupRunnersData,
|
||||||
|
groupRunnersDataPaginated,
|
||||||
|
groupRunnersCountData,
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
|
} from '../mock_data';
|
||||||
|
|
||||||
Vue.use(VueApollo);
|
Vue.use(VueApollo);
|
||||||
Vue.use(GlToast);
|
Vue.use(GlToast);
|
||||||
|
@ -90,6 +96,10 @@ describe('GroupRunnersApp', () => {
|
||||||
groupRunnersLimitedCount: mockGroupRunnersLimitedCount,
|
groupRunnersLimitedCount: mockGroupRunnersLimitedCount,
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
|
provide: {
|
||||||
|
onlineContactTimeoutSecs,
|
||||||
|
staleTimeoutSecs,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,10 @@ import runnerWithGroupData from 'test_fixtures/graphql/runner/details/runner.que
|
||||||
import runnerProjectsData from 'test_fixtures/graphql/runner/details/runner_projects.query.graphql.json';
|
import runnerProjectsData from 'test_fixtures/graphql/runner/details/runner_projects.query.graphql.json';
|
||||||
import runnerJobsData from 'test_fixtures/graphql/runner/details/runner_jobs.query.graphql.json';
|
import runnerJobsData from 'test_fixtures/graphql/runner/details/runner_jobs.query.graphql.json';
|
||||||
|
|
||||||
|
// Other mock data
|
||||||
|
export const onlineContactTimeoutSecs = 2 * 60 * 60;
|
||||||
|
export const staleTimeoutSecs = 5259492; // Ruby's `2.months`
|
||||||
|
|
||||||
export {
|
export {
|
||||||
runnersData,
|
runnersData,
|
||||||
runnersCountData,
|
runnersCountData,
|
||||||
|
|
|
@ -86,7 +86,9 @@ RSpec.describe Ci::RunnersHelper do
|
||||||
it 'returns the data in format' do
|
it 'returns the data in format' do
|
||||||
expect(helper.admin_runners_data_attributes).to eq({
|
expect(helper.admin_runners_data_attributes).to eq({
|
||||||
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
|
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
|
||||||
registration_token: Gitlab::CurrentSettings.runners_registration_token
|
registration_token: Gitlab::CurrentSettings.runners_registration_token,
|
||||||
|
online_contact_timeout_secs: 7200,
|
||||||
|
stale_timeout_secs: 7889238
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -128,12 +130,14 @@ RSpec.describe Ci::RunnersHelper do
|
||||||
let(:group) { create(:group) }
|
let(:group) { create(:group) }
|
||||||
|
|
||||||
it 'returns group data to render a runner list' do
|
it 'returns group data to render a runner list' do
|
||||||
data = helper.group_runners_data_attributes(group)
|
expect(helper.group_runners_data_attributes(group)).to eq({
|
||||||
|
registration_token: group.runners_token,
|
||||||
expect(data[:registration_token]).to eq(group.runners_token)
|
group_id: group.id,
|
||||||
expect(data[:group_id]).to eq(group.id)
|
group_full_path: group.full_path,
|
||||||
expect(data[:group_full_path]).to eq(group.full_path)
|
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
|
||||||
expect(data[:runner_install_help_page]).to eq('https://docs.gitlab.com/runner/install/')
|
online_contact_timeout_secs: 7200,
|
||||||
|
stale_timeout_secs: 7889238
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -425,16 +425,6 @@ RSpec.describe DiffHelper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#diff_file_path_text' do
|
|
||||||
it 'returns full path by default' do
|
|
||||||
expect(diff_file_path_text(diff_file)).to eq(diff_file.new_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns truncated path' do
|
|
||||||
expect(diff_file_path_text(diff_file, max: 10)).to eq("...open.rb")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#collapsed_diff_url" do
|
describe "#collapsed_diff_url" do
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,18 +8,22 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
let(:data) { described_class.build(note, user) }
|
let(:data) { described_class.build(note, user) }
|
||||||
let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors
|
let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors
|
||||||
|
|
||||||
before do
|
shared_examples 'includes general data' do
|
||||||
expect(data).to have_key(:object_attributes)
|
specify do
|
||||||
expect(data[:object_attributes]).to have_key(:url)
|
expect(data).to have_key(:object_attributes)
|
||||||
expect(data[:object_attributes][:url])
|
expect(data[:object_attributes]).to have_key(:url)
|
||||||
.to eq(Gitlab::UrlBuilder.build(note))
|
expect(data[:object_attributes][:url])
|
||||||
expect(data[:object_kind]).to eq('note')
|
.to eq(Gitlab::UrlBuilder.build(note))
|
||||||
expect(data[:user]).to eq(user.hook_attrs)
|
expect(data[:object_kind]).to eq('note')
|
||||||
|
expect(data[:user]).to eq(user.hook_attrs)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'When asking for a note on commit' do
|
describe 'When asking for a note on commit' do
|
||||||
let(:note) { create(:note_on_commit, project: project) }
|
let(:note) { create(:note_on_commit, project: project) }
|
||||||
|
|
||||||
|
it_behaves_like 'includes general data'
|
||||||
|
|
||||||
it 'returns the note and commit-specific data' do
|
it 'returns the note and commit-specific data' do
|
||||||
expect(data).to have_key(:commit)
|
expect(data).to have_key(:commit)
|
||||||
end
|
end
|
||||||
|
@ -31,6 +35,8 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
describe 'When asking for a note on commit diff' do
|
describe 'When asking for a note on commit diff' do
|
||||||
let(:note) { create(:diff_note_on_commit, project: project) }
|
let(:note) { create(:diff_note_on_commit, project: project) }
|
||||||
|
|
||||||
|
it_behaves_like 'includes general data'
|
||||||
|
|
||||||
it 'returns the note and commit-specific data' do
|
it 'returns the note and commit-specific data' do
|
||||||
expect(data).to have_key(:commit)
|
expect(data).to have_key(:commit)
|
||||||
end
|
end
|
||||||
|
@ -51,22 +57,21 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
create(:note_on_issue, noteable: issue, project: project)
|
create(:note_on_issue, noteable: issue, project: project)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the note and issue-specific data' do
|
it_behaves_like 'includes general data'
|
||||||
without_timestamps = lambda { |label| label.except('created_at', 'updated_at') }
|
|
||||||
hook_attrs = issue.reload.hook_attrs
|
|
||||||
|
|
||||||
expect(data).to have_key(:issue)
|
it 'returns the note and issue-specific data' do
|
||||||
expect(data[:issue].except('updated_at', 'labels'))
|
expect_next_instance_of(Gitlab::HookData::IssueBuilder) do |issue_data_builder|
|
||||||
.to eq(hook_attrs.except('updated_at', 'labels'))
|
expect(issue_data_builder).to receive(:build).and_return('Issue data')
|
||||||
expect(data[:issue]['updated_at'])
|
end
|
||||||
.to be >= hook_attrs['updated_at']
|
|
||||||
expect(data[:issue]['labels'].map(&without_timestamps))
|
expect(data[:issue]).to eq('Issue data')
|
||||||
.to eq(hook_attrs['labels'].map(&without_timestamps))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with confidential issue' do
|
context 'with confidential issue' do
|
||||||
let(:issue) { create(:issue, project: project, confidential: true) }
|
let(:issue) { create(:issue, project: project, confidential: true) }
|
||||||
|
|
||||||
|
it_behaves_like 'includes general data'
|
||||||
|
|
||||||
it 'sets event_type to confidential_note' do
|
it 'sets event_type to confidential_note' do
|
||||||
expect(data[:event_type]).to eq('confidential_note')
|
expect(data[:event_type]).to eq('confidential_note')
|
||||||
end
|
end
|
||||||
|
@ -77,10 +82,12 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'When asking for a note on merge request' do
|
describe 'When asking for a note on merge request' do
|
||||||
|
let(:label) { create(:label, project: project) }
|
||||||
let(:merge_request) do
|
let(:merge_request) do
|
||||||
create(:merge_request, created_at: fixed_time,
|
create(:labeled_merge_request, created_at: fixed_time,
|
||||||
updated_at: fixed_time,
|
updated_at: fixed_time,
|
||||||
source_project: project)
|
source_project: project,
|
||||||
|
labels: [label])
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:note) do
|
let(:note) do
|
||||||
|
@ -88,12 +95,14 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
project: project)
|
project: project)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the note and merge request data' do
|
it_behaves_like 'includes general data'
|
||||||
expect(data).to have_key(:merge_request)
|
|
||||||
expect(data[:merge_request].except('updated_at'))
|
it 'returns the merge request data' do
|
||||||
.to eq(merge_request.reload.hook_attrs.except('updated_at'))
|
expect_next_instance_of(Gitlab::HookData::MergeRequestBuilder) do |mr_data_builder|
|
||||||
expect(data[:merge_request]['updated_at'])
|
expect(mr_data_builder).to receive(:build).and_return('MR data')
|
||||||
.to be >= merge_request.hook_attrs['updated_at']
|
end
|
||||||
|
|
||||||
|
expect(data[:merge_request]).to eq('MR data')
|
||||||
end
|
end
|
||||||
|
|
||||||
include_examples 'project hook data'
|
include_examples 'project hook data'
|
||||||
|
@ -101,9 +110,11 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'When asking for a note on merge request diff' do
|
describe 'When asking for a note on merge request diff' do
|
||||||
|
let(:label) { create(:label, project: project) }
|
||||||
let(:merge_request) do
|
let(:merge_request) do
|
||||||
create(:merge_request, created_at: fixed_time, updated_at: fixed_time,
|
create(:labeled_merge_request, created_at: fixed_time, updated_at: fixed_time,
|
||||||
source_project: project)
|
source_project: project,
|
||||||
|
labels: [label])
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:note) do
|
let(:note) do
|
||||||
|
@ -111,12 +122,14 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
project: project)
|
project: project)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the note and merge request diff data' do
|
it_behaves_like 'includes general data'
|
||||||
expect(data).to have_key(:merge_request)
|
|
||||||
expect(data[:merge_request].except('updated_at'))
|
it 'returns the merge request data' do
|
||||||
.to eq(merge_request.reload.hook_attrs.except('updated_at'))
|
expect_next_instance_of(Gitlab::HookData::MergeRequestBuilder) do |mr_data_builder|
|
||||||
expect(data[:merge_request]['updated_at'])
|
expect(mr_data_builder).to receive(:build).and_return('MR data')
|
||||||
.to be >= merge_request.hook_attrs['updated_at']
|
end
|
||||||
|
|
||||||
|
expect(data[:merge_request]).to eq('MR data')
|
||||||
end
|
end
|
||||||
|
|
||||||
include_examples 'project hook data'
|
include_examples 'project hook data'
|
||||||
|
@ -134,6 +147,8 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
||||||
project: project)
|
project: project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'includes general data'
|
||||||
|
|
||||||
it 'returns the note and project snippet data' do
|
it 'returns the note and project snippet data' do
|
||||||
expect(data).to have_key(:snippet)
|
expect(data).to have_key(:snippet)
|
||||||
expect(data[:snippet].except('updated_at'))
|
expect(data[:snippet].except('updated_at'))
|
||||||
|
|
|
@ -6,7 +6,7 @@ RSpec.describe Gitlab::HookData::IssuableBuilder do
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
# This shared example requires a `builder` and `user` variable
|
# This shared example requires a `builder` and `user` variable
|
||||||
shared_examples 'issuable hook data' do |kind|
|
shared_examples 'issuable hook data' do |kind, hook_data_issuable_builder_class|
|
||||||
let(:data) { builder.build(user: user) }
|
let(:data) { builder.build(user: user) }
|
||||||
|
|
||||||
include_examples 'project hook data' do
|
include_examples 'project hook data' do
|
||||||
|
@ -20,7 +20,7 @@ RSpec.describe Gitlab::HookData::IssuableBuilder do
|
||||||
expect(data[:object_kind]).to eq(kind)
|
expect(data[:object_kind]).to eq(kind)
|
||||||
expect(data[:user]).to eq(user.hook_attrs)
|
expect(data[:user]).to eq(user.hook_attrs)
|
||||||
expect(data[:project]).to eq(builder.issuable.project.hook_attrs)
|
expect(data[:project]).to eq(builder.issuable.project.hook_attrs)
|
||||||
expect(data[:object_attributes]).to eq(builder.issuable.hook_attrs)
|
expect(data[:object_attributes]).to eq(hook_data_issuable_builder_class.new(issuable).build)
|
||||||
expect(data[:changes]).to eq({})
|
expect(data[:changes]).to eq({})
|
||||||
expect(data[:repository]).to eq(builder.issuable.project.hook_attrs.slice(:name, :url, :description, :homepage))
|
expect(data[:repository]).to eq(builder.issuable.project.hook_attrs.slice(:name, :url, :description, :homepage))
|
||||||
end
|
end
|
||||||
|
@ -95,12 +95,12 @@ RSpec.describe Gitlab::HookData::IssuableBuilder do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#build' do
|
describe '#build' do
|
||||||
it_behaves_like 'issuable hook data', 'issue' do
|
it_behaves_like 'issuable hook data', 'issue', Gitlab::HookData::IssueBuilder do
|
||||||
let(:issuable) { create(:issue, description: 'A description') }
|
let(:issuable) { create(:issue, description: 'A description') }
|
||||||
let(:builder) { described_class.new(issuable) }
|
let(:builder) { described_class.new(issuable) }
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'issuable hook data', 'merge_request' do
|
it_behaves_like 'issuable hook data', 'merge_request', Gitlab::HookData::MergeRequestBuilder do
|
||||||
let(:issuable) { create(:merge_request, description: 'A description') }
|
let(:issuable) { create(:merge_request, description: 'A description') }
|
||||||
let(:builder) { described_class.new(issuable) }
|
let(:builder) { described_class.new(issuable) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,6 +62,7 @@ RSpec.describe Gitlab::HookData::MergeRequestBuilder do
|
||||||
expect(data).to include(:human_time_estimate)
|
expect(data).to include(:human_time_estimate)
|
||||||
expect(data).to include(:human_total_time_spent)
|
expect(data).to include(:human_total_time_spent)
|
||||||
expect(data).to include(:human_time_change)
|
expect(data).to include(:human_time_change)
|
||||||
|
expect(data).to include(:labels)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the MR has an image in the description' do
|
context 'when the MR has an image in the description' do
|
||||||
|
|
|
@ -625,6 +625,16 @@ RSpec.describe Issuable do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#labels_hook_attrs" do
|
||||||
|
let(:project) { create(:project) }
|
||||||
|
let(:label) { create(:label) }
|
||||||
|
let(:issue) { create(:labeled_issue, project: project, labels: [label]) }
|
||||||
|
|
||||||
|
it "returns a list of label hook attributes" do
|
||||||
|
expect(issue.labels_hook_attrs).to match_array([label.hook_attrs])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '.labels_hash' do
|
describe '.labels_hash' do
|
||||||
let(:feature_label) { create(:label, title: 'Feature') }
|
let(:feature_label) { create(:label, title: 'Feature') }
|
||||||
let(:second_label) { create(:label, title: 'Second Label') }
|
let(:second_label) { create(:label, title: 'Second Label') }
|
||||||
|
|
|
@ -1172,18 +1172,6 @@ RSpec.describe Issue do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#hook_attrs' do
|
|
||||||
it 'delegates to Gitlab::HookData::IssueBuilder#build' do
|
|
||||||
builder = double
|
|
||||||
|
|
||||||
expect(Gitlab::HookData::IssueBuilder)
|
|
||||||
.to receive(:new).with(subject).and_return(builder)
|
|
||||||
expect(builder).to receive(:build)
|
|
||||||
|
|
||||||
subject.hook_attrs
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#check_for_spam?' do
|
describe '#check_for_spam?' do
|
||||||
let_it_be(:support_bot) { ::User.support_bot }
|
let_it_be(:support_bot) { ::User.support_bot }
|
||||||
|
|
||||||
|
@ -1332,15 +1320,6 @@ RSpec.describe Issue do
|
||||||
subject { create(:issue, updated_at: 1.hour.ago) }
|
subject { create(:issue, updated_at: 1.hour.ago) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#labels_hook_attrs" do
|
|
||||||
let(:label) { create(:label) }
|
|
||||||
let(:issue) { create(:labeled_issue, project: reusable_project, labels: [label]) }
|
|
||||||
|
|
||||||
it "returns a list of label hook attributes" do
|
|
||||||
expect(issue.labels_hook_attrs).to eq([label.hook_attrs])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "relative positioning" do
|
context "relative positioning" do
|
||||||
let_it_be(:group) { create(:group) }
|
let_it_be(:group) { create(:group) }
|
||||||
let_it_be(:project) { create(:project, group: group) }
|
let_it_be(:project) { create(:project, group: group) }
|
||||||
|
|
|
@ -1757,18 +1757,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#hook_attrs' do
|
|
||||||
it 'delegates to Gitlab::HookData::MergeRequestBuilder#build' do
|
|
||||||
builder = double
|
|
||||||
|
|
||||||
expect(Gitlab::HookData::MergeRequestBuilder)
|
|
||||||
.to receive(:new).with(subject).and_return(builder)
|
|
||||||
expect(builder).to receive(:build)
|
|
||||||
|
|
||||||
subject.hook_attrs
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#diverged_commits_count' do
|
describe '#diverged_commits_count' do
|
||||||
let(:project) { create(:project, :repository) }
|
let(:project) { create(:project, :repository) }
|
||||||
let(:forked_project) { fork_project(project, nil, repository: true) }
|
let(:forked_project) { fork_project(project, nil, repository: true) }
|
||||||
|
|
|
@ -89,19 +89,6 @@ RSpec.describe ProjectImportState, type: :model do
|
||||||
import_state.mark_as_failed(error_message)
|
import_state.mark_as_failed(error_message)
|
||||||
end.to change { project.reload.import_data }.from(import_data).to(nil)
|
end.to change { project.reload.import_data }.from(import_data).to(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when remove_import_data_on_failure feature flag is disabled' do
|
|
||||||
it 'removes project import data' do
|
|
||||||
stub_feature_flags(remove_import_data_on_failure: false)
|
|
||||||
|
|
||||||
project = create(:project, import_data: ProjectImportData.new(data: { 'test' => 'some data' }))
|
|
||||||
import_state = create(:import_state, :started, project: project)
|
|
||||||
|
|
||||||
expect do
|
|
||||||
import_state.mark_as_failed(error_message)
|
|
||||||
end.not_to change { project.reload.import_data }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#human_status_name' do
|
describe '#human_status_name' do
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# This shared example requires a `builder` and `user` variable
|
|
||||||
RSpec.shared_examples 'issuable hook data' do |kind|
|
|
||||||
let(:data) { builder.build(user: user) }
|
|
||||||
|
|
||||||
include_examples 'project hook data' do
|
|
||||||
let(:project) { builder.issuable.project }
|
|
||||||
end
|
|
||||||
|
|
||||||
include_examples 'deprecated repository hook data'
|
|
||||||
|
|
||||||
context "with a #{kind}" do
|
|
||||||
it 'contains issuable data' do
|
|
||||||
expect(data[:object_kind]).to eq(kind)
|
|
||||||
expect(data[:user]).to eq(user.hook_attrs)
|
|
||||||
expect(data[:project]).to eq(builder.issuable.project.hook_attrs)
|
|
||||||
expect(data[:object_attributes]).to eq(builder.issuable.hook_attrs)
|
|
||||||
expect(data[:changes]).to eq({})
|
|
||||||
expect(data[:repository]).to eq(builder.issuable.project.hook_attrs.slice(:name, :url, :description, :homepage))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not contain certain keys' do
|
|
||||||
expect(data).not_to have_key(:assignees)
|
|
||||||
expect(data).not_to have_key(:assignee)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'changes are given' do
|
|
||||||
let(:changes) do
|
|
||||||
{
|
|
||||||
cached_markdown_version: %w[foo bar],
|
|
||||||
description: ['A description', 'A cool description'],
|
|
||||||
description_html: %w[foo bar],
|
|
||||||
in_progress_merge_commit_sha: %w[foo bar],
|
|
||||||
lock_version: %w[foo bar],
|
|
||||||
merge_jid: %w[foo bar],
|
|
||||||
title: ['A title', 'Hello World'],
|
|
||||||
title_html: %w[foo bar]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:data) { builder.build(user: user, changes: changes) }
|
|
||||||
|
|
||||||
it 'populates the :changes hash' do
|
|
||||||
expect(data[:changes]).to match(hash_including({
|
|
||||||
title: { previous: 'A title', current: 'Hello World' },
|
|
||||||
description: { previous: 'A description', current: 'A cool description' }
|
|
||||||
}))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not contain certain keys' do
|
|
||||||
expect(data[:changes]).not_to have_key('cached_markdown_version')
|
|
||||||
expect(data[:changes]).not_to have_key('description_html')
|
|
||||||
expect(data[:changes]).not_to have_key('lock_version')
|
|
||||||
expect(data[:changes]).not_to have_key('title_html')
|
|
||||||
expect(data[:changes]).not_to have_key('in_progress_merge_commit_sha')
|
|
||||||
expect(data[:changes]).not_to have_key('merge_jid')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -154,6 +154,30 @@ RSpec.shared_examples 'logs an auth warning' do |requested_actions|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
RSpec.shared_examples 'allowed to delete container repository images' do
|
||||||
|
let(:authentication_abilities) do
|
||||||
|
[:admin_container_image]
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a valid token'
|
||||||
|
|
||||||
|
context 'allow to delete images' do
|
||||||
|
let(:current_params) do
|
||||||
|
{ scopes: ["repository:#{project.full_path}:*"] }
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a deletable'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'allow to delete images since registry 2.7' do
|
||||||
|
let(:current_params) do
|
||||||
|
{ scopes: ["repository:#{project.full_path}:delete"] }
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a deletable since registry 2.7'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
RSpec.shared_examples 'a container registry auth service' do
|
RSpec.shared_examples 'a container registry auth service' do
|
||||||
include_context 'container registry auth service context'
|
include_context 'container registry auth service context'
|
||||||
|
|
||||||
|
@ -544,38 +568,14 @@ RSpec.shared_examples 'a container registry auth service' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'delete authorized as maintainer' do
|
context 'delete authorized as maintainer' do
|
||||||
let_it_be(:current_project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
let_it_be(:current_user) { create(:user) }
|
let_it_be(:current_user) { create(:user) }
|
||||||
|
|
||||||
let(:authentication_abilities) do
|
|
||||||
[:admin_container_image]
|
|
||||||
end
|
|
||||||
|
|
||||||
before_all do
|
before_all do
|
||||||
current_project.add_maintainer(current_user)
|
project.add_maintainer(current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'a valid token'
|
it_behaves_like 'allowed to delete container repository images'
|
||||||
|
|
||||||
context 'allow to delete images' do
|
|
||||||
let(:current_params) do
|
|
||||||
{ scopes: ["repository:#{current_project.full_path}:*"] }
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'a deletable' do
|
|
||||||
let(:project) { current_project }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'allow to delete images since registry 2.7' do
|
|
||||||
let(:current_params) do
|
|
||||||
{ scopes: ["repository:#{current_project.full_path}:delete"] }
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'a deletable since registry 2.7' do
|
|
||||||
let(:project) { current_project }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'build authorized as user' do
|
context 'build authorized as user' do
|
||||||
|
|
Loading…
Reference in New Issue