summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-03-31 15:08:32 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-31 15:08:32 +0000
commit85ea3dd4f4855e99d9a69c8e609095199597195a (patch)
tree46e74d30d0ef5cced04005b4a76c66ec18f6a749
parent5820d448c17f93606afb52d878c00d84681764e0 (diff)
downloadgitlab-ce-85ea3dd4f4855e99d9a69c8e609095199597195a.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue19
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail/description.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue20
-rw-r--r--app/assets/javascripts/notes/components/toggle_replies_widget.vue2
-rw-r--r--app/assets/javascripts/pages/admin/groups/new/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue3
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js6
-rw-r--r--app/assets/javascripts/pages/projects/tags/new/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js4
-rw-r--r--app/assets/javascripts/runner/admin_runners/index.js9
-rw-r--r--app/assets/javascripts/runner/components/runner_list.vue7
-rw-r--r--app/assets/javascripts/runner/components/runner_status_popover.vue75
-rw-r--r--app/assets/javascripts/runner/constants.js22
-rw-r--r--app/assets/javascripts/runner/group_runners/index.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue2
-rw-r--r--app/assets/stylesheets/notify_enhanced.scss12
-rw-r--r--app/components/diffs/base_component.rb10
-rw-r--r--app/components/diffs/stats_component.html.haml1
-rw-r--r--app/components/diffs/stats_component.rb67
-rw-r--r--app/components/pajamas/alert_component.html.haml13
-rw-r--r--app/components/pajamas/alert_component.rb45
-rw-r--r--app/controllers/projects/packages/infrastructure_registry_controller.rb1
-rw-r--r--app/helpers/ci/runners_helper.rb8
-rw-r--r--app/helpers/diff_helper.rb43
-rw-r--r--app/models/concerns/issuable.rb4
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/project_import_state.rb4
-rw-r--r--app/views/layouts/header/_registration_enabled_callout.html.haml8
-rw-r--r--app/views/layouts/header/_storage_enforcement_banner.html.haml7
-rw-r--r--app/views/notify/_note_email.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.html.haml23
-rw-r--r--app/views/notify/service_desk_new_note_email.html.haml2
-rw-r--r--app/views/projects/diffs/_diffs.html.haml2
-rw-r--r--app/views/projects/diffs/_stats.html.haml1
-rw-r--r--app/views/shared/issuable/_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/_merge_request_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/_merge_request_reviewers.html.haml2
-rw-r--r--app/views/shared/issuable/_reviewers.html.haml2
-rw-r--r--config/feature_flags/development/vulnerability_report_page_size_selector.yml (renamed from config/feature_flags/development/remove_import_data_on_failure.yml)12
-rw-r--r--config/initializers/7_prometheus_metrics.rb39
-rw-r--r--doc/subscriptions/img/support-diagram.pngbin49941 -> 0 bytes
-rw-r--r--doc/subscriptions/img/support_diagram_c.pngbin0 -> 161323 bytes
-rw-r--r--doc/subscriptions/index.md5
-rw-r--r--doc/user/group/index.md4
-rw-r--r--doc/user/project/integrations/webhook_events.md26
-rw-r--r--lib/gitlab/data_builder/note.rb5
-rw-r--r--lib/gitlab/hook_data/issuable_builder.rb2
-rw-r--r--lib/gitlab/hook_data/merge_request_builder.rb1
-rw-r--r--locale/gitlab.pot47
-rw-r--r--spec/components/diffs/stats_component_spec.rb67
-rw-r--r--spec/components/pajamas/alert_component_spec.rb104
-rw-r--r--spec/controllers/projects/packages/infrastructure_registry_controller_spec.rb12
-rw-r--r--spec/features/merge_requests/user_mass_updates_spec.rb2
-rw-r--r--spec/frontend/issues/show/components/description_spec.js104
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js2
-rw-r--r--spec/frontend/runner/admin_runners/admin_runners_app_spec.js10
-rw-r--r--spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap3
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js30
-rw-r--r--spec/frontend/runner/components/runner_status_popover_spec.js36
-rw-r--r--spec/frontend/runner/group_runners/group_runners_app_spec.js12
-rw-r--r--spec/frontend/runner/mock_data.js4
-rw-r--r--spec/helpers/ci/runners_helper_spec.rb18
-rw-r--r--spec/helpers/diff_helper_spec.rb10
-rw-r--r--spec/lib/gitlab/data_builder/note_spec.rb79
-rw-r--r--spec/lib/gitlab/hook_data/issuable_builder_spec.rb8
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb1
-rw-r--r--spec/models/concerns/issuable_spec.rb10
-rw-r--r--spec/models/issue_spec.rb21
-rw-r--r--spec/models/merge_request_spec.rb12
-rw-r--r--spec/models/project_import_state_spec.rb13
-rw-r--r--spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb61
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb54
72 files changed, 869 insertions, 399 deletions
diff --git a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue
index 4156717908d..5e5d799d627 100644
--- a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue
+++ b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue
@@ -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" />
diff --git a/app/assets/javascripts/ide/components/jobs/detail/description.vue b/app/assets/javascripts/ide/components/jobs/detail/description.vue
index 9eaeabad5ef..8fd1973267c 100644
--- a/app/assets/javascripts/ide/components/jobs/detail/description.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail/description.vue
@@ -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: {
diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue
index 996bf307b8c..d75f686051d 100644
--- a/app/assets/javascripts/issues/show/components/description.vue
+++ b/app/assets/javascripts/issues/show/components/description.vue
@@ -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"
diff --git a/app/assets/javascripts/notes/components/toggle_replies_widget.vue b/app/assets/javascripts/notes/components/toggle_replies_widget.vue
index 01e3f84d00e..65b3fd6f8b3 100644
--- a/app/assets/javascripts/notes/components/toggle_replies_widget.vue
+++ b/app/assets/javascripts/notes/components/toggle_replies_widget.vue
@@ -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"
/>
diff --git a/app/assets/javascripts/pages/admin/groups/new/index.js b/app/assets/javascripts/pages/admin/groups/new/index.js
index 1630cfb8253..710d2d72f4c 100644
--- a/app/assets/javascripts/pages/admin/groups/new/index.js
+++ b/app/assets/javascripts/pages/admin/groups/new/index.js
@@ -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();
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
index ee70ff858be..37e8a316ee4 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
@@ -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);
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
index 9c039a6be81..88ff05b20a0 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
@@ -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';
diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js
index b071e7a45fc..9ef1017f9f2 100644
--- a/app/assets/javascripts/pages/projects/tags/new/index.js
+++ b/app/assets/javascripts/pages/projects/tags/new/index.js
@@ -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
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index 4bb461aadad..cf7162f477d 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -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();
diff --git a/app/assets/javascripts/runner/admin_runners/index.js b/app/assets/javascripts/runner/admin_runners/index.js
index 2405bab7957..12e2cb2ee9f 100644
--- a/app/assets/javascripts/runner/admin_runners/index.js
+++ b/app/assets/javascripts/runner/admin_runners/index.js
@@ -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, {
diff --git a/app/assets/javascripts/runner/components/runner_list.vue b/app/assets/javascripts/runner/components/runner_list.vue
index 05cd1879d9d..dcfd4b84dd2 100644
--- a/app/assets/javascripts/runner/components/runner_list.vue
+++ b/app/assets/javascripts/runner/components/runner_list.vue
@@ -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>
diff --git a/app/assets/javascripts/runner/components/runner_status_popover.vue b/app/assets/javascripts/runner/components/runner_status_popover.vue
new file mode 100644
index 00000000000..5b22f7828a1
--- /dev/null
+++ b/app/assets/javascripts/runner/components/runner_status_popover.vue
@@ -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>
diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js
index de0196e98a2..165968558c5 100644
--- a/app/assets/javascripts/runner/constants.js
+++ b/app/assets/javascripts/runner/constants.js
@@ -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
diff --git a/app/assets/javascripts/runner/group_runners/index.js b/app/assets/javascripts/runner/group_runners/index.js
index 60b7a7ab541..0dade30f820 100644
--- a/app/assets/javascripts/runner/group_runners/index.js
+++ b/app/assets/javascripts/runner/group_runners/index.js
@@ -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, {
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
index af85a2fda06..f28a2801bc0 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
@@ -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: {
diff --git a/app/assets/stylesheets/notify_enhanced.scss b/app/assets/stylesheets/notify_enhanced.scss
index 5df5a8592bf..5aa0fd3cd00 100644
--- a/app/assets/stylesheets/notify_enhanced.scss
+++ b/app/assets/stylesheets/notify_enhanced.scss
@@ -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;
}
diff --git a/app/components/diffs/base_component.rb b/app/components/diffs/base_component.rb
new file mode 100644
index 00000000000..9e1347d1e84
--- /dev/null
+++ b/app/components/diffs/base_component.rb
@@ -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
diff --git a/app/components/diffs/stats_component.html.haml b/app/components/diffs/stats_component.html.haml
new file mode 100644
index 00000000000..c0e816639a7
--- /dev/null
+++ b/app/components/diffs/stats_component.html.haml
@@ -0,0 +1 @@
+.js-diff-stats-dropdown{ data: { changed: @changed, added: @added, deleted: @removed, files: diff_files_data } }
diff --git a/app/components/diffs/stats_component.rb b/app/components/diffs/stats_component.rb
new file mode 100644
index 00000000000..55589c7b015
--- /dev/null
+++ b/app/components/diffs/stats_component.rb
@@ -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
diff --git a/app/components/pajamas/alert_component.html.haml b/app/components/pajamas/alert_component.html.haml
new file mode 100644
index 00000000000..a1d3c700e57
--- /dev/null
+++ b/app/components/pajamas/alert_component.html.haml
@@ -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
diff --git a/app/components/pajamas/alert_component.rb b/app/components/pajamas/alert_component.rb
new file mode 100644
index 00000000000..4bb6c41661b
--- /dev/null
+++ b/app/components/pajamas/alert_component.rb
@@ -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
diff --git a/app/controllers/projects/packages/infrastructure_registry_controller.rb b/app/controllers/projects/packages/infrastructure_registry_controller.rb
index 2fe353b7acb..99d75afc63a 100644
--- a/app/controllers/projects/packages/infrastructure_registry_controller.rb
+++ b/app/controllers/projects/packages/infrastructure_registry_controller.rb
@@ -9,7 +9,6 @@ module Projects
def show
@package = project.packages.find(params[:id])
- @package_files = @package.installable_package_files.recent
end
end
end
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index 28e4056daac..0e8b6fa6d25 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -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
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 2aabf0febdf..522593dd487 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -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
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 1eb30e88f16..729170da242 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -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:
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 5c91656cbc0..7bb388ed4dc 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -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
diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb
index 0f04eb7d4af..42914b0d668 100644
--- a/app/models/project_import_state.rb
+++ b/app/models/project_import_state.rb
@@ -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, _|
diff --git a/app/views/layouts/header/_registration_enabled_callout.html.haml b/app/views/layouts/header/_registration_enabled_callout.html.haml
index d1d23c86c81..affee15c4d0 100644
--- a/app/views/layouts/header/_registration_enabled_callout.html.haml
+++ b/app/views/layouts/header/_registration_enabled_callout.html.haml
@@ -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
diff --git a/app/views/layouts/header/_storage_enforcement_banner.html.haml b/app/views/layouts/header/_storage_enforcement_banner.html.haml
index 851fc57e44d..92c02d6ecfd 100644
--- a/app/views/layouts/header/_storage_enforcement_banner.html.haml
+++ b/app/views/layouts/header/_storage_enforcement_banner.html.haml
@@ -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]
diff --git a/app/views/notify/_note_email.html.haml b/app/views/notify/_note_email.html.haml
index f03e524a0f3..d5398337320 100644
--- a/app/views/notify/_note_email.html.haml
+++ b/app/views/notify/_note_email.html.haml
@@ -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,
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index f67ac5f8fb2..1542d5bba85 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -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)
diff --git a/app/views/notify/service_desk_new_note_email.html.haml b/app/views/notify/service_desk_new_note_email.html.haml
index 0c16cf3315f..02f6b3914c9 100644
--- a/app/views/notify/service_desk_new_note_email.html.haml
+++ b/app/views/notify/service_desk_new_note_email.html.haml
@@ -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)
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index bb2682bb7c0..7e3f9c7664e 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -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
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
deleted file mode 100644
index fe9658a440a..00000000000
--- a/app/views/projects/diffs/_stats.html.haml
+++ /dev/null
@@ -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) } }
diff --git a/app/views/shared/issuable/_assignees.html.haml b/app/views/shared/issuable/_assignees.html.haml
index e6d722cb08d..73f1e35f03f 100644
--- a/app/views/shared/issuable/_assignees.html.haml
+++ b/app/views/shared/issuable/_assignees.html.haml
@@ -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} }
diff --git a/app/views/shared/issuable/_merge_request_assignees.html.haml b/app/views/shared/issuable/_merge_request_assignees.html.haml
index 13dc6ae4abb..6c7a2496ec6 100644
--- a/app/views/shared/issuable/_merge_request_assignees.html.haml
+++ b/app/views/shared/issuable/_merge_request_assignees.html.haml
@@ -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?
diff --git a/app/views/shared/issuable/_merge_request_reviewers.html.haml b/app/views/shared/issuable/_merge_request_reviewers.html.haml
index df5c69e309f..8dd74e12aff 100644
--- a/app/views/shared/issuable/_merge_request_reviewers.html.haml
+++ b/app/views/shared/issuable/_merge_request_reviewers.html.haml
@@ -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?
diff --git a/app/views/shared/issuable/_reviewers.html.haml b/app/views/shared/issuable/_reviewers.html.haml
index 0bb0faa0bb8..4af2cb00859 100644
--- a/app/views/shared/issuable/_reviewers.html.haml
+++ b/app/views/shared/issuable/_reviewers.html.haml
@@ -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} }
diff --git a/config/feature_flags/development/remove_import_data_on_failure.yml b/config/feature_flags/development/vulnerability_report_page_size_selector.yml
index 341e027f28b..b410ee27adb 100644
--- a/config/feature_flags/development/remove_import_data_on_failure.yml
+++ b/config/feature_flags/development/vulnerability_report_page_size_selector.yml
@@ -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
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index 0d874f5bebc..6953de670a7 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -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
diff --git a/doc/subscriptions/img/support-diagram.png b/doc/subscriptions/img/support-diagram.png
deleted file mode 100644
index 1628a62f19a..00000000000
--- a/doc/subscriptions/img/support-diagram.png
+++ /dev/null
Binary files differ
diff --git a/doc/subscriptions/img/support_diagram_c.png b/doc/subscriptions/img/support_diagram_c.png
new file mode 100644
index 00000000000..a2fed80e912
--- /dev/null
+++ b/doc/subscriptions/img/support_diagram_c.png
Binary files differ
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index fbeeceb9dbb..824fe0bacde 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -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
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index a93fd9927b0..e748f49c0ea 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -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`:
diff --git a/doc/user/project/integrations/webhook_events.md b/doc/user/project/integrations/webhook_events.md
index 53c56a7c9af..2bf6b4bbe01 100644
--- a/doc/user/project/integrations/webhook_events.md
+++ b/doc/user/project/integrations/webhook_events.md
@@ -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.",
diff --git a/lib/gitlab/data_builder/note.rb b/lib/gitlab/data_builder/note.rb
index 73518d36d43..dec583f5a42 100644
--- a/lib/gitlab/data_builder/note.rb
+++ b/lib/gitlab/data_builder/note.rb
@@ -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
diff --git a/lib/gitlab/hook_data/issuable_builder.rb b/lib/gitlab/hook_data/issuable_builder.rb
index 5c8aa5050ed..c4e27bf424f 100644
--- a/lib/gitlab/hook_data/issuable_builder.rb
+++ b/lib/gitlab/hook_data/issuable_builder.rb
@@ -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
diff --git a/lib/gitlab/hook_data/merge_request_builder.rb b/lib/gitlab/hook_data/merge_request_builder.rb
index aaca16d8d7c..06ddd65d075 100644
--- a/lib/gitlab/hook_data/merge_request_builder.rb
+++ b/lib/gitlab/hook_data/merge_request_builder.rb
@@ -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?
}
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c6af956f4d7..dd6dab41ca8 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/spec/components/diffs/stats_component_spec.rb b/spec/components/diffs/stats_component_spec.rb
new file mode 100644
index 00000000000..2e5a5f2ca26
--- /dev/null
+++ b/spec/components/diffs/stats_component_spec.rb
@@ -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
diff --git a/spec/components/pajamas/alert_component_spec.rb b/spec/components/pajamas/alert_component_spec.rb
new file mode 100644
index 00000000000..628d715ff64
--- /dev/null
+++ b/spec/components/pajamas/alert_component_spec.rb
@@ -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
diff --git a/spec/controllers/projects/packages/infrastructure_registry_controller_spec.rb b/spec/controllers/projects/packages/infrastructure_registry_controller_spec.rb
index a655c742973..fc741d0f3f6 100644
--- a/spec/controllers/projects/packages/infrastructure_registry_controller_spec.rb
+++ b/spec/controllers/projects/packages/infrastructure_registry_controller_spec.rb
@@ -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
diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index f781ba0827c..a15b6072e70 100644
--- a/spec/features/merge_requests/user_mass_updates_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -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
diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js
index 6f6f50b89bc..b96dfc00c11 100644
--- a/spec/frontend/issues/show/components/description_spec.js
+++ b/spec/frontend/issues/show/components/description_spec.js
@@ -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,48 +293,86 @@ 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);
- });
+ 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');
+ it('closes from an open state', async () => {
+ await findTaskLink().trigger('click');
- expect(findWorkItemDetailModal().props('visible')).toBe(true);
+ expect(findWorkItemDetailModal().props('visible')).toBe(true);
- findWorkItemDetailModal().vm.$emit('close');
- await nextTick();
+ findWorkItemDetailModal().vm.$emit('close');
+ await nextTick();
- expect(findWorkItemDetailModal().props('visible')).toBe(false);
- });
+ 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);
+ it('tracks when opened', async () => {
+ const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- await findTaskLink().trigger('click');
+ 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(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);
+ },
+ );
+ });
});
});
});
diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js
index 175896c4ab0..97d1b077164 100644
--- a/spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in_spec.js
@@ -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');
diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
index edc25eb7e02..f8011860a49 100644
--- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
@@ -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,
diff --git a/spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap b/spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap
new file mode 100644
index 00000000000..80a04401760
--- /dev/null
+++ b/spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap
@@ -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"`;
diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js
index fab94771990..872394430ae 100644
--- a/spec/frontend/runner/components/runner_list_spec.js
+++ b/spec/frontend/runner/components/runner_list_spec.js
@@ -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})`);
});
diff --git a/spec/frontend/runner/components/runner_status_popover_spec.js b/spec/frontend/runner/components/runner_status_popover_spec.js
new file mode 100644
index 00000000000..789283d1245
--- /dev/null
+++ b/spec/frontend/runner/components/runner_status_popover_spec.js
@@ -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();
+ });
+});
diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js
index 6d7ecc4506a..706f46a7c56 100644
--- a/spec/frontend/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js
@@ -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,
+ },
});
};
diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js
index 49c25039719..fbe8926124c 100644
--- a/spec/frontend/runner/mock_data.js
+++ b/spec/frontend/runner/mock_data.js
@@ -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,
diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb
index dd1804e2078..0046d481282 100644
--- a/spec/helpers/ci/runners_helper_spec.rb
+++ b/spec/helpers/ci/runners_helper_spec.rb
@@ -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
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index ccc2db00236..84e702cd6a9 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -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
{
diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb
index 90ca5430526..3fa535dd800 100644
--- a/spec/lib/gitlab/data_builder/note_spec.rb
+++ b/spec/lib/gitlab/data_builder/note_spec.rb
@@ -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_behaves_like 'includes general data'
+
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
+ 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).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))
+ 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'))
diff --git a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb b/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
index f5ee8eba8bc..676396697fb 100644
--- a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index ddd681f75f0..771fc0218e2 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -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
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index e3c0e3a7a2b..b38135fc0b2 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -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') }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 61ad9dc26be..fe09dadd0db 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -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) }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 0d15851e583..9d734e85668 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -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) }
diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb
index 4ad2446f8d0..6d84b2c6931 100644
--- a/spec/models/project_import_state_spec.rb
+++ b/spec/models/project_import_state_spec.rb
@@ -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
diff --git a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb b/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb
deleted file mode 100644
index 13ffc1b7f87..00000000000
--- a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb
+++ /dev/null
@@ -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
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index 95471203983..7677e5d8cb2 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -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)
- 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
+ project.add_maintainer(current_user)
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