Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5820d448c1
commit
85ea3dd4f4
|
@ -1,10 +1,9 @@
|
|||
<script>
|
||||
import { GlTable, GlButton, GlBadge, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlTable, GlButton, GlBadge, GlTooltipDirective, GlAvatarLink, GlAvatar } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.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 UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
|
@ -21,7 +20,8 @@ export default {
|
|||
GlBadge,
|
||||
ClipboardButton,
|
||||
TooltipOnTruncate,
|
||||
UserAvatarLink,
|
||||
GlAvatarLink,
|
||||
GlAvatar,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
directives: {
|
||||
|
@ -102,13 +102,14 @@ export default {
|
|||
</template>
|
||||
<template #cell(owner)="{ item }">
|
||||
<span class="trigger-owner sr-only">{{ item.owner.name }}</span>
|
||||
<user-avatar-link
|
||||
<gl-avatar-link
|
||||
v-if="item.owner"
|
||||
:link-href="item.owner.path"
|
||||
:img-src="item.owner.avatarUrl"
|
||||
:tooltip-text="item.owner.name"
|
||||
:img-alt="item.owner.name"
|
||||
/>
|
||||
v-gl-tooltip
|
||||
:href="item.owner.path"
|
||||
:title="item.owner.name"
|
||||
>
|
||||
<gl-avatar :size="24" :src="item.owner.avatarUrl" />
|
||||
</gl-avatar-link>
|
||||
</template>
|
||||
<template #cell(lastUsed)="{ item }">
|
||||
<time-ago-tooltip v-if="item.lastUsed" :time="item.lastUsed" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
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 {
|
||||
components: {
|
||||
|
|
|
@ -4,6 +4,8 @@ import $ from 'jquery';
|
|||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
|
||||
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 TaskList from '~/task_list';
|
||||
import Tracking from '~/tracking';
|
||||
|
@ -65,13 +67,17 @@ export default {
|
|||
},
|
||||
},
|
||||
data() {
|
||||
const workItemId = getParameterByName('work_item_id');
|
||||
|
||||
return {
|
||||
preAnimation: false,
|
||||
pulseAnimation: false,
|
||||
initialUpdate: true,
|
||||
taskButtons: [],
|
||||
activeTask: {},
|
||||
workItemId: null,
|
||||
workItemId: isPositiveInteger(workItemId)
|
||||
? convertToGraphQLId(TYPE_WORK_ITEM, workItemId)
|
||||
: undefined,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -184,6 +190,7 @@ export default {
|
|||
taskLink.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.workItemId = convertToGraphQLId(TYPE_WORK_ITEM, issue);
|
||||
this.updateWorkItemIdUrlQuery(issue);
|
||||
this.track('viewed_work_item_from_modal', {
|
||||
category: 'workItems:show',
|
||||
label: 'work_item_view',
|
||||
|
@ -232,12 +239,19 @@ export default {
|
|||
this.$refs.modal.hide();
|
||||
},
|
||||
closeWorkItemDetailModal() {
|
||||
this.workItemId = null;
|
||||
this.workItemId = undefined;
|
||||
this.updateWorkItemIdUrlQuery(undefined);
|
||||
},
|
||||
handleCreateTask(description) {
|
||||
this.$emit('updateDescription', description);
|
||||
this.closeCreateTaskModal();
|
||||
},
|
||||
updateWorkItemIdUrlQuery(workItemId) {
|
||||
updateHistory({
|
||||
url: setUrlParams({ work_item_id: workItemId }),
|
||||
replace: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
safeHtmlConfig: { ADD_TAGS: ['gl-emoji', 'copy-code'] },
|
||||
};
|
||||
|
@ -281,7 +295,7 @@ export default {
|
|||
body-class="gl-p-0!"
|
||||
>
|
||||
<create-work-item
|
||||
:is-modal="true"
|
||||
is-modal
|
||||
:initial-title="activeTask.title"
|
||||
:issue-gid="issueGid"
|
||||
:lock-version="lockVersion"
|
||||
|
|
|
@ -57,7 +57,7 @@ export default {
|
|||
:link-href="author.path"
|
||||
:img-alt="author.name"
|
||||
:img-src="author.avatar_url"
|
||||
:img-size="26"
|
||||
:img-size="24"
|
||||
:tooltip-text="author.name"
|
||||
tooltip-placement="bottom"
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import initFilePickers from '~/file_pickers';
|
||||
import BindInOut from '../../../../behaviors/bind_in_out';
|
||||
import Group from '../../../../group';
|
||||
import BindInOut from '~/behaviors/bind_in_out';
|
||||
import Group from '~/group';
|
||||
|
||||
(() => {
|
||||
BindInOut.initAll();
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils';
|
||||
|
||||
import Translate from '../../../../../vue_shared/translate';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
|
||||
Vue.use(Translate);
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ import Vue from 'vue';
|
|||
import { __ } from '~/locale';
|
||||
import RefSelector from '~/ref/components/ref_selector.vue';
|
||||
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
|
||||
import setupNativeFormVariableList from '../../../../ci_variable_list/native_form_variable_list';
|
||||
import GlFieldErrors from '../../../../gl_field_errors';
|
||||
import Translate from '../../../../vue_shared/translate';
|
||||
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
|
||||
import GlFieldErrors from '~/gl_field_errors';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import intervalPatternInput from './components/interval_pattern_input.vue';
|
||||
import TimezoneDropdown from './components/timezone_dropdown';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import $ from 'jquery';
|
||||
import GLForm from '../../../../gl_form';
|
||||
import RefSelectDropdown from '../../../../ref_select_dropdown';
|
||||
import ZenMode from '../../../../zen_mode';
|
||||
import GLForm from '~/gl_form';
|
||||
import RefSelectDropdown from '~/ref_select_dropdown';
|
||||
import ZenMode from '~/zen_mode';
|
||||
|
||||
new ZenMode(); // eslint-disable-line no-new
|
||||
new GLForm($('.tag-form')); // eslint-disable-line no-new
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import $ from 'jquery';
|
||||
import initTree from 'ee_else_ce/repository';
|
||||
import initBlob from '~/blob_edit/blob_bundle';
|
||||
import ShortcutsNavigation from '../../../../behaviors/shortcuts/shortcuts_navigation';
|
||||
import NewCommitForm from '../../../../new_commit_form';
|
||||
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
|
||||
import NewCommitForm from '~/new_commit_form';
|
||||
|
||||
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
|
||||
initBlob();
|
||||
|
|
|
@ -26,7 +26,12 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const { runnerInstallHelpPage, registrationToken } = el.dataset;
|
||||
const {
|
||||
runnerInstallHelpPage,
|
||||
registrationToken,
|
||||
onlineContactTimeoutSecs,
|
||||
staleTimeoutSecs,
|
||||
} = el.dataset;
|
||||
|
||||
const { cacheConfig, typeDefs, localMutations } = createLocalState();
|
||||
|
||||
|
@ -40,6 +45,8 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
|
|||
provide: {
|
||||
runnerInstallHelpPage,
|
||||
localMutations,
|
||||
onlineContactTimeoutSecs,
|
||||
staleTimeoutSecs,
|
||||
},
|
||||
render(h) {
|
||||
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 { formatJobCount, tableField } from '../utils';
|
||||
import RunnerSummaryCell from './cells/runner_summary_cell.vue';
|
||||
import RunnerStatusPopover from './runner_status_popover.vue';
|
||||
import RunnerStatusCell from './cells/runner_status_cell.vue';
|
||||
import RunnerTags from './runner_tags.vue';
|
||||
|
||||
|
@ -26,6 +27,7 @@ export default {
|
|||
GlSkeletonLoader,
|
||||
TooltipOnTruncate,
|
||||
TimeAgo,
|
||||
RunnerStatusPopover,
|
||||
RunnerSummaryCell,
|
||||
RunnerTags,
|
||||
RunnerStatusCell,
|
||||
|
@ -136,6 +138,11 @@ export default {
|
|||
/>
|
||||
</template>
|
||||
|
||||
<template #head(status)="{ label }">
|
||||
{{ label }}
|
||||
<runner-status-popover />
|
||||
</template>
|
||||
|
||||
<template #cell(status)="{ item }">
|
||||
<runner-status-cell :runner="item" />
|
||||
</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');
|
||||
|
||||
// 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
|
||||
export const I18N_ONLINE_TIMEAGO_TOOLTIP = s__(
|
||||
'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_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
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ export const initGroupRunners = (selector = '#js-group-runners') => {
|
|||
groupId,
|
||||
groupFullPath,
|
||||
groupRunnersLimitedCount,
|
||||
onlineContactTimeoutSecs,
|
||||
staleTimeoutSecs,
|
||||
} = el.dataset;
|
||||
|
||||
const apolloProvider = new VueApollo({
|
||||
|
@ -32,6 +34,8 @@ export const initGroupRunners = (selector = '#js-group-runners') => {
|
|||
provide: {
|
||||
runnerInstallHelpPage,
|
||||
groupId,
|
||||
onlineContactTimeoutSecs: parseInt(onlineContactTimeoutSecs, 10),
|
||||
staleTimeoutSecs: parseInt(staleTimeoutSecs, 10),
|
||||
},
|
||||
render(h) {
|
||||
return h(GroupRunnersApp, {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { numberToHumanSize } from '../../../../lib/utils/number_utils';
|
||||
import { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -26,11 +26,15 @@ a {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
.md {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
.gl-mb-5 {
|
||||
@include gl-mb-5;
|
||||
}
|
||||
|
||||
.gl-mt-5 {
|
||||
@include gl-mt-5;
|
||||
}
|
||||
|
||||
.content {
|
||||
hr {
|
||||
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
|
||||
@package = project.packages.find(params[:id])
|
||||
@package_files = @package.installable_package_files.recent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -63,7 +63,9 @@ module Ci
|
|||
# Runner install help page is external, located at
|
||||
# https://gitlab.com/gitlab-org/gitlab-runner
|
||||
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
|
||||
|
||||
|
@ -83,7 +85,9 @@ module Ci
|
|||
registration_token: group.runners_token,
|
||||
group_id: group.id,
|
||||
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
|
||||
|
||||
|
|
|
@ -195,24 +195,6 @@ module DiffHelper
|
|||
!diff_file.deleted_file? && @merge_request && @merge_request.source_project
|
||||
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)
|
||||
diffs_collection.overflow?.tap do |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)
|
||||
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?
|
||||
params[:w] == '1'
|
||||
end
|
||||
|
@ -302,14 +267,6 @@ module DiffHelper
|
|||
link_to "#{hide_whitespace? ? 'Show' : 'Hide'} whitespace changes", url, class: options[:class]
|
||||
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)
|
||||
Gitlab::CodeNavigationPath.new(merge_request.project, merge_request.diff_head_sha)
|
||||
end
|
||||
|
|
|
@ -524,6 +524,10 @@ module Issuable
|
|||
labels.order('title ASC').pluck(:title)
|
||||
end
|
||||
|
||||
def labels_hook_attrs
|
||||
labels.map(&:hook_attrs)
|
||||
end
|
||||
|
||||
# Convert this Issuable class name to a format usable by Ability definitions
|
||||
#
|
||||
# Examples:
|
||||
|
|
|
@ -386,10 +386,6 @@ class Issue < ApplicationRecord
|
|||
resource_parent.root_namespace&.issue_repositioning_disabled?
|
||||
end
|
||||
|
||||
def hook_attrs
|
||||
Gitlab::HookData::IssueBuilder.new(self).build
|
||||
end
|
||||
|
||||
# `from` argument can be a Namespace or Project.
|
||||
def to_reference(from = nil, full: false)
|
||||
reference = "#{self.class.reference_prefix}#{iid}"
|
||||
|
@ -530,10 +526,6 @@ class Issue < ApplicationRecord
|
|||
::MergeRequestsClosingIssues.count_for_issue(self.id, user)
|
||||
end
|
||||
|
||||
def labels_hook_attrs
|
||||
labels.map(&:hook_attrs)
|
||||
end
|
||||
|
||||
def previous_updated_at
|
||||
previous_changes['updated_at']&.first || updated_at
|
||||
end
|
||||
|
|
|
@ -58,9 +58,7 @@ class ProjectImportState < ApplicationRecord
|
|||
end
|
||||
|
||||
after_transition any => :failed do |state, _|
|
||||
if Feature.enabled?(:remove_import_data_on_failure, state.project, default_enabled: :yaml)
|
||||
state.project.remove_import_data
|
||||
end
|
||||
state.project.remove_import_data
|
||||
end
|
||||
|
||||
after_transition started: :finished do |state, _|
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
- return unless show_registration_enabled_user_callout?
|
||||
|
||||
= render 'shared/global_alert',
|
||||
title: _('Anyone can register for an account.'),
|
||||
= render Pajamas::AlertComponent.new(title: _('Anyone can register for an account.'),
|
||||
variant: :warning,
|
||||
alert_class: 'js-registration-enabled-callout',
|
||||
alert_data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT, dismiss_endpoint: callouts_path },
|
||||
close_button_data: { testid: 'close-registration-enabled-callout' } do
|
||||
alert_data: { feature_id: Users::CalloutsHelper::REGISTRATION_ENABLED_CALLOUT,
|
||||
dismiss_endpoint: callouts_path },
|
||||
close_button_data: { testid: 'close-registration-enabled-callout' }) do
|
||||
.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.')
|
||||
.gl-alert-actions
|
||||
|
|
|
@ -3,7 +3,12 @@
|
|||
- banner_info = storage_enforcement_banner_info(namespace)
|
||||
- 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
|
||||
= banner_info[:text]
|
||||
= banner_info[:learn_more_link]
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
= content_for :head do
|
||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||
|
||||
%table.code
|
||||
%table.code.gl-mb-5
|
||||
= render partial: "projects/diffs/email_line",
|
||||
collection: discussion.truncated_diff_lines(diff_limit: diff_limit),
|
||||
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)),
|
||||
mr_link: merge_request_reference_link(@merge_request) }
|
||||
|
||||
%p
|
||||
.branch
|
||||
= merge_path_description(@merge_request, 'to')
|
||||
.author
|
||||
Author: #{@merge_request.author_name}
|
||||
.assignee
|
||||
= assignees_label(@merge_request)
|
||||
.reviewer
|
||||
= reviewers_label(@merge_request)
|
||||
.approvers
|
||||
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
|
||||
.branch
|
||||
= merge_path_description(@merge_request, 'to')
|
||||
.author
|
||||
Author: #{@merge_request.author_name}
|
||||
.assignee
|
||||
= assignees_label(@merge_request)
|
||||
.reviewer
|
||||
= reviewers_label(@merge_request)
|
||||
.approvers
|
||||
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
|
||||
|
||||
- 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)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
- 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)) }
|
||||
.md
|
||||
= markdown(@note.note, pipeline: :email, author: @note.author, issuable_reference_expansion_enabled: true)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
.btn-group.gl-ml-3
|
||||
= inline_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)
|
||||
= 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
|
||||
- else
|
||||
- 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
|
||||
%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
|
||||
- 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
|
||||
- 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
|
||||
- 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
|
||||
- if merge_request_reviewer.attention_requested?
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
= render 'shared/issuable/merge_request_reviewers', issuable: issuable, count: render_count
|
||||
- else
|
||||
- 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
|
||||
%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
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80074
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352156
|
||||
milestone: '14.8'
|
||||
name: vulnerability_report_page_size_selector
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82438
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/356888
|
||||
milestone: '14.10'
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: true
|
||||
group: group::threat insights
|
||||
default_enabled: false
|
|
@ -1,7 +1,6 @@
|
|||
# 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') if PUMA_EXTERNAL_METRICS_SERVER
|
||||
require Rails.root.join('metrics_server', 'metrics_server')
|
||||
|
||||
# Keep separate directories for separate processes
|
||||
def metrics_temp_dir
|
||||
|
@ -20,11 +19,17 @@ def prometheus_metrics_dir
|
|||
ENV['prometheus_multiproc_dir'] || metrics_temp_dir
|
||||
end
|
||||
|
||||
def puma_metrics_server_process?
|
||||
def puma_master?
|
||||
Prometheus::PidProvider.worker_id == 'puma_master'
|
||||
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.
|
||||
# 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?
|
||||
Gitlab::Metrics::Samplers::PumaSampler.instance.start
|
||||
|
||||
if PUMA_EXTERNAL_METRICS_SERVER && Settings.monitoring.web_exporter.enabled
|
||||
MetricsServer.start_for_puma
|
||||
else
|
||||
Gitlab::Metrics::Exporter::WebExporter.instance.start
|
||||
end
|
||||
MetricsServer.start_for_puma if puma_dedicated_metrics_server?
|
||||
end
|
||||
|
||||
Gitlab::Ci::Parsers.instrument!
|
||||
|
@ -100,11 +101,7 @@ Gitlab::Cluster::LifecycleEvents.on_worker_start do
|
|||
if Gitlab::Runtime.puma?
|
||||
# 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.
|
||||
if PUMA_EXTERNAL_METRICS_SERVER
|
||||
::MetricsServer::PumaProcessSupervisor.instance.stop
|
||||
else
|
||||
Gitlab::Metrics::Exporter::WebExporter.instance.stop
|
||||
end
|
||||
::MetricsServer::PumaProcessSupervisor.instance.stop if puma_dedicated_metrics_server?
|
||||
|
||||
Gitlab::Metrics::Samplers::ActionCableSampler.instance(logger: logger).start
|
||||
end
|
||||
|
@ -119,15 +116,11 @@ rescue IOError => e
|
|||
Gitlab::Metrics.error_detected!
|
||||
end
|
||||
|
||||
if Gitlab::Runtime.puma?
|
||||
if Gitlab::Runtime.puma? && puma_dedicated_metrics_server?
|
||||
Gitlab::Cluster::LifecycleEvents.on_before_graceful_shutdown do
|
||||
# We need to ensure that before we re-exec or shutdown server
|
||||
# we also stop the metrics server
|
||||
if PUMA_EXTERNAL_METRICS_SERVER
|
||||
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
||||
else
|
||||
Gitlab::Metrics::Exporter::WebExporter.instance.stop
|
||||
end
|
||||
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
||||
end
|
||||
|
||||
Gitlab::Cluster::LifecycleEvents.on_before_master_restart do
|
||||
|
@ -136,10 +129,6 @@ if Gitlab::Runtime.puma?
|
|||
#
|
||||
# We do it again, for being extra safe,
|
||||
# but it should not be needed
|
||||
if PUMA_EXTERNAL_METRICS_SERVER
|
||||
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
||||
else
|
||||
Gitlab::Metrics::Exporter::WebExporter.instance.stop
|
||||
end
|
||||
::MetricsServer::PumaProcessSupervisor.instance.shutdown
|
||||
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
|
||||
|
||||
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.
|
||||
If interested, email the relevant community program team: `education@gitlab.com`, `opensource@gitlab.com`, or `startups@gitlab.com`.
|
||||
Because these Community Programs are free of cost, regular Priority Support is not included.
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
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
|
||||
to the shared group. This includes members who inherited group membership from a parent group.
|
||||
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 share a given group, for example, `Frontend` with another group, for example,
|
||||
`Engineering`:
|
||||
|
|
|
@ -538,6 +538,32 @@ Payload example:
|
|||
"iid": 1,
|
||||
"description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.",
|
||||
"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":{
|
||||
"name":"Gitlab Test",
|
||||
"description":"Aut reprehenderit ut est.",
|
||||
|
|
|
@ -43,10 +43,9 @@ module Gitlab
|
|||
if note.for_commit?
|
||||
data[:commit] = build_data_for_commit(project, user, note)
|
||||
elsif note.for_issue?
|
||||
data[:issue] = note.noteable.hook_attrs
|
||||
data[:issue][:labels] = note.noteable.labels_hook_attrs
|
||||
data[:issue] = Gitlab::HookData::IssueBuilder.new(note.noteable).build
|
||||
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?
|
||||
data[:snippet] = note.noteable.hook_attrs
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ module Gitlab
|
|||
event_type: event_type,
|
||||
user: user.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),
|
||||
changes: final_changes(changes.slice(*safe_keys)),
|
||||
# DEPRECATED
|
||||
|
|
|
@ -60,6 +60,7 @@ module Gitlab
|
|||
human_time_estimate: merge_request.human_time_estimate,
|
||||
assignee_ids: merge_request.assignee_ids,
|
||||
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
|
||||
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}."
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
|
@ -24566,16 +24572,16 @@ msgstr ""
|
|||
msgid "MrDeploymentActions|Stop environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "MrList|Assigned to %{name}, go to their profile."
|
||||
msgid "MrList|Assigned to %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "MrList|Attention requested from assignee %{name}, go to their profile."
|
||||
msgid "MrList|Attention requested from assignee %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "MrList|Attention requested from reviewer %{name}, go to their profile."
|
||||
msgid "MrList|Attention requested from reviewer %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "MrList|Review requested from %{name}, go to their profile."
|
||||
msgid "MrList|Review requested from %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-project"
|
||||
|
@ -32367,6 +32373,9 @@ msgstr ""
|
|||
msgid "Runners|Never contacted"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Never contacted:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|New group runners view"
|
||||
msgstr ""
|
||||
|
||||
|
@ -32388,12 +32397,18 @@ msgstr ""
|
|||
msgid "Runners|Offline runners"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Offline:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Online"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Online runners"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Online:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Pause from accepting jobs"
|
||||
msgstr ""
|
||||
|
||||
|
@ -32462,9 +32477,18 @@ msgstr ""
|
|||
msgid "Runners|Runner cannot be deleted, please contact your administrator"
|
||||
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"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
|
@ -32492,6 +32516,9 @@ msgstr ""
|
|||
msgid "Runners|Runner registration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Runner statuses"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Runner unassigned from project."
|
||||
msgstr ""
|
||||
|
||||
|
@ -32522,6 +32549,9 @@ msgstr ""
|
|||
msgid "Runners|Stale runners"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Stale:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Status"
|
||||
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?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|This runner has not run any jobs."
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|This runner is associated with specific projects."
|
||||
msgstr ""
|
||||
|
||||
|
@ -32618,9 +32651,6 @@ msgstr ""
|
|||
msgid "Runners|stale"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runner|This runner has not run any jobs."
|
||||
msgstr ""
|
||||
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33792,6 +33822,9 @@ msgstr ""
|
|||
msgid "SecurityReports|Severity"
|
||||
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."
|
||||
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
|
||||
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
|
||||
|
|
|
@ -70,7 +70,7 @@ RSpec.describe 'Merge requests > User mass updates', :js do
|
|||
it 'updates merge request with assignee' do
|
||||
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
|
||||
|
|
|
@ -2,11 +2,13 @@ import $ from 'jquery';
|
|||
import { nextTick } from 'vue';
|
||||
import '~/behaviors/markdown/render_gfm';
|
||||
import { GlTooltip, GlModal } from '@gitlab/ui';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import Description from '~/issues/show/components/description.vue';
|
||||
import { updateHistory } from '~/lib/utils/url_utility';
|
||||
import TaskList from '~/task_list';
|
||||
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
|
||||
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
|
||||
|
@ -17,6 +19,10 @@ import {
|
|||
} from '../mock_data/mock_data';
|
||||
|
||||
jest.mock('~/flash');
|
||||
jest.mock('~/lib/utils/url_utility', () => ({
|
||||
...jest.requireActual('~/lib/utils/url_utility'),
|
||||
updateHistory: jest.fn(),
|
||||
}));
|
||||
jest.mock('~/task_list');
|
||||
|
||||
const showModal = jest.fn();
|
||||
|
@ -55,6 +61,8 @@ describe('Description component', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
setWindowLocation(TEST_HOST);
|
||||
|
||||
if (!document.querySelector('.issuable-meta')) {
|
||||
const metaData = document.createElement('div');
|
||||
metaData.classList.add('issuable-meta');
|
||||
|
@ -285,47 +293,85 @@ describe('Description component', () => {
|
|||
describe('work items detail', () => {
|
||||
const findTaskLink = () => wrapper.find('a.gfm-issue');
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
props: {
|
||||
descriptionHtml: descriptionHtmlWithTask,
|
||||
},
|
||||
provide: {
|
||||
glFeatures: { workItems: true },
|
||||
},
|
||||
describe('when opening and closing', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
props: {
|
||||
descriptionHtml: descriptionHtmlWithTask,
|
||||
},
|
||||
provide: {
|
||||
glFeatures: { workItems: true },
|
||||
},
|
||||
});
|
||||
return nextTick();
|
||||
});
|
||||
return nextTick();
|
||||
});
|
||||
|
||||
it('opens when task button is clicked', async () => {
|
||||
expect(findWorkItemDetailModal().props('visible')).toBe(false);
|
||||
it('opens when task button is clicked', async () => {
|
||||
expect(findWorkItemDetailModal().props('visible')).toBe(false);
|
||||
|
||||
await findTaskLink().trigger('click');
|
||||
await findTaskLink().trigger('click');
|
||||
|
||||
expect(findWorkItemDetailModal().props('visible')).toBe(true);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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',
|
||||
expect(findWorkItemDetailModal().props('visible')).toBe(true);
|
||||
expect(updateHistory).toHaveBeenCalledWith({
|
||||
url: `${TEST_HOST}/?work_item_id=2`,
|
||||
replace: true,
|
||||
});
|
||||
});
|
||||
|
||||
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 SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
|
||||
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');
|
||||
|
||||
|
|
|
@ -41,7 +41,13 @@ import adminRunnersCountQuery from '~/runner/graphql/list/admin_runners_count.qu
|
|||
import { captureException } from '~/runner/sentry_utils';
|
||||
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 mockRunners = runnersData.data.runners.nodes;
|
||||
|
@ -94,6 +100,8 @@ describe('AdminRunnersApp', () => {
|
|||
},
|
||||
provide: {
|
||||
localMutations,
|
||||
onlineContactTimeoutSecs,
|
||||
staleTimeoutSecs,
|
||||
...provide,
|
||||
},
|
||||
...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';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
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 mockActiveRunnersCount = mockRunners.length;
|
||||
|
@ -28,21 +29,34 @@ describe('RunnerList', () => {
|
|||
activeRunnersCount: mockActiveRunnersCount,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
onlineContactTimeoutSecs,
|
||||
staleTimeoutSecs,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent({}, mountExtended);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('Displays headers', () => {
|
||||
createComponent(
|
||||
{
|
||||
stubs: {
|
||||
RunnerStatusPopover: {
|
||||
template: '<div/>',
|
||||
},
|
||||
},
|
||||
},
|
||||
mountExtended,
|
||||
);
|
||||
|
||||
const headerLabels = findHeaders().wrappers.map((w) => w.text());
|
||||
|
||||
expect(findHeaders().at(0).findComponent(RunnerStatusPopover).exists()).toBe(true);
|
||||
|
||||
expect(headerLabels).toEqual([
|
||||
'Status',
|
||||
'Runner',
|
||||
|
@ -61,6 +75,8 @@ describe('RunnerList', () => {
|
|||
});
|
||||
|
||||
it('Displays a list of runners', () => {
|
||||
createComponent({}, mountExtended);
|
||||
|
||||
expect(findRows()).toHaveLength(4);
|
||||
|
||||
expect(findSkeletonLoader().exists()).toBe(false);
|
||||
|
@ -69,6 +85,8 @@ describe('RunnerList', () => {
|
|||
it('Displays details of a runner', () => {
|
||||
const { id, description, version, shortSha } = mockRunners[0];
|
||||
|
||||
createComponent({}, mountExtended);
|
||||
|
||||
// Badges
|
||||
expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText(
|
||||
'never contacted paused',
|
||||
|
@ -183,6 +201,8 @@ describe('RunnerList', () => {
|
|||
const { id, shortSha } = mockRunners[0];
|
||||
const numericId = getIdFromGraphQLId(id);
|
||||
|
||||
createComponent({}, mountExtended);
|
||||
|
||||
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 { captureException } from '~/runner/sentry_utils';
|
||||
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(GlToast);
|
||||
|
@ -90,6 +96,10 @@ describe('GroupRunnersApp', () => {
|
|||
groupRunnersLimitedCount: mockGroupRunnersLimitedCount,
|
||||
...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 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 {
|
||||
runnersData,
|
||||
runnersCountData,
|
||||
|
|
|
@ -86,7 +86,9 @@ RSpec.describe Ci::RunnersHelper do
|
|||
it 'returns the data in format' do
|
||||
expect(helper.admin_runners_data_attributes).to eq({
|
||||
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
|
||||
|
@ -128,12 +130,14 @@ RSpec.describe Ci::RunnersHelper do
|
|||
let(:group) { create(:group) }
|
||||
|
||||
it 'returns group data to render a runner list' do
|
||||
data = helper.group_runners_data_attributes(group)
|
||||
|
||||
expect(data[:registration_token]).to eq(group.runners_token)
|
||||
expect(data[:group_id]).to eq(group.id)
|
||||
expect(data[:group_full_path]).to eq(group.full_path)
|
||||
expect(data[:runner_install_help_page]).to eq('https://docs.gitlab.com/runner/install/')
|
||||
expect(helper.group_runners_data_attributes(group)).to eq({
|
||||
registration_token: group.runners_token,
|
||||
group_id: group.id,
|
||||
group_full_path: group.full_path,
|
||||
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
|
||||
online_contact_timeout_secs: 7200,
|
||||
stale_timeout_secs: 7889238
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -425,16 +425,6 @@ RSpec.describe DiffHelper do
|
|||
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
|
||||
let(:params) do
|
||||
{
|
||||
|
|
|
@ -8,18 +8,22 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
let(:data) { described_class.build(note, user) }
|
||||
let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors
|
||||
|
||||
before do
|
||||
expect(data).to have_key(:object_attributes)
|
||||
expect(data[:object_attributes]).to have_key(:url)
|
||||
expect(data[:object_attributes][:url])
|
||||
.to eq(Gitlab::UrlBuilder.build(note))
|
||||
expect(data[:object_kind]).to eq('note')
|
||||
expect(data[:user]).to eq(user.hook_attrs)
|
||||
shared_examples 'includes general data' do
|
||||
specify do
|
||||
expect(data).to have_key(:object_attributes)
|
||||
expect(data[:object_attributes]).to have_key(:url)
|
||||
expect(data[:object_attributes][:url])
|
||||
.to eq(Gitlab::UrlBuilder.build(note))
|
||||
expect(data[:object_kind]).to eq('note')
|
||||
expect(data[:user]).to eq(user.hook_attrs)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'When asking for a note on commit' do
|
||||
let(:note) { create(:note_on_commit, project: project) }
|
||||
|
||||
it_behaves_like 'includes general data'
|
||||
|
||||
it 'returns the note and commit-specific data' do
|
||||
expect(data).to have_key(:commit)
|
||||
end
|
||||
|
@ -31,6 +35,8 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
describe 'When asking for a note on commit diff' do
|
||||
let(:note) { create(:diff_note_on_commit, project: project) }
|
||||
|
||||
it_behaves_like 'includes general data'
|
||||
|
||||
it 'returns the note and commit-specific data' do
|
||||
expect(data).to have_key(:commit)
|
||||
end
|
||||
|
@ -51,22 +57,21 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
create(:note_on_issue, noteable: issue, project: project)
|
||||
end
|
||||
|
||||
it 'returns the note and issue-specific data' do
|
||||
without_timestamps = lambda { |label| label.except('created_at', 'updated_at') }
|
||||
hook_attrs = issue.reload.hook_attrs
|
||||
it_behaves_like 'includes general data'
|
||||
|
||||
expect(data).to have_key(:issue)
|
||||
expect(data[:issue].except('updated_at', 'labels'))
|
||||
.to eq(hook_attrs.except('updated_at', 'labels'))
|
||||
expect(data[:issue]['updated_at'])
|
||||
.to be >= hook_attrs['updated_at']
|
||||
expect(data[:issue]['labels'].map(&without_timestamps))
|
||||
.to eq(hook_attrs['labels'].map(&without_timestamps))
|
||||
it 'returns the note and issue-specific data' do
|
||||
expect_next_instance_of(Gitlab::HookData::IssueBuilder) do |issue_data_builder|
|
||||
expect(issue_data_builder).to receive(:build).and_return('Issue data')
|
||||
end
|
||||
|
||||
expect(data[:issue]).to eq('Issue data')
|
||||
end
|
||||
|
||||
context 'with confidential issue' do
|
||||
let(:issue) { create(:issue, project: project, confidential: true) }
|
||||
|
||||
it_behaves_like 'includes general data'
|
||||
|
||||
it 'sets event_type to confidential_note' do
|
||||
expect(data[:event_type]).to eq('confidential_note')
|
||||
end
|
||||
|
@ -77,10 +82,12 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
end
|
||||
|
||||
describe 'When asking for a note on merge request' do
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:merge_request) do
|
||||
create(:merge_request, created_at: fixed_time,
|
||||
create(:labeled_merge_request, created_at: fixed_time,
|
||||
updated_at: fixed_time,
|
||||
source_project: project)
|
||||
source_project: project,
|
||||
labels: [label])
|
||||
end
|
||||
|
||||
let(:note) do
|
||||
|
@ -88,12 +95,14 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
project: project)
|
||||
end
|
||||
|
||||
it 'returns the note and merge request data' do
|
||||
expect(data).to have_key(:merge_request)
|
||||
expect(data[:merge_request].except('updated_at'))
|
||||
.to eq(merge_request.reload.hook_attrs.except('updated_at'))
|
||||
expect(data[:merge_request]['updated_at'])
|
||||
.to be >= merge_request.hook_attrs['updated_at']
|
||||
it_behaves_like 'includes general data'
|
||||
|
||||
it 'returns the merge request data' do
|
||||
expect_next_instance_of(Gitlab::HookData::MergeRequestBuilder) do |mr_data_builder|
|
||||
expect(mr_data_builder).to receive(:build).and_return('MR data')
|
||||
end
|
||||
|
||||
expect(data[:merge_request]).to eq('MR data')
|
||||
end
|
||||
|
||||
include_examples 'project hook data'
|
||||
|
@ -101,9 +110,11 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
end
|
||||
|
||||
describe 'When asking for a note on merge request diff' do
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:merge_request) do
|
||||
create(:merge_request, created_at: fixed_time, updated_at: fixed_time,
|
||||
source_project: project)
|
||||
create(:labeled_merge_request, created_at: fixed_time, updated_at: fixed_time,
|
||||
source_project: project,
|
||||
labels: [label])
|
||||
end
|
||||
|
||||
let(:note) do
|
||||
|
@ -111,12 +122,14 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
project: project)
|
||||
end
|
||||
|
||||
it 'returns the note and merge request diff data' do
|
||||
expect(data).to have_key(:merge_request)
|
||||
expect(data[:merge_request].except('updated_at'))
|
||||
.to eq(merge_request.reload.hook_attrs.except('updated_at'))
|
||||
expect(data[:merge_request]['updated_at'])
|
||||
.to be >= merge_request.hook_attrs['updated_at']
|
||||
it_behaves_like 'includes general data'
|
||||
|
||||
it 'returns the merge request data' do
|
||||
expect_next_instance_of(Gitlab::HookData::MergeRequestBuilder) do |mr_data_builder|
|
||||
expect(mr_data_builder).to receive(:build).and_return('MR data')
|
||||
end
|
||||
|
||||
expect(data[:merge_request]).to eq('MR data')
|
||||
end
|
||||
|
||||
include_examples 'project hook data'
|
||||
|
@ -134,6 +147,8 @@ RSpec.describe Gitlab::DataBuilder::Note do
|
|||
project: project)
|
||||
end
|
||||
|
||||
it_behaves_like 'includes general data'
|
||||
|
||||
it 'returns the note and project snippet data' do
|
||||
expect(data).to have_key(:snippet)
|
||||
expect(data[:snippet].except('updated_at'))
|
||||
|
|
|
@ -6,7 +6,7 @@ RSpec.describe Gitlab::HookData::IssuableBuilder do
|
|||
let_it_be(:user) { create(:user) }
|
||||
|
||||
# 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) }
|
||||
|
||||
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[: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[:object_attributes]).to eq(hook_data_issuable_builder_class.new(issuable).build)
|
||||
expect(data[:changes]).to eq({})
|
||||
expect(data[:repository]).to eq(builder.issuable.project.hook_attrs.slice(:name, :url, :description, :homepage))
|
||||
end
|
||||
|
@ -95,12 +95,12 @@ RSpec.describe Gitlab::HookData::IssuableBuilder do
|
|||
end
|
||||
|
||||
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(:builder) { described_class.new(issuable) }
|
||||
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(:builder) { described_class.new(issuable) }
|
||||
end
|
||||
|
|
|
@ -62,6 +62,7 @@ RSpec.describe Gitlab::HookData::MergeRequestBuilder do
|
|||
expect(data).to include(:human_time_estimate)
|
||||
expect(data).to include(:human_total_time_spent)
|
||||
expect(data).to include(:human_time_change)
|
||||
expect(data).to include(:labels)
|
||||
end
|
||||
|
||||
context 'when the MR has an image in the description' do
|
||||
|
|
|
@ -625,6 +625,16 @@ RSpec.describe Issuable do
|
|||
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
|
||||
let(:feature_label) { create(:label, title: 'Feature') }
|
||||
let(:second_label) { create(:label, title: 'Second Label') }
|
||||
|
|
|
@ -1172,18 +1172,6 @@ RSpec.describe Issue do
|
|||
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
|
||||
let_it_be(:support_bot) { ::User.support_bot }
|
||||
|
||||
|
@ -1332,15 +1320,6 @@ RSpec.describe Issue do
|
|||
subject { create(:issue, updated_at: 1.hour.ago) }
|
||||
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
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
|
|
|
@ -1757,18 +1757,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
|
|||
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
|
||||
let(:project) { create(:project, :repository) }
|
||||
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)
|
||||
end.to change { project.reload.import_data }.from(import_data).to(nil)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
include_context 'container registry auth service context'
|
||||
|
||||
|
@ -544,38 +568,14 @@ RSpec.shared_examples 'a container registry auth service' do
|
|||
end
|
||||
|
||||
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(:authentication_abilities) do
|
||||
[:admin_container_image]
|
||||
end
|
||||
|
||||
before_all do
|
||||
current_project.add_maintainer(current_user)
|
||||
project.add_maintainer(current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'a valid token'
|
||||
|
||||
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
|
||||
it_behaves_like 'allowed to delete container repository images'
|
||||
end
|
||||
|
||||
context 'build authorized as user' do
|
||||
|
|
Loading…
Reference in New Issue