Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_todo.yml5
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue12
-rw-r--r--app/assets/javascripts/api.js3
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js15
-rw-r--r--app/assets/javascripts/header.js24
-rw-r--r--app/assets/javascripts/issue_show/components/header_actions.vue4
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue18
-rw-r--r--app/assets/javascripts/pages/groups/details/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/labels/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/labels/index/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/labels/new/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js18
-rw-r--r--app/assets/javascripts/set_status_modal/components/user_availability_status.vue26
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue61
-rw-r--r--app/assets/javascripts/set_status_modal/utils.js9
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue9
-rw-r--r--app/helpers/page_layout_helper.rb11
-rw-r--r--app/helpers/profiles_helper.rb14
-rw-r--r--app/models/pages/lookup_path.rb36
-rw-r--r--app/services/concerns/users/participable_service.rb3
-rw-r--r--app/uploaders/gitlab_uploader.rb8
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml9
-rw-r--r--app/views/layouts/header/_default.html.haml3
-rw-r--r--app/views/profiles/show.html.haml10
-rw-r--r--app/views/projects/_merge_request_merge_options_settings.html.haml1
-rw-r--r--app/views/users/show.html.haml4
-rw-r--r--changelogs/unreleased/276185_alerts_integrations_list_tracking.yml5
-rw-r--r--changelogs/unreleased/ak-fix-chatbot-replies.yml5
-rw-r--r--config/feature_flags/development/push_rules_supersede_code_owners.yml7
-rw-r--r--config/feature_flags/development/set_user_availability_status.yml8
-rw-r--r--db/migrate/20201112195322_reseed_merge_trains_enabled.rb19
-rw-r--r--db/schema_migrations/202011121953221
-rw-r--r--doc/administration/auth/smartcard.md4
-rw-r--r--doc/administration/compliance.md4
-rw-r--r--doc/administration/database_load_balancing.md4
-rw-r--r--doc/administration/object_storage.md4
-rw-r--r--doc/administration/operations/extra_sidekiq_processes.md4
-rw-r--r--doc/administration/pages/index.md3
-rw-r--r--doc/administration/raketasks/doctor.md4
-rw-r--r--doc/administration/raketasks/geo.md4
-rw-r--r--doc/administration/raketasks/ldap.md4
-rw-r--r--doc/administration/troubleshooting/elasticsearch.md4
-rw-r--r--doc/administration/troubleshooting/group_saml_scim.md4
-rw-r--r--doc/api/appearance.md4
-rw-r--r--doc/api/dependency_proxy.md4
-rw-r--r--doc/api/epic_links.md4
-rw-r--r--doc/api/geo_nodes.md4
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql32
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json82
-rw-r--r--doc/api/graphql/reference/index.md18
-rw-r--r--doc/api/group_activity_analytics.md4
-rw-r--r--doc/api/group_import_export.md4
-rw-r--r--doc/api/notes.md4
-rw-r--r--doc/api/personal_access_tokens.md4
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md2
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md21
-rw-r--r--doc/ci/unit_test_reports.md17
-rw-r--r--doc/development/approval_rules.md4
-rw-r--r--doc/development/creating_enums.md4
-rw-r--r--doc/development/database/setting_multiple_values.md4
-rw-r--r--doc/development/experiment_guide/index.md4
-rw-r--r--doc/development/feature_categorization/index.md4
-rw-r--r--doc/development/foreign_keys.md4
-rw-r--r--doc/development/graphql_guide/batchloader.md4
-rw-r--r--doc/development/hash_indexes.md4
-rw-r--r--doc/development/i18n/externalization.md4
-rw-r--r--doc/development/i18n/index.md4
-rw-r--r--doc/development/i18n/merging_translations.md4
-rw-r--r--doc/development/i18n/proofreader.md4
-rw-r--r--doc/development/i18n/translation.md4
-rw-r--r--doc/development/image_scaling.md4
-rw-r--r--doc/development/import_export.md4
-rw-r--r--doc/development/insert_into_tables_in_batches.md4
-rw-r--r--doc/development/integrations/secure.md4
-rw-r--r--doc/development/integrations/secure_partner_integration.md4
-rw-r--r--doc/development/iterating_tables_in_batches.md4
-rw-r--r--doc/development/lfs.md4
-rw-r--r--doc/development/new_fe_guide/modules/widget_extensions.md4
-rw-r--r--doc/development/new_fe_guide/tips.md2
-rw-r--r--doc/development/ordering_table_columns.md4
-rw-r--r--doc/development/permissions.md4
-rw-r--r--doc/development/policies.md4
-rw-r--r--doc/development/serializing_data.md4
-rw-r--r--doc/development/sql.md4
-rw-r--r--doc/development/testing_guide/end_to_end/best_practices.md2
-rw-r--r--doc/development/understanding_explain_plans.md4
-rw-r--r--doc/development/value_stream_analytics.md4
-rw-r--r--doc/development/verifying_database_capabilities.md4
-rw-r--r--doc/development/what_requires_downtime.md4
-rw-r--r--doc/development/wikis.md6
-rw-r--r--doc/user/analytics/value_stream_analytics.md2
-rw-r--r--doc/user/project/integrations/webex_teams.md6
-rw-r--r--doc/user/search/index.md1
-rw-r--r--lib/gitlab/chat/output.rb44
-rw-r--r--lib/gitlab/octokit/middleware.rb2
-rw-r--r--locale/gitlab.pot38
-rw-r--r--qa/qa/page/component/issuable/sidebar.rb6
-rw-r--r--rubocop/cop/line_break_around_conditional_block.rb132
-rw-r--r--spec/controllers/concerns/metrics_dashboard_spec.rb1
-rw-r--r--spec/features/groups/milestone_spec.rb1
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb77
-rw-r--r--spec/finders/merge_requests/by_approvals_finder_spec.rb1
-rw-r--r--spec/fixtures/api/schemas/internal/pages/lookup_path.json6
-rw-r--r--spec/frontend/alerts_settings/alerts_integrations_list_spec.js17
-rw-r--r--spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js2
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js17
-rw-r--r--spec/frontend/notes/components/note_header_spec.js12
-rw-r--r--spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js257
-rw-r--r--spec/frontend/set_status_modal/user_availability_status_spec.js31
-rw-r--r--spec/frontend/vue_shared/components/user_popover/user_popover_spec.js30
-rw-r--r--spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb1
-rw-r--r--spec/helpers/markup_helper_spec.rb1
-rw-r--r--spec/helpers/page_layout_helper_spec.rb38
-rw-r--r--spec/helpers/profiles_helper_spec.rb32
-rw-r--r--spec/helpers/todos_helper_spec.rb2
-rw-r--r--spec/lib/banzai/reference_parser/design_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/chat/output_spec.rb99
-rw-r--r--spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb1
-rw-r--r--spec/lib/gitlab/relative_positioning/mover_spec.rb1
-rw-r--r--spec/migrations/reseed_merge_trains_enabled_spec.rb26
-rw-r--r--spec/models/pages/lookup_path_spec.rb56
-rw-r--r--spec/requests/api/admin/instance_clusters_spec.rb1
-rw-r--r--spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb1
-rw-r--r--spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb1
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/version_spec.rb1
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb3
-rw-r--r--spec/requests/api/graphql/project/jira_import_spec.rb2
-rw-r--r--spec/requests/api/graphql/user/group_member_query_spec.rb1
-rw-r--r--spec/requests/api/graphql/user/project_member_query_spec.rb1
-rw-r--r--spec/requests/api/graphql/user_query_spec.rb5
-rw-r--r--spec/requests/api/internal/pages_spec.rb21
-rw-r--r--spec/requests/api/issues/get_project_issues_spec.rb2
-rw-r--r--spec/requests/api/issues/issues_spec.rb2
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb2
-rw-r--r--spec/requests/projects/metrics/dashboards/builder_spec.rb1
-rw-r--r--spec/rubocop/cop/line_break_around_conditional_block_spec.rb454
-rw-r--r--spec/services/admin/propagate_integration_service_spec.rb1
-rw-r--r--spec/services/alert_management/create_alert_issue_service_spec.rb1
-rw-r--r--spec/services/metrics/dashboard/panel_preview_service_spec.rb1
-rw-r--r--spec/services/notes/destroy_service_spec.rb1
-rw-r--r--spec/services/notification_service_spec.rb1
-rw-r--r--spec/support/shared_contexts/design_management_shared_contexts.rb2
-rw-r--r--spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb3
-rw-r--r--spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb1
-rw-r--r--spec/uploaders/gitlab_uploader_spec.rb18
-rw-r--r--tooling/overcommit/Gemfile2
-rw-r--r--tooling/overcommit/Gemfile.lock6
151 files changed, 1343 insertions, 880 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index ba6ee058218..293c8fccf33 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -359,11 +359,6 @@ RSpec/EmptyExampleGroup:
- 'ee/spec/services/personal_access_tokens/revoke_invalid_tokens_spec.rb'
- 'spec/services/projects/prometheus/alerts/notify_service_spec.rb'
-# Offense count: 176
-# Cop supports --auto-correct.
-RSpec/EmptyLineAfterLetBlock:
- Enabled: false
-
# Offense count: 1310
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index fbd86ec935f..4904b85cbd2 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-4a6451746c2e3cd3e6aab83f14495384fc8a9cae
+b8a027e4ea96235545000da5ff35d05a91b4370b
diff --git a/Gemfile b/Gemfile
index 6acafa0cf59..0833710d8db 100644
--- a/Gemfile
+++ b/Gemfile
@@ -370,7 +370,7 @@ group :development, :test do
gem 'spring', '~> 2.1.0'
gem 'spring-commands-rspec', '~> 1.0.4'
- gem 'gitlab-styles', '~> 5.0.0', require: false
+ gem 'gitlab-styles', '~> 5.1.0', require: false
gem 'scss_lint', '~> 0.59.0', require: false
gem 'haml_lint', '~> 0.36.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 58f5f7753ec..c95223b8ff1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -449,7 +449,7 @@ GEM
gitlab-puma (>= 2.7, < 5)
gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5)
- gitlab-styles (5.0.0)
+ gitlab-styles (5.1.0)
rubocop (~> 0.89.1)
rubocop-gitlab-security (~> 0.1.0)
rubocop-performance (~> 1.8.1)
@@ -1355,7 +1355,7 @@ DEPENDENCIES
gitlab-puma (~> 4.3.3.gitlab.2)
gitlab-puma_worker_killer (~> 0.1.1.gitlab.1)
gitlab-sidekiq-fetcher (= 0.5.2)
- gitlab-styles (~> 5.0.0)
+ gitlab-styles (~> 5.1.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2)
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
index a6a2d762fb6..12c0409629f 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
@@ -99,7 +99,17 @@ export default {
};
},
mounted() {
- this.trackPageViews();
+ const callback = entries => {
+ const isVisible = entries.some(entry => entry.isIntersecting);
+
+ if (isVisible) {
+ this.trackPageViews();
+ this.observer.disconnect();
+ }
+ };
+
+ this.observer = new IntersectionObserver(callback);
+ this.observer.observe(this.$el);
},
methods: {
tbodyTrClass(item) {
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 1fff097a799..f469f49ce20 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -566,12 +566,13 @@ const Api = {
});
},
- postUserStatus({ emoji, message }) {
+ postUserStatus({ emoji, message, availability }) {
const url = Api.buildUrl(this.userPostStatusPath);
return axios.put(url, {
emoji,
message,
+ availability,
});
},
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 62948f74aaa..7acd687ac81 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -1,7 +1,9 @@
import $ from 'jquery';
import '~/lib/utils/jquery_at_who';
import { escape, template } from 'lodash';
+import { s__ } from '~/locale';
import SidebarMediator from '~/sidebar/sidebar_mediator';
+import { isUserBusy } from '~/set_status_modal/utils';
import glRegexp from './lib/utils/regexp';
import AjaxCache from './lib/utils/ajax_cache';
import { spriteIcon } from './lib/utils/common_utils';
@@ -39,6 +41,7 @@ export function membersBeforeSave(members) {
title: sanitize(title),
search: sanitize(`${member.username} ${member.name}`),
icon: avatarIcon,
+ availability: member.availability,
};
});
}
@@ -253,13 +256,17 @@ class GfmAutoComplete {
alias: 'users',
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
- const { avatarTag, username, title, icon } = value;
+ const { avatarTag, username, title, icon, availability } = value;
if (username != null) {
tmpl = GfmAutoComplete.Members.templateFunction({
avatarTag,
username,
title,
icon,
+ availabilityStatus:
+ availability && isUserBusy(availability)
+ ? `<span class="gl-text-gray-500"> ${s__('UserAvailability|(Busy)')}</span>`
+ : '',
});
}
return tmpl;
@@ -775,8 +782,10 @@ GfmAutoComplete.Emoji = {
};
// Team Members
GfmAutoComplete.Members = {
- templateFunction({ avatarTag, username, title, icon }) {
- return `<li>${avatarTag} ${username} <small>${escape(title)}</small> ${icon}</li>`;
+ templateFunction({ avatarTag, username, title, icon, availabilityStatus }) {
+ return `<li>${avatarTag} ${username} <small>${escape(
+ title,
+ )}${availabilityStatus}</small> ${icon}</li>`;
},
};
GfmAutoComplete.Labels = {
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index b833cca1db6..1cedb557d46 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -1,5 +1,6 @@
import $ from 'jquery';
import Vue from 'vue';
+import { GlToast } from '@gitlab/ui';
import Translate from '~/vue_shared/translate';
import { highCountTrim } from '~/lib/utils/text_utility';
import Tracking from '~/tracking';
@@ -34,26 +35,45 @@ function initStatusTriggers() {
const statusModalElement = document.createElement('div');
setStatusModalWrapperEl.appendChild(statusModalElement);
+ Vue.use(GlToast);
Vue.use(Translate);
// eslint-disable-next-line no-new
new Vue({
el: statusModalElement,
data() {
- const { currentEmoji, currentMessage } = setStatusModalWrapperEl.dataset;
+ const {
+ currentEmoji,
+ defaultEmoji,
+ currentMessage,
+ currentAvailability,
+ canSetUserAvailability,
+ } = setStatusModalWrapperEl.dataset;
return {
currentEmoji,
+ defaultEmoji,
currentMessage,
+ currentAvailability,
+ canSetUserAvailability,
};
},
render(createElement) {
- const { currentEmoji, currentMessage } = this;
+ const {
+ currentEmoji,
+ defaultEmoji,
+ currentMessage,
+ currentAvailability,
+ canSetUserAvailability,
+ } = this;
return createElement(SetStatusModalWrapper, {
props: {
currentEmoji,
+ defaultEmoji,
currentMessage,
+ currentAvailability,
+ canSetUserAvailability,
},
});
},
diff --git a/app/assets/javascripts/issue_show/components/header_actions.vue b/app/assets/javascripts/issue_show/components/header_actions.vue
index ae0aca0fd27..4c8c86390f4 100644
--- a/app/assets/javascripts/issue_show/components/header_actions.vue
+++ b/app/assets/javascripts/issue_show/components/header_actions.vue
@@ -87,6 +87,9 @@ export default {
? sprintf(__('Reopen %{issueType}'), { issueType: this.issueType })
: sprintf(__('Close %{issueType}'), { issueType: this.issueType });
},
+ qaSelector() {
+ return this.isClosed ? 'reopen_issue_button' : 'close_issue_button';
+ },
buttonVariant() {
return this.isClosed ? 'default' : 'warning';
},
@@ -216,6 +219,7 @@ export default {
v-if="showToggleIssueStateButton"
class="gl-display-none gl-display-sm-inline-flex!"
category="secondary"
+ :data-qa-selector="qaSelector"
:loading="isUpdatingState"
:variant="buttonVariant"
@click="toggleIssueState"
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 1a97756c689..cacf209ed81 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -1,7 +1,8 @@
<script>
/* eslint-disable vue/no-v-html */
import { mapActions } from 'vuex';
-import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlIcon, GlLoadingIcon, GlTooltipDirective, GlSprintf } from '@gitlab/ui';
+import { isUserBusy } from '~/set_status_modal/utils';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
@@ -11,6 +12,7 @@ export default {
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
GlIcon,
GlLoadingIcon,
+ GlSprintf,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -85,9 +87,16 @@ export default {
authorStatus() {
return this.author.status_tooltip_html;
},
+ authorIsBusy() {
+ const { status } = this.author;
+ return status?.availability && isUserBusy(status.availability);
+ },
emojiElement() {
return this.$refs?.authorStatus?.querySelector('gl-emoji');
},
+ authorName() {
+ return this.author.name;
+ },
},
mounted() {
this.emojiTitle = this.emojiElement ? this.emojiElement.getAttribute('title') : '';
@@ -146,7 +155,12 @@ export default {
:data-username="author.username"
>
<slot name="note-header-info"></slot>
- <span class="note-header-author-name bold">{{ author.name }}</span>
+ <span class="note-header-author-name gl-font-weight-bold">
+ <gl-sprintf v-if="authorIsBusy" :message="s__('UserAvailability|%{author} (Busy)')">
+ <template #author>{{ authorName }}</template>
+ </gl-sprintf>
+ <template v-else>{{ authorName }}</template>
+ </span>
</a>
<span
v-if="authorStatus"
diff --git a/app/assets/javascripts/pages/groups/details/index.js b/app/assets/javascripts/pages/groups/details/index.js
index 3bcaa0f0232..0417134f2a7 100644
--- a/app/assets/javascripts/pages/groups/details/index.js
+++ b/app/assets/javascripts/pages/groups/details/index.js
@@ -1,5 +1,3 @@
import initGroupDetails from '../shared/group_details';
-document.addEventListener('DOMContentLoaded', () => {
- initGroupDetails('details');
-});
+initGroupDetails('details');
diff --git a/app/assets/javascripts/pages/groups/labels/edit/index.js b/app/assets/javascripts/pages/groups/labels/edit/index.js
index 83d6ac9fd14..2e8308fe084 100644
--- a/app/assets/javascripts/pages/groups/labels/edit/index.js
+++ b/app/assets/javascripts/pages/groups/labels/edit/index.js
@@ -1,3 +1,4 @@
import Labels from 'ee_else_ce/labels';
-document.addEventListener('DOMContentLoaded', () => new Labels());
+// eslint-disable-next-line no-new
+new Labels();
diff --git a/app/assets/javascripts/pages/groups/labels/index/index.js b/app/assets/javascripts/pages/groups/labels/index/index.js
index 6e45de2a724..87d522d7654 100644
--- a/app/assets/javascripts/pages/groups/labels/index/index.js
+++ b/app/assets/javascripts/pages/groups/labels/index/index.js
@@ -1,3 +1,3 @@
import initLabels from '~/init_labels';
-document.addEventListener('DOMContentLoaded', initLabels);
+initLabels();
diff --git a/app/assets/javascripts/pages/groups/labels/new/index.js b/app/assets/javascripts/pages/groups/labels/new/index.js
index 83d6ac9fd14..2e8308fe084 100644
--- a/app/assets/javascripts/pages/groups/labels/new/index.js
+++ b/app/assets/javascripts/pages/groups/labels/new/index.js
@@ -1,3 +1,4 @@
import Labels from 'ee_else_ce/labels';
-document.addEventListener('DOMContentLoaded', () => new Labels());
+// eslint-disable-next-line no-new
+new Labels();
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
index 71c67ac74ed..2832cbed5ac 100644
--- a/app/assets/javascripts/pages/groups/merge_requests/index.js
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -7,15 +7,13 @@ import { FILTERED_SEARCH } from '~/pages/constants';
const ISSUABLE_BULK_UPDATE_PREFIX = 'merge_request_';
-document.addEventListener('DOMContentLoaded', () => {
- addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
- issuableInitBulkUpdateSidebar.init(ISSUABLE_BULK_UPDATE_PREFIX);
+addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
+issuableInitBulkUpdateSidebar.init(ISSUABLE_BULK_UPDATE_PREFIX);
- initFilteredSearch({
- page: FILTERED_SEARCH.MERGE_REQUESTS,
- isGroupDecendent: true,
- useDefaultState: true,
- filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
- });
- projectSelect();
+initFilteredSearch({
+ page: FILTERED_SEARCH.MERGE_REQUESTS,
+ isGroupDecendent: true,
+ useDefaultState: true,
+ filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
+projectSelect();
diff --git a/app/assets/javascripts/set_status_modal/components/user_availability_status.vue b/app/assets/javascripts/set_status_modal/components/user_availability_status.vue
new file mode 100644
index 00000000000..e86d94f86c6
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/components/user_availability_status.vue
@@ -0,0 +1,26 @@
+<script>
+import { AVAILABILITY_STATUS, isUserBusy, isValidAvailibility } from '../utils';
+
+export default {
+ name: 'UserAvailabilityStatus',
+ props: {
+ availability: {
+ type: String,
+ required: true,
+ validator: isValidAvailibility,
+ },
+ },
+ computed: {
+ isBusy() {
+ const { availability = AVAILABILITY_STATUS.NOT_SET } = this;
+ return isUserBusy(availability);
+ },
+ },
+};
+</script>
+
+<template>
+ <span v-if="isBusy" class="gl-font-weight-normal gl-text-gray-500">{{
+ s__('UserAvailability|(Busy)')
+ }}</span>
+</template>
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
index dcb76b4ab21..30e4e92d0cc 100644
--- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -2,24 +2,35 @@
/* eslint-disable vue/no-v-html */
import $ from 'jquery';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
-import { GlModal, GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { GlModal, GlTooltipDirective, GlIcon, GlFormCheckbox } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __, s__ } from '~/locale';
import Api from '~/api';
import EmojiMenuInModal from './emoji_menu_in_modal';
+import { isUserBusy, isValidAvailibility } from './utils';
import * as Emoji from '~/emoji';
const emojiMenuClass = 'js-modal-status-emoji-menu';
+export const AVAILABILITY_STATUS = {
+ BUSY: 'busy',
+ NOT_SET: 'not_set',
+};
export default {
components: {
GlIcon,
GlModal,
+ GlFormCheckbox,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
+ defaultEmoji: {
+ type: String,
+ required: false,
+ default: '',
+ },
currentEmoji: {
type: String,
required: true,
@@ -28,6 +39,17 @@ export default {
type: String,
required: true,
},
+ currentAvailability: {
+ type: String,
+ required: false,
+ validator: isValidAvailibility,
+ default: '',
+ },
+ canSetUserAvailability: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -39,11 +61,15 @@ export default {
message: this.currentMessage,
modalId: 'set-user-status-modal',
noEmoji: true,
+ availability: isUserBusy(this.currentAvailability),
};
},
computed: {
+ isCustomEmoji() {
+ return this.emoji !== this.defaultEmoji;
+ },
isDirty() {
- return this.message.length || this.emoji.length;
+ return Boolean(this.message.length || this.isCustomEmoji);
},
},
mounted() {
@@ -67,7 +93,7 @@ export default {
this.emojiTag = Emoji.glEmojiTag(this.emoji);
}
this.noEmoji = this.emoji === '';
- this.defaultEmojiTag = Emoji.glEmojiTag('speech_balloon');
+ this.defaultEmojiTag = Emoji.glEmojiTag(this.defaultEmoji);
this.emojiMenu = new EmojiMenuInModal(
Emoji,
@@ -76,6 +102,7 @@ export default {
this.setEmoji,
this.$refs.userStatusForm,
);
+ this.setDefaultEmoji();
})
.catch(() => createFlash(__('Failed to load emoji list.')));
},
@@ -94,7 +121,7 @@ export default {
},
setDefaultEmoji() {
const { emojiTag } = this;
- const hasStatusMessage = this.message;
+ const hasStatusMessage = Boolean(this.message.length);
if (hasStatusMessage && emojiTag) {
return;
}
@@ -126,20 +153,26 @@ export default {
this.hideEmojiMenu();
},
removeStatus() {
+ this.availability = false;
this.clearStatusInputs();
this.setStatus();
},
setStatus() {
- const { emoji, message } = this;
+ const { emoji, message, availability } = this;
Api.postUserStatus({
emoji,
message,
+ availability: availability ? AVAILABILITY_STATUS.BUSY : AVAILABILITY_STATUS.NOT_SET,
})
.then(this.onUpdateSuccess)
.catch(this.onUpdateFail);
},
onUpdateSuccess() {
+ this.$toast.show(s__('SetStatusModal|Status updated'), {
+ type: 'success',
+ position: 'top-center',
+ });
this.closeModal();
window.location.reload();
},
@@ -175,7 +208,7 @@ export default {
name="user[status][emoji]"
/>
<div ref="userStatusForm" class="form-group position-relative m-0">
- <div class="input-group">
+ <div class="input-group gl-mb-5">
<span class="input-group-prepend">
<button
ref="toggleEmojiMenuButton"
@@ -223,6 +256,22 @@ export default {
</button>
</span>
</div>
+ <div v-if="canSetUserAvailability" class="form-group">
+ <div class="gl-display-flex">
+ <gl-form-checkbox
+ v-model="availability"
+ data-testid="user-availability-checkbox"
+ class="gl-mb-0"
+ >
+ <span class="gl-font-weight-bold">{{ s__('SetStatusModal|Busy') }}</span>
+ </gl-form-checkbox>
+ </div>
+ <div class="gl-display-flex">
+ <span class="gl-text-gray-600 gl-ml-5">
+ {{ s__('SetStatusModal|"Busy" will be shown next to your name') }}
+ </span>
+ </div>
+ </div>
</div>
</div>
</gl-modal>
diff --git a/app/assets/javascripts/set_status_modal/utils.js b/app/assets/javascripts/set_status_modal/utils.js
new file mode 100644
index 00000000000..dccb66be11f
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/utils.js
@@ -0,0 +1,9 @@
+export const AVAILABILITY_STATUS = {
+ BUSY: 'busy',
+ NOT_SET: 'not_set',
+};
+
+export const isUserBusy = status => status === AVAILABILITY_STATUS.BUSY;
+
+export const isValidAvailibility = availability =>
+ availability.length ? Object.values(AVAILABILITY_STATUS).includes(availability) : true;
diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
index 3f5738b2b93..2ab4c55d9b0 100644
--- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
@@ -6,6 +6,7 @@ import {
GlDeprecatedSkeletonLoading as GlSkeletonLoading,
GlIcon,
} from '@gitlab/ui';
+import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
import UserAvatarImage from '../user_avatar/user_avatar_image.vue';
import { glEmojiTag } from '../../../emoji';
@@ -25,6 +26,7 @@ export default {
GlPopover,
GlSkeletonLoading,
UserAvatarImage,
+ UserAvailabilityStatus,
},
props: {
target: {
@@ -63,6 +65,9 @@ export default {
websiteUrl.length
);
},
+ availabilityStatus() {
+ return this.user?.status?.availability || null;
+ },
},
};
</script>
@@ -89,6 +94,10 @@ export default {
<div class="gl-mb-3">
<h5 class="gl-m-0">
{{ user.name }}
+ <user-availability-status
+ v-if="availabilityStatus"
+ :availability="availabilityStatus"
+ />
</h5>
<span class="gl-text-gray-500">@{{ user.username }}</span>
</div>
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index e39dfa44d86..e3d82e7a091 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -158,6 +158,17 @@ module PageLayoutHelper
end
end
+ def user_status_properties(user)
+ default_properties = { current_emoji: '', current_message: '', can_set_user_availability: Feature.enabled?(:set_user_availability_status, user), default_emoji: UserStatus::DEFAULT_EMOJI }
+ return default_properties unless user&.status
+
+ default_properties.merge({
+ current_emoji: user.status.emoji.to_s,
+ current_message: user.status.message.to_s,
+ current_availability: user.status.availability.to_s
+ })
+ end
+
private
def generic_canonical_url
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index 5a42e581867..04a3b915493 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -29,4 +29,18 @@ module ProfilesHelper
def user_profile?
params[:controller] == 'users'
end
+
+ def availability_values
+ Types::AvailabilityEnum.enum
+ end
+
+ def user_status_set_to_busy?(status)
+ status&.availability == availability_values[:busy]
+ end
+
+ def show_status_emoji?(status)
+ return false unless status
+
+ status.message.present? || status.emoji != UserStatus::DEFAULT_EMOJI
+ end
end
diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb
index 89f6591ea1e..9855731778f 100644
--- a/app/models/pages/lookup_path.rb
+++ b/app/models/pages/lookup_path.rb
@@ -40,37 +40,35 @@ module Pages
def artifacts_archive
return unless Feature.enabled?(:pages_serve_from_artifacts_archive, project)
- archive = project.pages_metadatum.artifacts_archive
-
- archive&.file
+ project.pages_metadatum.artifacts_archive
end
def deployment
return unless Feature.enabled?(:pages_serve_from_deployments, project)
- deployment = project.pages_metadatum.pages_deployment
-
- deployment&.file
+ project.pages_metadatum.pages_deployment
end
def zip_source
source = deployment || artifacts_archive
- return unless source
+ return unless source&.file
- if source.file_storage?
- return unless Feature.enabled?(:pages_serve_with_zip_file_protocol, project)
+ return if source.file.file_storage? && !Feature.enabled?(:pages_serve_with_zip_file_protocol, project)
- {
- type: 'zip',
- path: 'file://' + source.path
- }
- else
- {
- type: 'zip',
- path: source.url(expire_at: 1.day.from_now)
- }
- end
+ # artifacts archive doesn't support this
+ file_count = source.file_count if source.respond_to?(:file_count)
+
+ global_id = ::Gitlab::GlobalId.build(source, id: source.id).to_s
+
+ {
+ type: 'zip',
+ path: source.file.url_or_file_path(expire_at: 1.day.from_now),
+ global_id: global_id,
+ sha256: source.file_sha256,
+ file_size: source.size,
+ file_count: file_count
+ }
end
def file_source
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index 6fde9abfdb0..fac8e91d216 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -45,7 +45,8 @@ module Users
type: user.class.name,
username: user.username,
name: user.name,
- avatar_url: user.avatar_url
+ avatar_url: user.avatar_url,
+ availability: user&.status&.availability
}
end
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index 411d8b2614f..9758d3c87aa 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -118,6 +118,14 @@ class GitlabUploader < CarrierWave::Uploader::Base
storage.store!(file)
end
+ def url_or_file_path(url_options = {})
+ if file_storage?
+ 'file://' + path
+ else
+ url(url_options)
+ end
+ end
+
private
# Designed to be overridden by child uploaders that have a dynamic path
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 4c6bfc0b33c..addf2375222 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -2,13 +2,16 @@
%ul
%li.current-user
- .user-name.bold
+ .user-name.gl-font-weight-bold
= current_user.name
+ - if current_user&.status && user_status_set_to_busy?(current_user.status)
+ %span.gl-font-weight-normal.gl-text-gray-500= s_("UserProfile|(Busy)")
= current_user.to_reference
- if current_user.status
.user-status.d-flex.align-items-center.gl-mt-2.has-tooltip{ title: current_user.status.message_html, data: { html: 'true', placement: 'bottom' } }
- %span.user-status-emoji.d-flex.align-items-center
- = emoji_icon current_user.status.emoji
+ - if show_status_emoji?(current_user.status)
+ .user-status-emoji.d-flex.align-items-center
+ = emoji_icon current_user.status.emoji
%span.user-status-message.str-truncated
= current_user.status.message_html.html_safe
%li.divider
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index f6dc808aa55..794d1589172 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -1,4 +1,5 @@
- has_impersonation_link = header_link?(:admin_impersonation)
+- user_status_data = user_status_properties(current_user)
%header.navbar.navbar-gitlab.navbar-expand-sm.js-navbar{ data: { qa_selector: 'navbar' } }
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
@@ -103,4 +104,4 @@
#whats-new-app{ data: { storage_key: whats_new_storage_key } }
- if can?(current_user, :update_user_status, current_user)
- .js-set-status-modal-wrapper{ data: { current_emoji: current_user.status.present? ? current_user.status.emoji : '', current_message: current_user.status.present? ? current_user.status.message : '' } }
+ .js-set-status-modal-wrapper{ data: user_status_data }
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index f5fab727a57..bf9f1336a4f 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -2,6 +2,8 @@
- page_title s_("Profiles|Edit Profile")
- @content_class = "limit-container-width" unless fluid_layout
- gravatar_link = link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host
+- availability = availability_values
+- custom_emoji = show_status_emoji?(@user.status)
= bootstrap_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user gl-mt-3 js-quick-submit gl-show-field-errors' }, authenticity_token: true do |f|
= form_errors(@user)
@@ -48,9 +50,9 @@
- emoji_button = button_tag type: :button,
class: 'js-toggle-emoji-menu emoji-menu-toggle-button gl-button btn has-tooltip',
title: s_("Profiles|Add status emoji") do
- - if @user.status
+ - if custom_emoji
= emoji_icon @user.status.emoji
- %span#js-no-emoji-placeholder.no-emoji-placeholder{ class: ('hidden' if @user.status) }
+ %span#js-no-emoji-placeholder.no-emoji-placeholder{ class: ('hidden' if custom_emoji) }
= sprite_icon('slight-smile', css_class: 'award-control-icon-neutral')
= sprite_icon('smiley', css_class: 'award-control-icon-positive')
= sprite_icon('smile', css_class: 'award-control-icon-super-positive')
@@ -68,6 +70,10 @@
prepend: emoji_button,
append: reset_message_button,
placeholder: s_("Profiles|What's your status?")
+ - if Feature.enabled?(:set_user_availability_status, @user)
+ .checkbox-icon-inline-wrapper
+ = status_form.check_box :availability, { data: { testid: "user-availability-checkbox" }, label: s_("Profiles|Busy"), wrapper_class: 'gl-mr-0 gl-font-weight-bold' }, availability["busy"], availability["not_set"]
+ .gl-text-gray-600.gl-ml-5= s_('Profiles|"Busy" will be shown next to your name')
- if Feature.enabled?(:user_time_settings)
%hr
.row.user-time-preferences
diff --git a/app/views/projects/_merge_request_merge_options_settings.html.haml b/app/views/projects/_merge_request_merge_options_settings.html.haml
index 047b4dafbfc..8951f2ed22f 100644
--- a/app/views/projects/_merge_request_merge_options_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_options_settings.html.haml
@@ -4,6 +4,7 @@
%b= s_('ProjectSettings|Merge options')
%p.text-secondary= s_('ProjectSettings|Additional merge request capabilities that influence how and when merges will be performed')
= render_if_exists 'projects/merge_pipelines_settings', form: form
+ = render_if_exists 'projects/merge_trains_settings', form: form
.form-check.mb-2
= form.check_box :resolve_outdated_diff_discussions, class: 'form-check-input'
= form.label :resolve_outdated_diff_discussions, class: 'form-check-label' do
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 90e0b230948..ee037a7d66a 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -47,8 +47,10 @@
.user-info
.cover-title{ itemprop: 'name' }
= @user.name
+ - if @user&.status && user_status_set_to_busy?(@user.status)
+ %span.gl-font-base.gl-text-gray-500.gl-vertical-align-middle= s_("UserProfile|(Busy)")
- - if @user.status
+ - if show_status_emoji?(@user.status)
.cover-status
= emoji_icon(@user.status.emoji)
= markdown_field(@user.status, :message)
diff --git a/changelogs/unreleased/276185_alerts_integrations_list_tracking.yml b/changelogs/unreleased/276185_alerts_integrations_list_tracking.yml
new file mode 100644
index 00000000000..e96f3ef6998
--- /dev/null
+++ b/changelogs/unreleased/276185_alerts_integrations_list_tracking.yml
@@ -0,0 +1,5 @@
+---
+title: Fix alerts integration list Snowplow tracking event
+merge_request: 47413
+author:
+type: fixed
diff --git a/changelogs/unreleased/ak-fix-chatbot-replies.yml b/changelogs/unreleased/ak-fix-chatbot-replies.yml
new file mode 100644
index 00000000000..30b961d9a3b
--- /dev/null
+++ b/changelogs/unreleased/ak-fix-chatbot-replies.yml
@@ -0,0 +1,5 @@
+---
+title: Fix chatbot replies not including job log
+merge_request: 42010
+author:
+type: fixed
diff --git a/config/feature_flags/development/push_rules_supersede_code_owners.yml b/config/feature_flags/development/push_rules_supersede_code_owners.yml
new file mode 100644
index 00000000000..7226e443640
--- /dev/null
+++ b/config/feature_flags/development/push_rules_supersede_code_owners.yml
@@ -0,0 +1,7 @@
+---
+name: push_rules_supersede_code_owners
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44126
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/262019
+type: development
+group: group::source code
+default_enabled: true
diff --git a/config/feature_flags/development/set_user_availability_status.yml b/config/feature_flags/development/set_user_availability_status.yml
new file mode 100644
index 00000000000..be3ff522ccc
--- /dev/null
+++ b/config/feature_flags/development/set_user_availability_status.yml
@@ -0,0 +1,8 @@
+---
+name: set_user_availability_status
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46844
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/281073
+milestone: '13.6'
+type: development
+group: group::optimize
+default_enabled: false
diff --git a/db/migrate/20201112195322_reseed_merge_trains_enabled.rb b/db/migrate/20201112195322_reseed_merge_trains_enabled.rb
new file mode 100644
index 00000000000..777c59d16b1
--- /dev/null
+++ b/db/migrate/20201112195322_reseed_merge_trains_enabled.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class ReseedMergeTrainsEnabled < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ update_column_in_batches(:project_ci_cd_settings, :merge_trains_enabled, true) do |table, query|
+ query.where(table[:merge_pipelines_enabled].eq(true))
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema_migrations/20201112195322 b/db/schema_migrations/20201112195322
new file mode 100644
index 00000000000..ba4c7e9d50f
--- /dev/null
+++ b/db/schema_migrations/20201112195322
@@ -0,0 +1 @@
+644bf793b3b62330bfb1f4dc406e6dd01cbcf4651ebfd2873fb551c757c8a3b4 \ No newline at end of file
diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md
index a696d0499a4..d2937d36a35 100644
--- a/doc/administration/auth/smartcard.md
+++ b/doc/administration/auth/smartcard.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
type: reference
---
diff --git a/doc/administration/compliance.md b/doc/administration/compliance.md
index 44270283308..4cb2c002015 100644
--- a/doc/administration/compliance.md
+++ b/doc/administration/compliance.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Compliance
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/administration/database_load_balancing.md b/doc/administration/database_load_balancing.md
index e88f88a0427..2b25ee4bab8 100644
--- a/doc/administration/database_load_balancing.md
+++ b/doc/administration/database_load_balancing.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md
index 0c0659255bf..2b714c36d93 100644
--- a/doc/administration/object_storage.md
+++ b/doc/administration/object_storage.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
type: reference
---
diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md
index eed92d32ec9..5de79882083 100644
--- a/doc/administration/operations/extra_sidekiq_processes.md
+++ b/doc/administration/operations/extra_sidekiq_processes.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index e206e383856..e08f3f7ec0c 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -415,9 +415,6 @@ internet connectivity is gated by a proxy. To use a proxy for GitLab Pages:
### Using a custom Certificate Authority (CA)
-NOTE: **Note:**
-[Before 13.3](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4411), when using Omnibus, a [workaround was required](https://docs.gitlab.com/13.1/ee/administration/pages/index.html#using-a-custom-certificate-authority-ca).
-
When using certificates issued by a custom CA, [Access Control](../../user/project/pages/pages_access_control.md#gitlab-pages-access-control) and
the [online view of HTML job artifacts](../../ci/pipelines/job_artifacts.md#browsing-artifacts)
will fail to work if the custom CA is not recognized.
diff --git a/doc/administration/raketasks/doctor.md b/doc/administration/raketasks/doctor.md
index 5bc749b17da..e6a6751f199 100644
--- a/doc/administration/raketasks/doctor.md
+++ b/doc/administration/raketasks/doctor.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Geo
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/administration/raketasks/geo.md b/doc/administration/raketasks/geo.md
index 75155d88a24..0ee6fd8fc75 100644
--- a/doc/administration/raketasks/geo.md
+++ b/doc/administration/raketasks/geo.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Geo
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/administration/raketasks/ldap.md b/doc/administration/raketasks/ldap.md
index 2aaa4097632..821a72291fd 100644
--- a/doc/administration/raketasks/ldap.md
+++ b/doc/administration/raketasks/ldap.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/administration/troubleshooting/elasticsearch.md b/doc/administration/troubleshooting/elasticsearch.md
index 14f5baff14f..5f29ed31ad7 100644
--- a/doc/administration/troubleshooting/elasticsearch.md
+++ b/doc/administration/troubleshooting/elasticsearch.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Global Search
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/administration/troubleshooting/group_saml_scim.md b/doc/administration/troubleshooting/group_saml_scim.md
index 28b440d1abd..efc8eaab198 100644
--- a/doc/administration/troubleshooting/group_saml_scim.md
+++ b/doc/administration/troubleshooting/group_saml_scim.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
type: reference
---
diff --git a/doc/api/appearance.md b/doc/api/appearance.md
index ad44c93ce32..07d26b1a643 100644
--- a/doc/api/appearance.md
+++ b/doc/api/appearance.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/api/dependency_proxy.md b/doc/api/dependency_proxy.md
index 426d2381858..4d937027dec 100644
--- a/doc/api/dependency_proxy.md
+++ b/doc/api/dependency_proxy.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Package
+group: Package
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/api/epic_links.md b/doc/api/epic_links.md
index c4afb1e7299..a368b806f75 100644
--- a/doc/api/epic_links.md
+++ b/doc/api/epic_links.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Plan
+group: Product Planning
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md
index f198b4f4aa2..c9b86320a9f 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Geo
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 8c9b7fda260..7de0b97b160 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -9455,6 +9455,11 @@ type Group {
shareWithGroupLock: Boolean
"""
+ Group statistics
+ """
+ stats: GroupStats
+
+ """
Total storage limit of the root namespace in bytes
"""
storageSizeLimit: Float
@@ -9879,6 +9884,33 @@ type GroupPermissions {
}
"""
+Contains release-related statistics about a group
+"""
+type GroupReleaseStats {
+ """
+ Total number of releases in all descendant projects of the group. Will always
+ return `null` if `group_level_release_statistics` feature flag is disabled
+ """
+ releasesCount: Int
+
+ """
+ Percentage of the group's descendant projects that have at least one release.
+ Will always return `null` if `group_level_release_statistics` feature flag is disabled
+ """
+ releasesPercentage: Int
+}
+
+"""
+Contains statistics about a group
+"""
+type GroupStats {
+ """
+ Statistics related to releases within the group
+ """
+ releaseStats: GroupReleaseStats
+}
+
+"""
Health status of an issue or epic
"""
enum HealthStatus {
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 7fdf1c2baec..bb42b0765eb 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -25804,6 +25804,20 @@
"deprecationReason": null
},
{
+ "name": "stats",
+ "description": "Group statistics",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "GroupStats",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "storageSizeLimit",
"description": "Total storage limit of the root namespace in bytes",
"args": [
@@ -26989,6 +27003,74 @@
"possibleTypes": null
},
{
+ "kind": "OBJECT",
+ "name": "GroupReleaseStats",
+ "description": "Contains release-related statistics about a group",
+ "fields": [
+ {
+ "name": "releasesCount",
+ "description": "Total number of releases in all descendant projects of the group. Will always return `null` if `group_level_release_statistics` feature flag is disabled",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "releasesPercentage",
+ "description": "Percentage of the group's descendant projects that have at least one release. Will always return `null` if `group_level_release_statistics` feature flag is disabled",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "GroupStats",
+ "description": "Contains statistics about a group",
+ "fields": [
+ {
+ "name": "releaseStats",
+ "description": "Statistics related to releases within the group",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "GroupReleaseStats",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
"kind": "ENUM",
"name": "HealthStatus",
"description": "Health status of an issue or epic",
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index aee92a019c3..9d43874dee8 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1447,6 +1447,7 @@ Autogenerated return type of EpicTreeReorder.
| `requireTwoFactorAuthentication` | Boolean | Indicates if all users in this group are required to set up two-factor authentication |
| `rootStorageStatistics` | RootStorageStatistics | Aggregated storage statistics of the namespace. Only available for root namespaces |
| `shareWithGroupLock` | Boolean | Indicates if sharing a project with another group within this group is prevented |
+| `stats` | GroupStats | Group statistics |
| `storageSizeLimit` | Float | Total storage limit of the root namespace in bytes |
| `subgroupCreationLevel` | String | The permission level required to create subgroups within the group |
| `temporaryStorageIncreaseEndsOn` | Time | Date until the temporary storage increase is active |
@@ -1486,6 +1487,23 @@ Represents a Group Membership.
| ----- | ---- | ----------- |
| `readGroup` | Boolean! | Indicates the user can perform `read_group` on this resource |
+### GroupReleaseStats
+
+Contains release-related statistics about a group.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `releasesCount` | Int | Total number of releases in all descendant projects of the group. Will always return `null` if `group_level_release_statistics` feature flag is disabled |
+| `releasesPercentage` | Int | Percentage of the group's descendant projects that have at least one release. Will always return `null` if `group_level_release_statistics` feature flag is disabled |
+
+### GroupStats
+
+Contains statistics about a group.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `releaseStats` | GroupReleaseStats | Statistics related to releases within the group |
+
### HttpIntegrationCreatePayload
Autogenerated return type of HttpIntegrationCreate.
diff --git a/doc/api/group_activity_analytics.md b/doc/api/group_activity_analytics.md
index 38394c6412d..ea2527e6c4a 100644
--- a/doc/api/group_activity_analytics.md
+++ b/doc/api/group_activity_analytics.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/api/group_import_export.md b/doc/api/group_import_export.md
index dfd78e88233..5677cb51c37 100644
--- a/doc/api/group_import_export.md
+++ b/doc/api/group_import_export.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 4416ce11db2..70aa4923b97 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Plan
+group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/api/personal_access_tokens.md b/doc/api/personal_access_tokens.md
index 8a838a0f179..f7cbaeba438 100644
--- a/doc/api/personal_access_tokens.md
+++ b/doc/api/personal_access_tokens.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Compliance
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
index 2330bdb4c7c..9c6fbba1337 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
@@ -56,7 +56,7 @@ To enable pipelines for merged results for your project:
1. [Configure your CI/CD configuration file](../index.md#configuring-pipelines-for-merge-requests)
so that the pipeline or individual jobs run for merge requests.
1. Visit your project's **Settings > General** and expand **Merge requests**.
-1. Check **Enable merge trains and pipelines for merged results**.
+1. Check **Enable merged results pipelines.**.
1. Click **Save changes**.
CAUTION: **Caution:**
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
index c5d99011645..d4099cdeafd 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
@@ -82,9 +82,13 @@ To enable merge trains for your project:
1. If you are on a self-managed GitLab instance, ensure the [feature flag](#merge-trains-feature-flag) is set correctly.
1. [Configure your CI/CD configuration file](../../index.md#configuring-pipelines-for-merge-requests)
so that the pipeline or individual jobs run for merge requests.
-1. Visit your project's **Settings > General** and expand **Merge requests**.
-1. Check **Enable merge trains and pipelines for merged results**.
-1. Click **Save changes**.
+1. Visit your project's **Settings > General** and expand **Merge requests**
+1. Check **Enable merged results pipelines.** (if not enabled)
+1. Check **Enable merge trains.**
+1. Click **Save changes**
+
+In GitLab 13.5 and earlier, there is only one checkbox, named
+**Enable merge trains and pipelines for merged results**.
CAUTION: **Caution:**
If you select the check box but don't configure your CI/CD to use
@@ -200,10 +204,13 @@ for more information.
### Merge Trains feature flag **(PREMIUM ONLY)**
-Merge trains are automatically enabled when [pipelines for merged results](../index.md#pipelines-for-merged-results)
-are enabled. To use pipelines for merged results without using merge trains, you must
-enable a [feature flag](../../../../user/feature_flags.md) that blocks the merge trains
-feature.
+In [GitLab 13.6 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/244831),
+you can [enable or disable merge trains in the project settings](#enable-merge-trains).
+
+In GitLab 13.5 and earlier, merge trains are automatically enabled when
+[pipelines for merged results](../index.md#pipelines-for-merged-results) are enabled.
+To use pipelines for merged results without using merge trains, you can enable a
+[feature flag](../../../../user/feature_flags.md) that blocks the merge trains feature.
[GitLab administrators with access to the GitLab Rails console](../../../../administration/feature_flags.md)
can enable the feature flag to disable merge trains:
diff --git a/doc/ci/unit_test_reports.md b/doc/ci/unit_test_reports.md
index a6f2499d0fc..0a1f0000969 100644
--- a/doc/ci/unit_test_reports.md
+++ b/doc/ci/unit_test_reports.md
@@ -265,6 +265,23 @@ javascript:
- junit.xml
```
+#### Karma
+
+The [Karma-junit-reporter](https://github.com/karma-runner/karma-junit-reporter) npm package can generate test reports for JavaScript applications.
+In the following `.gitlab-ci.yml` example, the `javascript` job uses Karma to generate the test reports:
+
+```yaml
+javascript:
+ stage: test
+ script:
+ - karma start --reporters junit
+ artifacts:
+ when: always
+ reports:
+ junit:
+ - junit.xml
+```
+
## Viewing Unit test reports on GitLab
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24792) in GitLab 12.5 behind a feature flag (`junit_pipeline_view`), disabled by default.
diff --git a/doc/development/approval_rules.md b/doc/development/approval_rules.md
index 892050611e1..735cc3fc7e2 100644
--- a/doc/development/approval_rules.md
+++ b/doc/development/approval_rules.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/creating_enums.md b/doc/development/creating_enums.md
index 559a79700d2..fbf35171ecb 100644
--- a/doc/development/creating_enums.md
+++ b/doc/development/creating_enums.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/database/setting_multiple_values.md b/doc/development/database/setting_multiple_values.md
index 86cf80007f6..c354247a9f8 100644
--- a/doc/development/database/setting_multiple_values.md
+++ b/doc/development/database/setting_multiple_values.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/experiment_guide/index.md b/doc/development/experiment_guide/index.md
index 5d9e0569f2a..3b2f1d21463 100644
--- a/doc/development/experiment_guide/index.md
+++ b/doc/development/experiment_guide/index.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Growth
+group: Activation
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/feature_categorization/index.md b/doc/development/feature_categorization/index.md
index 4b9d1fc0427..c196f83e603 100644
--- a/doc/development/feature_categorization/index.md
+++ b/doc/development/feature_categorization/index.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Infrastructure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/foreign_keys.md b/doc/development/foreign_keys.md
index f6e092e6d20..ff8c03ce76b 100644
--- a/doc/development/foreign_keys.md
+++ b/doc/development/foreign_keys.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/graphql_guide/batchloader.md b/doc/development/graphql_guide/batchloader.md
index c9a22e04857..6d529358499 100644
--- a/doc/development/graphql_guide/batchloader.md
+++ b/doc/development/graphql_guide/batchloader.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/hash_indexes.md b/doc/development/hash_indexes.md
index dcf6eb1c8dc..cc0b0b3a736 100644
--- a/doc/development/hash_indexes.md
+++ b/doc/development/hash_indexes.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 78caac0d6d5..65a1d83a8fc 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/i18n/index.md b/doc/development/i18n/index.md
index 070a55e7a34..13f2b43b788 100644
--- a/doc/development/i18n/index.md
+++ b/doc/development/i18n/index.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md
index 4cd1f8deef0..c4a709f84d3 100644
--- a/doc/development/i18n/merging_translations.md
+++ b/doc/development/i18n/merging_translations.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index a124bddb357..56a4f835b3f 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/i18n/translation.md b/doc/development/i18n/translation.md
index 5d8af8a096e..128bacfda97 100644
--- a/doc/development/i18n/translation.md
+++ b/doc/development/i18n/translation.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/image_scaling.md b/doc/development/image_scaling.md
index 270c2dbcae2..05f44209bc4 100644
--- a/doc/development/image_scaling.md
+++ b/doc/development/image_scaling.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Memory
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index 77c71f0f991..a73144abce6 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/insert_into_tables_in_batches.md b/doc/development/insert_into_tables_in_batches.md
index 2b411f98d20..5fe37a95b59 100644
--- a/doc/development/insert_into_tables_in_batches.md
+++ b/doc/development/insert_into_tables_in_batches.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
description: "Sometimes it is necessary to store large amounts of records at once, which can be inefficient
when iterating collections and performing individual `save`s. With the arrival of `insert_all`
diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md
index 44c69acbd87..9bb92709d54 100644
--- a/doc/development/integrations/secure.md
+++ b/doc/development/integrations/secure.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Protect
+group: Container Security
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/integrations/secure_partner_integration.md b/doc/development/integrations/secure_partner_integration.md
index f555ceec2ca..52d10f0bd3c 100644
--- a/doc/development/integrations/secure_partner_integration.md
+++ b/doc/development/integrations/secure_partner_integration.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Secure
+group: Static Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/iterating_tables_in_batches.md b/doc/development/iterating_tables_in_batches.md
index 0b7ed09fd7b..062b755de38 100644
--- a/doc/development/iterating_tables_in_batches.md
+++ b/doc/development/iterating_tables_in_batches.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/lfs.md b/doc/development/lfs.md
index cf84ac72c06..64bc709ff9a 100644
--- a/doc/development/lfs.md
+++ b/doc/development/lfs.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/new_fe_guide/modules/widget_extensions.md b/doc/development/new_fe_guide/modules/widget_extensions.md
index 16f82beaece..3844fedb126 100644
--- a/doc/development/new_fe_guide/modules/widget_extensions.md
+++ b/doc/development/new_fe_guide/modules/widget_extensions.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/new_fe_guide/tips.md b/doc/development/new_fe_guide/tips.md
index fa00b8bee1e..d7e9c440335 100644
--- a/doc/development/new_fe_guide/tips.md
+++ b/doc/development/new_fe_guide/tips.md
@@ -1,6 +1,6 @@
---
stage: none
-group: unassigned
+group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md
index 4332e1b1f8f..124f82ac2c8 100644
--- a/doc/development/ordering_table_columns.md
+++ b/doc/development/ordering_table_columns.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/permissions.md b/doc/development/permissions.md
index af427427cb6..859c838280e 100644
--- a/doc/development/permissions.md
+++ b/doc/development/permissions.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/policies.md b/doc/development/policies.md
index 3b5b073c6a2..b44367f7075 100644
--- a/doc/development/policies.md
+++ b/doc/development/policies.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/serializing_data.md b/doc/development/serializing_data.md
index 0e394185b0d..83a75eef556 100644
--- a/doc/development/serializing_data.md
+++ b/doc/development/serializing_data.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/sql.md b/doc/development/sql.md
index 0b7b2a4024f..bf405eab688 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/testing_guide/end_to_end/best_practices.md b/doc/development/testing_guide/end_to_end/best_practices.md
index 3ee4a3d1b87..4d12a0f79cb 100644
--- a/doc/development/testing_guide/end_to_end/best_practices.md
+++ b/doc/development/testing_guide/end_to_end/best_practices.md
@@ -1,6 +1,6 @@
---
stage: none
-group: unassigned
+group: Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
index 82bf0f6d99f..896b34a4ab4 100644
--- a/doc/development/understanding_explain_plans.md
+++ b/doc/development/understanding_explain_plans.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/value_stream_analytics.md b/doc/development/value_stream_analytics.md
index 90d7597d46f..4560a115562 100644
--- a/doc/development/value_stream_analytics.md
+++ b/doc/development/value_stream_analytics.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/verifying_database_capabilities.md b/doc/development/verifying_database_capabilities.md
index 930f40ef65e..fe60ec11bce 100644
--- a/doc/development/verifying_database_capabilities.md
+++ b/doc/development/verifying_database_capabilities.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index 9b3c4e8a684..18a2e17967a 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Enablement
+group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
diff --git a/doc/development/wikis.md b/doc/development/wikis.md
index 9a436762645..f8bcaac2ec0 100644
--- a/doc/development/wikis.md
+++ b/doc/development/wikis.md
@@ -1,8 +1,8 @@
---
type: reference, dev
-stage: none
-group: Development
-info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
+stage: Create
+group: Knowledge
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
description: "GitLab's development guidelines for Wikis"
---
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index f84e17657b1..244e37ba3ab 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -105,7 +105,7 @@ Each stage of Value Stream Analytics is further described in the table below.
| --------- | --------------- |
| Issue | Measures the median time between creating an issue and taking action to solve it, by either labeling it or adding it to a milestone, whatever comes first. The label will be tracked only if it already has an [Issue Board list](../project/issue_board.md) created for it. |
| Plan | Measures the median time between the action you took for the previous stage, and pushing the first commit to the branch. The very first commit of the branch is the one that triggers the separation between **Plan** and **Code**, and at least one of the commits in the branch needs to contain the related issue number (e.g., `#42`). If none of the commits in the branch mention the related issue number, it is not considered to the measurement time of the stage. |
-| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR) related to that commit. The key to keep the process tracked is to include the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically) to the description of the merge request (for example, `Closes #xxx`, where `xxx` is the number of the issue related to this merge request). If the issue closing pattern is not present in the merge request description, the MR is not considered to the measurement time of the stage. |
+| Code | Measures the median time between pushing a first commit (previous stage) and creating a merge request (MR) related to that commit. The key to keep the process tracked is to include the [issue closing pattern](../project/issues/managing_issues.md#closing-issues-automatically) to the description of the merge request (for example, `Closes #xxx`, where `xxx` is the number of the issue related to this merge request). If the closing pattern is not present, then the calculation takes the creation time of the first commit in the merge request as the start time. |
| Test | Measures the median time to run the entire pipeline for that project. It's related to the time GitLab CI/CD takes to run every job for the commits pushed to that merge request defined in the previous stage. It is basically the start->finish time for all pipelines. |
| Review | Measures the median time taken to review the merge request that has a closing issue pattern, between its creation and until it's merged. |
| Staging | Measures the median time between merging the merge request with a closing issue pattern until the very first deployment to a [production environment](#how-the-production-environment-is-identified). If there isn't a production environment, this is not tracked. |
diff --git a/doc/user/project/integrations/webex_teams.md b/doc/user/project/integrations/webex_teams.md
index 39daa14407f..7654909b735 100644
--- a/doc/user/project/integrations/webex_teams.md
+++ b/doc/user/project/integrations/webex_teams.md
@@ -10,9 +10,9 @@ You can configure GitLab to send notifications to a Webex Teams space.
## Create a webhook for the space
-1. Go to the [Incoming Webhooks app page](https://apphub.webex.com/teams/applications/incoming-webhooks-cisco-systems).
+1. Go to the [Incoming Webhooks app page](https://apphub.webex.com/teams/applications/incoming-webhooks-cisco-systems-38054).
1. Click **Connect** and log in to Webex Teams, if required.
-1. Enter a name for the webhook and select the space that will receive the notifications.
+1. Enter a name for the webhook and select the space to receive the notifications.
1. Click **ADD**.
1. Copy the **Webhook URL**.
@@ -27,4 +27,4 @@ Once you have a webhook URL for your Webex Teams space, you can configure GitLab
1. Paste the **Webhook** URL for the Webex Teams space.
1. Configure the remaining options and then click **Test settings and save changes**.
-The Webex Teams space will begin to receive all applicable GitLab events.
+The Webex Teams space now begins to receive all applicable GitLab events.
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 8eb3cc23d0b..79c6cf3b07d 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -44,6 +44,7 @@ groups:
- Author
- Assignee
- [Milestone](../project/milestones/index.md)
+ - [Iteration](../group/iterations/index.md) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.6)
- Release
- [Label](../project/labels.md)
- My-reaction
diff --git a/lib/gitlab/chat/output.rb b/lib/gitlab/chat/output.rb
index 411b1555a7d..4a55b81a9eb 100644
--- a/lib/gitlab/chat/output.rb
+++ b/lib/gitlab/chat/output.rb
@@ -12,7 +12,10 @@ module Gitlab
PRIMARY_SECTION = 'chat_reply'
# The backup trace section in case the primary one could not be found.
- FALLBACK_SECTION = 'build_script'
+ FALLBACK_SECTION = 'step_script'
+
+ # `step_script` used to be `build_script` before runner 13.1
+ LEGACY_SECTION = 'build_script'
# build - The `Ci::Build` to obtain the output from.
def initialize(build)
@@ -37,24 +40,6 @@ module Gitlab
end
end
- # Returns the offset to seek to and the number of bytes to read relative
- # to the offset.
- def read_offset_and_length
- section = find_build_trace_section(PRIMARY_SECTION) ||
- find_build_trace_section(FALLBACK_SECTION)
-
- unless section
- raise(
- MissingBuildSectionError,
- "The build_script trace section could not be found for build #{build.id}"
- )
- end
-
- length = section[:byte_end] - section[:byte_start]
-
- [section[:byte_start], length]
- end
-
# Removes the line containing the executed command from the build output.
#
# output - A `String` containing the output of a trace section.
@@ -88,6 +73,27 @@ module Gitlab
def trace
@trace ||= build.trace
end
+
+ private
+
+ # Returns the offset to seek to and the number of bytes to read relative
+ # to the offset.
+ def read_offset_and_length
+ section = find_build_trace_section(PRIMARY_SECTION) ||
+ find_build_trace_section(FALLBACK_SECTION) ||
+ find_build_trace_section(LEGACY_SECTION)
+
+ unless section
+ raise(
+ MissingBuildSectionError,
+ "The build_script trace section could not be found for build #{build.id}"
+ )
+ end
+
+ length = section[:byte_end] - section[:byte_start]
+
+ [section[:byte_start], length]
+ end
end
end
end
diff --git a/lib/gitlab/octokit/middleware.rb b/lib/gitlab/octokit/middleware.rb
index 2dd7d08a58b..a3c0fdcf467 100644
--- a/lib/gitlab/octokit/middleware.rb
+++ b/lib/gitlab/octokit/middleware.rb
@@ -8,7 +8,7 @@ module Gitlab
end
def call(env)
- Gitlab::UrlBlocker.validate!(env[:url], { allow_localhost: allow_local_requests?, allow_local_network: allow_local_requests? })
+ Gitlab::UrlBlocker.validate!(env[:url], allow_localhost: allow_local_requests?, allow_local_network: allow_local_requests?)
@app.call(env)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 041f1eb0eb9..91a66eb80f0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20577,6 +20577,9 @@ msgstr ""
msgid "Profiles| You are going to change the username %{currentUsernameBold} to %{newUsernameBold}. Profile and projects will be redirected to the %{newUsername} namespace but this redirect will expire once the %{currentUsername} namespace is registered by another user or group. Please update your Git repository remotes as soon as possible."
msgstr ""
+msgid "Profiles|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "Profiles|%{provider} Active"
msgstr ""
@@ -20607,6 +20610,9 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
+msgid "Profiles|Busy"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -21219,7 +21225,10 @@ msgstr ""
msgid "ProjectSettings|Enable 'Delete source branch' option by default"
msgstr ""
-msgid "ProjectSettings|Enable merge trains and pipelines for merged results"
+msgid "ProjectSettings|Enable merge trains."
+msgstr ""
+
+msgid "ProjectSettings|Enable merged results pipelines."
msgstr ""
msgid "ProjectSettings|Encourage"
@@ -21261,6 +21270,9 @@ msgstr ""
msgid "ProjectSettings|Global"
msgstr ""
+msgid "ProjectSettings|If pipelines for merge requests are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches."
+msgstr ""
+
msgid "ProjectSettings|Internal"
msgstr ""
@@ -21321,9 +21333,6 @@ msgstr ""
msgid "ProjectSettings|Pipelines"
msgstr ""
-msgid "ProjectSettings|Pipelines for merge requests must be enabled in the CI/CD configuration file, or pipelines could be unresolvable or dropped"
-msgstr ""
-
msgid "ProjectSettings|Pipelines must succeed"
msgstr ""
@@ -21420,6 +21429,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
+msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
+msgstr ""
+
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
@@ -24734,9 +24746,15 @@ msgstr ""
msgid "SetPasswordToCloneLink|set a password"
msgstr ""
+msgid "SetStatusModal|\"Busy\" will be shown next to your name"
+msgstr ""
+
msgid "SetStatusModal|Add status emoji"
msgstr ""
+msgid "SetStatusModal|Busy"
+msgstr ""
+
msgid "SetStatusModal|Clear status"
msgstr ""
@@ -24755,6 +24773,9 @@ msgstr ""
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
+msgid "SetStatusModal|Status updated"
+msgstr ""
+
msgid "SetStatusModal|What's your status?"
msgstr ""
@@ -29489,6 +29510,12 @@ msgstr ""
msgid "User was successfully updated."
msgstr ""
+msgid "UserAvailability|%{author} (Busy)"
+msgstr ""
+
+msgid "UserAvailability|(Busy)"
+msgstr ""
+
msgid "UserLists|Add"
msgstr ""
@@ -29546,6 +29573,9 @@ msgstr ""
msgid "UserList|created %{timeago}"
msgstr ""
+msgid "UserProfile|(Busy)"
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
diff --git a/qa/qa/page/component/issuable/sidebar.rb b/qa/qa/page/component/issuable/sidebar.rb
index e41eb12a99d..cc39260ce38 100644
--- a/qa/qa/page/component/issuable/sidebar.rb
+++ b/qa/qa/page/component/issuable/sidebar.rb
@@ -65,15 +65,13 @@ module QA
def has_assignee?(username)
within_element(:assignee_block) do
- wait_until(reload: false) do
- has_text?(username)
- end
+ has_text?(username, wait: 120)
end
end
def has_no_assignee_named?(username)
within_element(:assignee_block) do
- has_no_text?(username, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
+ has_no_text?(username, wait: 120)
end
end
diff --git a/rubocop/cop/line_break_around_conditional_block.rb b/rubocop/cop/line_break_around_conditional_block.rb
deleted file mode 100644
index 2523cc162f3..00000000000
--- a/rubocop/cop/line_break_around_conditional_block.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-# frozen_string_literal: true
-
-module RuboCop
- module Cop
- # Ensures a line break around conditional blocks.
- #
- # @example
- # # bad
- # do_something
- # if condition
- # do_extra_stuff
- # end
- # do_something_more
- #
- # # good
- # do_something
- #
- # if condition
- # do_extra_stuff
- # end
- #
- # do_something_more
- #
- # # bad
- # do_something
- # unless condition
- # do_extra_stuff
- # end
- #
- # do_something_more
- #
- # # good
- # def a_method
- # if condition
- # do_something
- # end
- # end
- #
- # # good
- # on_block do
- # if condition
- # do_something
- # end
- # end
- class LineBreakAroundConditionalBlock < RuboCop::Cop::Cop
- include RangeHelp
-
- MSG = 'Add a line break around conditional blocks'
-
- def on_if(node)
- # This cop causes errors in haml files, so let's skip those
- return if in_haml?(node)
- return if node.single_line?
- return unless node.if? || node.unless?
-
- add_offense(node, location: :expression, message: MSG) unless previous_line_valid?(node)
- add_offense(node, location: :expression, message: MSG) unless last_line_valid?(node)
- end
-
- def autocorrect(node)
- lambda do |corrector|
- line = range_by_whole_lines(node.source_range)
- unless previous_line_valid?(node)
- corrector.insert_before(line, "\n")
- end
-
- unless last_line_valid?(node)
- corrector.insert_after(line, "\n")
- end
- end
- end
-
- private
-
- def previous_line_valid?(node)
- previous_line(node).empty? ||
- start_clause_line?(previous_line(node)) ||
- block_start?(previous_line(node)) ||
- begin_line?(previous_line(node)) ||
- assignment_line?(previous_line(node)) ||
- rescue_line?(previous_line(node))
- end
-
- def last_line_valid?(node)
- last_line(node).empty? ||
- end_line?(last_line(node)) ||
- end_clause_line?(last_line(node))
- end
-
- def previous_line(node)
- processed_source[node.loc.line - 2]
- end
-
- def last_line(node)
- processed_source[node.loc.last_line]
- end
-
- def start_clause_line?(line)
- line =~ /^\s*(def|=end|#|module|class|if|unless|else|elsif|ensure|when)/
- end
-
- def end_clause_line?(line)
- line =~ /^\s*(#|rescue|else|elsif|when)/
- end
-
- def begin_line?(line)
- # an assignment followed by a begin or ust a begin
- line =~ /^\s*(@?(\w|\|+|=|\[|\]|\s)+begin|begin)/
- end
-
- def assignment_line?(line)
- line =~ /^\s*.*=/
- end
-
- def rescue_line?(line)
- line =~ /^\s*rescue/
- end
-
- def block_start?(line)
- line.match(/ (do|{)( \|.*?\|)?\s?(#.+)?\z/)
- end
-
- def end_line?(line)
- line =~ /^\s*(end|})/
- end
-
- def in_haml?(node)
- node.location.expression.source_buffer.name.end_with?('.haml.rb')
- end
- end
- end
-end
diff --git a/spec/controllers/concerns/metrics_dashboard_spec.rb b/spec/controllers/concerns/metrics_dashboard_spec.rb
index 8a4d8828aaa..83546403ce5 100644
--- a/spec/controllers/concerns/metrics_dashboard_spec.rb
+++ b/spec/controllers/concerns/metrics_dashboard_spec.rb
@@ -155,6 +155,7 @@ RSpec.describe MetricsDashboard do
'.gitlab/dashboards/errors.yml' => dashboard_yml
}
end
+
let_it_be(:project) { create(:project, :custom_repo, files: dashboards) }
before do
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 3ae9a2b7555..8d1008b98a6 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -83,6 +83,7 @@ RSpec.describe 'Group milestones' do
description: 'Lorem Ipsum is simply dummy text'
)
end
+
let_it_be(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.1') }
let_it_be(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') }
let_it_be(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') }
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 697fccaca34..d0340dfc880 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -20,6 +20,10 @@ RSpec.describe 'User edit profile' do
wait_for_requests
end
+ def toggle_busy_status
+ find('[data-testid="user-availability-checkbox"]').set(true)
+ end
+
it 'changes user profile' do
fill_in 'user_skype', with: 'testskype'
fill_in 'user_linkedin', with: 'testlinkedin'
@@ -180,20 +184,51 @@ RSpec.describe 'User edit profile' do
expect(page).to have_emoji('speech_balloon')
end
end
+
+ it 'sets the users status to busy' do
+ busy_status = find('[data-testid="user-availability-checkbox"]')
+
+ expect(busy_status.checked?).to eq(false)
+
+ toggle_busy_status
+ submit_settings
+ visit profile_path
+
+ expect(busy_status.checked?).to eq(true)
+ end
+
+ context 'with set_user_availability_status feature flag disabled' do
+ before do
+ stub_feature_flags(set_user_availability_status: false)
+ visit root_path(user)
+ end
+
+ it 'does not display the availability checkbox' do
+ expect(page).not_to have_css('[data-testid="user-availability-checkbox"]')
+ end
+ end
end
context 'user menu' do
let(:issue) { create(:issue, project: project)}
let(:project) { create(:project) }
- def open_user_status_modal
+ def open_modal(button_text)
find('.header-user-dropdown-toggle').click
page.within ".header-user" do
- click_button 'Set status'
+ click_button button_text
end
end
+ def open_user_status_modal
+ open_modal 'Set status'
+ end
+
+ def open_edit_status_modal
+ open_modal 'Edit status'
+ end
+
def set_user_status_in_modal
page.within "#set-user-status-modal" do
click_button 'Set status'
@@ -246,6 +281,19 @@ RSpec.describe 'User edit profile' do
end
end
+ it 'sets the users status to busy' do
+ open_user_status_modal
+ busy_status = find('[data-testid="user-availability-checkbox"]')
+
+ expect(busy_status.checked?).to eq(false)
+
+ toggle_busy_status
+ set_user_status_in_modal
+ open_edit_status_modal
+
+ expect(busy_status.checked?).to eq(true)
+ end
+
it 'opens the emoji modal again after closing it' do
open_user_status_modal
select_emoji('biohazard', true)
@@ -307,11 +355,7 @@ RSpec.describe 'User edit profile' do
expect(page).to have_content user_status.message
end
- find('.header-user-dropdown-toggle').click
-
- page.within ".header-user" do
- click_button 'Edit status'
- end
+ open_edit_status_modal
find('.js-clear-user-status-button').click
set_user_status_in_modal
@@ -333,11 +377,7 @@ RSpec.describe 'User edit profile' do
expect(page).to have_content user_status.message
end
- find('.header-user-dropdown-toggle').click
-
- page.within ".header-user" do
- click_button 'Edit status'
- end
+ open_edit_status_modal
page.within "#set-user-status-modal" do
click_button 'Remove status'
@@ -357,6 +397,19 @@ RSpec.describe 'User edit profile' do
expect(page).to have_emoji('speech_balloon')
end
end
+
+ context 'with set_user_availability_status feature flag disabled' do
+ before do
+ stub_feature_flags(set_user_availability_status: false)
+ visit root_path(user)
+ end
+
+ it 'does not display the availability checkbox' do
+ open_user_status_modal
+
+ expect(page).not_to have_css('[data-testid="user-availability-checkbox"]')
+ end
+ end
end
context 'User time preferences', :js do
diff --git a/spec/finders/merge_requests/by_approvals_finder_spec.rb b/spec/finders/merge_requests/by_approvals_finder_spec.rb
index 0e1856879f1..5c56e610c0b 100644
--- a/spec/finders/merge_requests/by_approvals_finder_spec.rb
+++ b/spec/finders/merge_requests/by_approvals_finder_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe MergeRequests::ByApprovalsFinder do
create(:approval, merge_request: mr, user: first_user)
end
end
+
let_it_be(:merge_request_with_both_approvals) do
create(:merge_request).tap do |mr|
create(:approval, merge_request: mr, user: first_user)
diff --git a/spec/fixtures/api/schemas/internal/pages/lookup_path.json b/spec/fixtures/api/schemas/internal/pages/lookup_path.json
index a7793d679be..9d81ea495f1 100644
--- a/spec/fixtures/api/schemas/internal/pages/lookup_path.json
+++ b/spec/fixtures/api/schemas/internal/pages/lookup_path.json
@@ -15,7 +15,11 @@
"required": ["type", "path"],
"properties" : {
"type": { "type": "string", "enum": ["file", "zip"] },
- "path": { "type": "string" }
+ "path": { "type": "string" },
+ "global_id": { "type": "string" },
+ "sha256": { "type": "string" },
+ "file_size": { "type": "integer" },
+ "file_count": { "type": ["integer", "null"] }
},
"additionalProperties": false
},
diff --git a/spec/frontend/alerts_settings/alerts_integrations_list_spec.js b/spec/frontend/alerts_settings/alerts_integrations_list_spec.js
index 051caf0e49d..90bb38f0c2b 100644
--- a/spec/frontend/alerts_settings/alerts_integrations_list_spec.js
+++ b/spec/frontend/alerts_settings/alerts_integrations_list_spec.js
@@ -1,5 +1,6 @@
import { GlTable, GlIcon, GlButton } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
+import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import Tracking from '~/tracking';
import AlertIntegrationsList, {
i18n,
@@ -23,6 +24,7 @@ const mockIntegrations = [
describe('AlertIntegrationsList', () => {
let wrapper;
+ const { trigger: triggerIntersection } = useMockIntersectionObserver();
function mountComponent({ data = {}, props = {} } = {}) {
wrapper = mount(AlertIntegrationsList, {
@@ -100,12 +102,23 @@ describe('AlertIntegrationsList', () => {
describe('Snowplow tracking', () => {
beforeEach(() => {
- jest.spyOn(Tracking, 'event');
mountComponent();
+ jest.spyOn(Tracking, 'event');
+ });
+
+ it('should NOT track alert list page views when list is collapsed', () => {
+ triggerIntersection(wrapper.vm.$el, { entry: { isIntersecting: false } });
+
+ expect(Tracking.event).not.toHaveBeenCalled();
});
- it('should track alert list page views', () => {
+ it('should track alert list page views only once when list is expanded', () => {
+ triggerIntersection(wrapper.vm.$el, { entry: { isIntersecting: true } });
+ triggerIntersection(wrapper.vm.$el, { entry: { isIntersecting: true } });
+ triggerIntersection(wrapper.vm.$el, { entry: { isIntersecting: true } });
+
const { category, action } = trackAlertIntegrationsViewsOptions;
+ expect(Tracking.event).toHaveBeenCalledTimes(1);
expect(Tracking.event).toHaveBeenCalledWith(category, action);
});
});
diff --git a/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js
index c24a76f1df0..7384cf9a095 100644
--- a/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js
@@ -3,6 +3,7 @@ import { mount, createLocalVue } from '@vue/test-utils';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
+import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue';
import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue';
@@ -47,6 +48,7 @@ describe('AlertsSettingsWrapper', () => {
let wrapper;
let fakeApollo;
let destroyIntegrationHandler;
+ useMockIntersectionObserver();
const findLoader = () => wrapper.find(IntegrationsList).find(GlLoadingIcon);
const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr');
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 8da4320d993..e7025955e8e 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -378,6 +378,7 @@ describe('GfmAutoComplete', () => {
username: 'my-group',
title: '',
icon: '',
+ availabilityStatus: '',
}),
).toBe('<li>IMG my-group <small></small> </li>');
});
@@ -389,6 +390,7 @@ describe('GfmAutoComplete', () => {
username: 'my-group',
title: '',
icon: '<i class="icon"/>',
+ availabilityStatus: '',
}),
).toBe('<li>IMG my-group <small></small> <i class="icon"/></li>');
});
@@ -400,9 +402,24 @@ describe('GfmAutoComplete', () => {
username: 'my-group',
title: 'MyGroup+',
icon: '<i class="icon"/>',
+ availabilityStatus: '',
}),
).toBe('<li>IMG my-group <small>MyGroup+</small> <i class="icon"/></li>');
});
+
+ it('should add user availability status if availabilityStatus is set', () => {
+ expect(
+ GfmAutoComplete.Members.templateFunction({
+ avatarTag: 'IMG',
+ username: 'my-group',
+ title: '',
+ icon: '<i class="icon"/>',
+ availabilityStatus: '<span class="gl-text-gray-500"> (Busy)</span>',
+ }),
+ ).toBe(
+ '<li>IMG my-group <small><span class="gl-text-gray-500"> (Busy)</span></small> <i class="icon"/></li>',
+ );
+ });
});
describe('labels', () => {
diff --git a/spec/frontend/notes/components/note_header_spec.js b/spec/frontend/notes/components/note_header_spec.js
index 1cc4a2fa853..69aab0d051e 100644
--- a/spec/frontend/notes/components/note_header_spec.js
+++ b/spec/frontend/notes/components/note_header_spec.js
@@ -1,7 +1,9 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex';
+import { GlSprintf } from '@gitlab/ui';
import NoteHeader from '~/notes/components/note_header.vue';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -28,6 +30,9 @@ describe('NoteHeader component', () => {
path: '/root',
state: 'active',
username: 'root',
+ status: {
+ availability: '',
+ },
};
const createComponent = props => {
@@ -37,6 +42,7 @@ describe('NoteHeader component', () => {
actions,
}),
propsData: { ...props },
+ stubs: { GlSprintf },
});
};
@@ -97,6 +103,12 @@ describe('NoteHeader component', () => {
expect(wrapper.find('.js-user-link').exists()).toBe(true);
});
+ it('renders busy status if author availability is set', () => {
+ createComponent({ author: { ...author, status: { availability: AVAILABILITY_STATUS.BUSY } } });
+
+ expect(wrapper.find('.js-user-link').text()).toContain('(Busy)');
+ });
+
it('renders deleted user text if author is not passed as a prop', () => {
createComponent();
diff --git a/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
new file mode 100644
index 00000000000..fad23aa05a4
--- /dev/null
+++ b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
@@ -0,0 +1,257 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlModal, GlFormCheckbox } from '@gitlab/ui';
+import { initEmojiMock } from 'helpers/emoji';
+import Api from '~/api';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
+import SetStatusModalWrapper, {
+ AVAILABILITY_STATUS,
+} from '~/set_status_modal/set_status_modal_wrapper.vue';
+
+jest.mock('~/api');
+jest.mock('~/flash');
+
+describe('SetStatusModalWrapper', () => {
+ let wrapper;
+ let mockEmoji;
+ const $toast = {
+ show: jest.fn(),
+ };
+
+ const defaultEmoji = 'speech_balloon';
+ const defaultMessage = "They're comin' in too fast!";
+
+ const defaultProps = {
+ currentEmoji: defaultEmoji,
+ currentMessage: defaultMessage,
+ defaultEmoji,
+ canSetUserAvailability: true,
+ };
+
+ const createComponent = (props = {}) => {
+ return shallowMount(SetStatusModalWrapper, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ mocks: {
+ $toast,
+ },
+ });
+ };
+
+ const findModal = () => wrapper.find(GlModal);
+ const findFormField = field => wrapper.find(`[name="user[status][${field}]"]`);
+ const findClearStatusButton = () => wrapper.find('.js-clear-user-status-button');
+ const findNoEmojiPlaceholder = () => wrapper.find('.js-no-emoji-placeholder');
+ const findToggleEmojiButton = () => wrapper.find('.js-toggle-emoji-menu');
+ const findAvailabilityCheckbox = () => wrapper.find(GlFormCheckbox);
+
+ const initModal = ({ mockOnUpdateSuccess = true, mockOnUpdateFailure = true } = {}) => {
+ const modal = findModal();
+ // mock internal emoji methods
+ wrapper.vm.showEmojiMenu = jest.fn();
+ wrapper.vm.hideEmojiMenu = jest.fn();
+ if (mockOnUpdateSuccess) wrapper.vm.onUpdateSuccess = jest.fn();
+ if (mockOnUpdateFailure) wrapper.vm.onUpdateFail = jest.fn();
+
+ modal.vm.$emit('shown');
+ return wrapper.vm.$nextTick();
+ };
+
+ beforeEach(async () => {
+ mockEmoji = await initEmojiMock();
+ wrapper = createComponent();
+ return initModal();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mockEmoji.restore();
+ });
+
+ describe('with minimum props', () => {
+ it('sets the hidden status emoji field', () => {
+ const field = findFormField('emoji');
+ expect(field.exists()).toBe(true);
+ expect(field.element.value).toBe(defaultEmoji);
+ });
+
+ it('sets the message field', () => {
+ const field = findFormField('message');
+ expect(field.exists()).toBe(true);
+ expect(field.element.value).toBe(defaultMessage);
+ });
+
+ it('sets the availability field to false', () => {
+ const field = findAvailabilityCheckbox();
+ expect(field.exists()).toBe(true);
+ expect(field.element.checked).toBeUndefined();
+ });
+
+ it('has a clear status button', () => {
+ expect(findClearStatusButton().isVisible()).toBe(true);
+ });
+
+ it('clicking the toggle emoji button displays the emoji list', () => {
+ expect(wrapper.vm.showEmojiMenu).not.toHaveBeenCalled();
+ findToggleEmojiButton().trigger('click');
+ expect(wrapper.vm.showEmojiMenu).toHaveBeenCalled();
+ });
+ });
+
+ describe('with no currentMessage set', () => {
+ beforeEach(async () => {
+ mockEmoji = await initEmojiMock();
+ wrapper = createComponent({ currentMessage: '' });
+ return initModal();
+ });
+
+ it('does not set the message field', () => {
+ expect(findFormField('message').element.value).toBe('');
+ });
+
+ it('hides the clear status button', () => {
+ expect(findClearStatusButton().isVisible()).toBe(false);
+ });
+
+ it('shows the placeholder emoji', () => {
+ expect(findNoEmojiPlaceholder().isVisible()).toBe(true);
+ });
+ });
+
+ describe('with no currentEmoji set', () => {
+ beforeEach(async () => {
+ mockEmoji = await initEmojiMock();
+ wrapper = createComponent({ currentEmoji: '' });
+ return initModal();
+ });
+
+ it('does not set the hidden status emoji field', () => {
+ expect(findFormField('emoji').element.value).toBe('');
+ });
+
+ it('hides the placeholder emoji', () => {
+ expect(findNoEmojiPlaceholder().isVisible()).toBe(false);
+ });
+
+ describe('with no currentMessage set', () => {
+ beforeEach(async () => {
+ mockEmoji = await initEmojiMock();
+ wrapper = createComponent({ currentEmoji: '', currentMessage: '' });
+ return initModal();
+ });
+
+ it('shows the placeholder emoji', () => {
+ expect(findNoEmojiPlaceholder().isVisible()).toBe(true);
+ });
+ });
+ });
+
+ describe('update status', () => {
+ describe('succeeds', () => {
+ beforeEach(() => {
+ jest.spyOn(Api, 'postUserStatus').mockResolvedValue();
+ });
+
+ it('clicking "removeStatus" clears the emoji and message fields', async () => {
+ findModal().vm.$emit('cancel');
+ await wrapper.vm.$nextTick();
+
+ expect(findFormField('message').element.value).toBe('');
+ expect(findFormField('emoji').element.value).toBe('');
+ });
+
+ it('clicking "setStatus" submits the user status', async () => {
+ findModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ // set the availability status
+ findAvailabilityCheckbox().vm.$emit('input', true);
+
+ findModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ const commonParams = { emoji: defaultEmoji, message: defaultMessage };
+
+ expect(Api.postUserStatus).toHaveBeenCalledTimes(2);
+ expect(Api.postUserStatus).toHaveBeenNthCalledWith(1, {
+ availability: AVAILABILITY_STATUS.NOT_SET,
+ ...commonParams,
+ });
+ expect(Api.postUserStatus).toHaveBeenNthCalledWith(2, {
+ availability: AVAILABILITY_STATUS.BUSY,
+ ...commonParams,
+ });
+ });
+
+ it('calls the "onUpdateSuccess" handler', async () => {
+ findModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.onUpdateSuccess).toHaveBeenCalled();
+ });
+ });
+
+ describe('success message', () => {
+ beforeEach(async () => {
+ mockEmoji = await initEmojiMock();
+ wrapper = createComponent({ currentEmoji: '', currentMessage: '' });
+ jest.spyOn(Api, 'postUserStatus').mockResolvedValue();
+ return initModal({ mockOnUpdateSuccess: false });
+ });
+
+ it('displays a toast success message', async () => {
+ findModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect($toast.show).toHaveBeenCalledWith('Status updated', {
+ position: 'top-center',
+ type: 'success',
+ });
+ });
+ });
+
+ describe('with errors', () => {
+ beforeEach(() => {
+ jest.spyOn(Api, 'postUserStatus').mockRejectedValue();
+ });
+
+ it('calls the "onUpdateFail" handler', async () => {
+ findModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.onUpdateFail).toHaveBeenCalled();
+ });
+ });
+
+ describe('error message', () => {
+ beforeEach(async () => {
+ mockEmoji = await initEmojiMock();
+ wrapper = createComponent({ currentEmoji: '', currentMessage: '' });
+ jest.spyOn(Api, 'postUserStatus').mockRejectedValue();
+ return initModal({ mockOnUpdateFailure: false });
+ });
+
+ it('flashes an error message', async () => {
+ findModal().vm.$emit('ok');
+ await wrapper.vm.$nextTick();
+
+ expect(createFlash).toHaveBeenCalledWith(
+ "Sorry, we weren't able to set your status. Please try again later.",
+ );
+ });
+ });
+ });
+
+ describe('with canSetUserAvailability=false', () => {
+ beforeEach(async () => {
+ mockEmoji = await initEmojiMock();
+ wrapper = createComponent({ canSetUserAvailability: false });
+ return initModal();
+ });
+
+ it('hides the set availability checkbox', () => {
+ expect(findAvailabilityCheckbox().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/set_status_modal/user_availability_status_spec.js b/spec/frontend/set_status_modal/user_availability_status_spec.js
new file mode 100644
index 00000000000..95ca0251ce0
--- /dev/null
+++ b/spec/frontend/set_status_modal/user_availability_status_spec.js
@@ -0,0 +1,31 @@
+import { shallowMount } from '@vue/test-utils';
+import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+
+describe('UserAvailabilityStatus', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ return shallowMount(UserAvailabilityStatus, {
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with availability status', () => {
+ it(`set to ${AVAILABILITY_STATUS.BUSY}`, () => {
+ wrapper = createComponent({ availability: AVAILABILITY_STATUS.BUSY });
+ expect(wrapper.text()).toContain('(Busy)');
+ });
+
+ it(`set to ${AVAILABILITY_STATUS.NOT_SET}`, () => {
+ wrapper = createComponent({ availability: AVAILABILITY_STATUS.NOT_SET });
+ expect(wrapper.html()).toBe('');
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
index c208d7b0226..7d58a865ba3 100644
--- a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
@@ -1,6 +1,8 @@
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlSprintf, GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue';
+import UserAvailabilityStatus from '~/set_status_modal/components/user_availability_status.vue';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
const DEFAULT_PROPS = {
user: {
@@ -34,6 +36,7 @@ describe('User Popover Component', () => {
const findByTestId = testid => wrapper.find(`[data-testid="${testid}"]`);
const findUserStatus = () => wrapper.find('.js-user-status');
const findTarget = () => document.querySelector('.js-user-link');
+ const findAvailabilityStatus = () => wrapper.find(UserAvailabilityStatus);
const createWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(UserPopover, {
@@ -43,7 +46,8 @@ describe('User Popover Component', () => {
...props,
},
stubs: {
- 'gl-sprintf': GlSprintf,
+ GlSprintf,
+ UserAvailabilityStatus,
},
...options,
});
@@ -199,6 +203,30 @@ describe('User Popover Component', () => {
expect(findUserStatus().exists()).toBe(false);
});
+
+ it('should show the busy status if user set to busy', () => {
+ const user = {
+ ...DEFAULT_PROPS.user,
+ status: { availability: AVAILABILITY_STATUS.BUSY },
+ };
+
+ createWrapper({ user });
+
+ expect(findAvailabilityStatus().exists()).toBe(true);
+ expect(wrapper.text()).toContain(user.name);
+ expect(wrapper.text()).toContain('(Busy)');
+ });
+
+ it('should hide the busy status for any other status', () => {
+ const user = {
+ ...DEFAULT_PROPS.user,
+ status: { availability: AVAILABILITY_STATUS.NOT_SET },
+ };
+
+ createWrapper({ user });
+
+ expect(wrapper.text()).not.toContain('(Busy)');
+ });
});
describe('security bot', () => {
diff --git a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
index ae3097c1d9e..deb5ff584cf 100644
--- a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe Resolvers::MergeRequestPipelinesResolver do
sha: merge_request.diff_head_sha
)
end
+
let_it_be(:other_project_pipeline) { create(:ci_pipeline, project: merge_request.source_project, ref: 'other-ref') }
let_it_be(:other_pipeline) { create(:ci_pipeline) }
let(:current_user) { create(:user) }
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 302ab0cc137..6c5855eeb91 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe MarkupHelper do
project.add_maintainer(user)
user
end
+
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let_it_be(:snippet) { create(:project_snippet, project: project) }
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index c213ad7ee59..99cdee6dbb2 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -221,4 +221,42 @@ RSpec.describe PageLayoutHelper do
end
end
end
+
+ describe '#user_status_properties' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:user) { build(:user) }
+
+ availability_types = Types::AvailabilityEnum.enum
+
+ where(:message, :emoji, :availability) do
+ "Some message" | UserStatus::DEFAULT_EMOJI | availability_types[:busy]
+ "Some message" | UserStatus::DEFAULT_EMOJI | availability_types[:not_set]
+ "Some message" | "basketball" | availability_types[:busy]
+ "Some message" | "basketball" | availability_types[:not_set]
+ "Some message" | "" | availability_types[:busy]
+ "Some message" | "" | availability_types[:not_set]
+ "" | UserStatus::DEFAULT_EMOJI | availability_types[:busy]
+ "" | UserStatus::DEFAULT_EMOJI | availability_types[:not_set]
+ "" | "basketball" | availability_types[:busy]
+ "" | "basketball" | availability_types[:not_set]
+ "" | "" | availability_types[:busy]
+ "" | "" | availability_types[:not_set]
+ end
+
+ with_them do
+ it "sets the default user status fields" do
+ user.status = UserStatus.new(message: message, emoji: emoji, availability: availability)
+ result = {
+ can_set_user_availability: true,
+ current_availability: availability,
+ current_emoji: emoji,
+ current_message: message,
+ default_emoji: UserStatus::DEFAULT_EMOJI
+ }
+
+ expect(helper.user_status_properties(user)).to eq(result)
+ end
+ end
+ end
end
diff --git a/spec/helpers/profiles_helper_spec.rb b/spec/helpers/profiles_helper_spec.rb
index 61b7ff94edb..9687d038162 100644
--- a/spec/helpers/profiles_helper_spec.rb
+++ b/spec/helpers/profiles_helper_spec.rb
@@ -80,6 +80,38 @@ RSpec.describe ProfilesHelper do
end
end
+ describe "#user_status_set_to_busy?" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:availability, :result) do
+ "busy" | true
+ "not_set" | false
+ "" | false
+ nil | false
+ end
+
+ with_them do
+ it { expect(helper.user_status_set_to_busy?(OpenStruct.new(availability: availability))).to eq(result) }
+ end
+ end
+
+ describe "#show_status_emoji?" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:message, :emoji, :result) do
+ "Some message" | UserStatus::DEFAULT_EMOJI | true
+ "Some message" | "" | true
+ "" | "basketball" | true
+ "" | "basketball" | true
+ "" | UserStatus::DEFAULT_EMOJI | false
+ "" | UserStatus::DEFAULT_EMOJI | false
+ end
+
+ with_them do
+ it { expect(helper.show_status_emoji?(OpenStruct.new(message: message, emoji: emoji))).to eq(result) }
+ end
+ end
+
def stub_cas_omniauth_provider
provider = OpenStruct.new(
'name' => 'cas3',
diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb
index 6b658a475b1..9481d756c16 100644
--- a/spec/helpers/todos_helper_spec.rb
+++ b/spec/helpers/todos_helper_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe TodosHelper do
project: issue.project,
note: 'I am note, hear me roar')
end
+
let_it_be(:design_todo) do
create(:todo, :mentioned,
user: user,
@@ -20,6 +21,7 @@ RSpec.describe TodosHelper do
author: author,
note: note)
end
+
let_it_be(:alert_todo) do
alert = create(:alert_management_alert, iid: 1001)
create(:todo, target: alert)
diff --git a/spec/lib/banzai/reference_parser/design_parser_spec.rb b/spec/lib/banzai/reference_parser/design_parser_spec.rb
index 92d3a4aaad2..a9cb2952c26 100644
--- a/spec/lib/banzai/reference_parser/design_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/design_parser_spec.rb
@@ -29,9 +29,11 @@ RSpec.describe Banzai::ReferenceParser::DesignParser do
let_it_be(:other_project_link) do
design_link(create(:design, :with_versions))
end
+
let_it_be(:public_link) do
design_link(create(:design, :with_versions, issue: create(:issue, project: public_project)))
end
+
let_it_be(:public_but_confidential_link) do
design_link(create(:design, :with_versions, issue: create(:issue, :confidential, project: public_project)))
end
diff --git a/spec/lib/gitlab/chat/output_spec.rb b/spec/lib/gitlab/chat/output_spec.rb
index 38e17c39fad..34f6bc0904c 100644
--- a/spec/lib/gitlab/chat/output_spec.rb
+++ b/spec/lib/gitlab/chat/output_spec.rb
@@ -8,62 +8,67 @@ RSpec.describe Gitlab::Chat::Output do
end
let(:output) { described_class.new(build) }
+ let(:trace) { Gitlab::Ci::Trace.new(build) }
+
+ before do
+ trace.set("\e[0KRunning with gitlab-runner 13.4.0~beta.108.g2ed41114 (2ed41114)
+\e[0;m\e[0K on GDK local runner g_XWCUS4
+\e[0;msection_start:1604068171:resolve_secrets\r\e[0K\e[0K\e[36;1mResolving secrets\e[0;m
+\e[0;msection_end:1604068171:resolve_secrets\r\e[0Ksection_start:1604068171:prepare_executor\r\e[0K\e[0K\e[36;1mPreparing the \"docker\" executor\e[0;m
+\e[0;m\e[0KUsing Docker executor with image ubuntu:20.04 ...
+\e[0;m\e[0KUsing locally found image version due to if-not-present pull policy
+\e[0;m\e[0KUsing docker image sha256:d70eaf7277eada08fca944de400e7e4dd97b1262c06ed2b1011500caa4decaf1 for ubuntu:20.04 with digest ubuntu@sha256:fff16eea1a8ae92867721d90c59a75652ea66d29c05294e6e2f898704bdb8cf1 ...
+\e[0;msection_end:1604068172:prepare_executor\r\e[0Ksection_start:1604068172:prepare_script\r\e[0K\e[0K\e[36;1mPreparing environment\e[0;m
+\e[0;mRunning on runner-gxwcus4-project-21-concurrent-0 via MacBook-Pro.local...
+section_end:1604068173:prepare_script\r\e[0Ksection_start:1604068173:get_sources\r\e[0K\e[0K\e[36;1mGetting source from Git repository\e[0;m
+\e[0;m\e[32;1mFetching changes with git depth set to 50...\e[0;m
+Initialized empty Git repository in /builds/267388-group-1/playground/.git/
+\e[32;1mCreated fresh repository.\e[0;m
+\e[32;1mChecking out 6c8eb7f4 as master...\e[0;m
+
+\e[32;1mSkipping Git submodules setup\e[0;m
+section_end:1604068175:get_sources\r\e[0Ksection_start:1604068175:step_script\r\e[0K\e[0K\e[36;1mExecuting \"step_script\" stage of the job script\e[0;m
+\e[0;m\e[32;1m$ echo \"success!\"\e[0;m
+success!
+section_end:1604068175:step_script\r\e[0Ksection_start:1604068175:chat_reply\r\033[0K
+Chat Reply
+section_end:1604068176:chat_reply\r\033[0K\e[32;1mJob succeeded
+\e[0;m")
+ end
describe '#to_s' do
- it 'returns the build output as a String' do
- trace = Gitlab::Ci::Trace.new(build)
-
- trace.set("echo hello\nhello")
-
- allow(build)
- .to receive(:trace)
- .and_return(trace)
-
- allow(output)
- .to receive(:read_offset_and_length)
- .and_return([0, 13])
-
- expect(output.to_s).to eq('he')
+ it 'returns the chat reply as a String' do
+ expect(output.to_s).to eq("Chat Reply")
end
- end
- describe '#read_offset_and_length' do
context 'without the chat_reply trace section' do
- it 'falls back to using the build_script trace section' do
- expect(output)
- .to receive(:find_build_trace_section)
- .with('chat_reply')
- .and_return(nil)
-
- expect(output)
- .to receive(:find_build_trace_section)
- .with('build_script')
- .and_return({ name: 'build_script', byte_start: 1, byte_end: 4 })
-
- expect(output.read_offset_and_length).to eq([1, 3])
+ before do
+ trace.set(trace.raw.gsub('chat_reply', 'not_found'))
end
- end
- context 'without the build_script trace section' do
- it 'raises MissingBuildSectionError' do
- expect { output.read_offset_and_length }
- .to raise_error(described_class::MissingBuildSectionError)
+ it 'falls back to using the step_script trace section' do
+ expect(output.to_s).to eq("\e[0;m\e[32;1m$ echo \"success!\"\e[0;m\nsuccess!")
end
- end
-
- context 'with the chat_reply trace section' do
- it 'returns the read offset and length as an Array' do
- trace = Gitlab::Ci::Trace.new(build)
-
- allow(build)
- .to receive(:trace)
- .and_return(trace)
-
- allow(trace)
- .to receive(:extract_sections)
- .and_return([{ name: 'chat_reply', byte_start: 1, byte_end: 4 }])
- expect(output.read_offset_and_length).to eq([1, 3])
+ context 'without the step_script trace section' do
+ before do
+ trace.set(trace.raw.gsub('step_script', 'build_script'))
+ end
+
+ it 'falls back to using the build_script trace section' do
+ expect(output.to_s).to eq("\e[0;m\e[32;1m$ echo \"success!\"\e[0;m\nsuccess!")
+ end
+
+ context 'without the build_script trace section' do
+ before do
+ trace.set(trace.raw.gsub('build_script', 'not_found'))
+ end
+
+ it 'raises MissingBuildSectionError' do
+ expect { output.to_s }
+ .to raise_error(described_class::MissingBuildSectionError)
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
index 949cfb5a34d..762687beedb 100644
--- a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe Gitlab::ImportExport::JSON::StreamingSerializer do
group: group,
approvals_before_merge: 1)
end
+
let_it_be(:issue) do
create(:issue,
assignees: [user],
diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
index b6a396afd61..fd3b71deb37 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -973,6 +973,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
create(:project, :builds_disabled, :issues_disabled,
{ name: 'project', path: 'project' })
end
+
let(:shared) { project.import_export_shared }
let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
diff --git a/spec/lib/gitlab/relative_positioning/mover_spec.rb b/spec/lib/gitlab/relative_positioning/mover_spec.rb
index dafd34585a8..cbb15ae876d 100644
--- a/spec/lib/gitlab/relative_positioning/mover_spec.rb
+++ b/spec/lib/gitlab/relative_positioning/mover_spec.rb
@@ -32,6 +32,7 @@ RSpec.describe RelativePositioning::Mover do
let_it_be(:one_free_space_set) do
indices.drop(1).map { |iid| create(:issue, project: one_free_space, iid: iid.succ) }
end
+
let_it_be(:three_sibs_set) do
[1, 2, 3].map { |iid| create(:issue, iid: iid, project: three_sibs) }
end
diff --git a/spec/migrations/reseed_merge_trains_enabled_spec.rb b/spec/migrations/reseed_merge_trains_enabled_spec.rb
new file mode 100644
index 00000000000..71ef0b47da9
--- /dev/null
+++ b/spec/migrations/reseed_merge_trains_enabled_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20201112195322_reseed_merge_trains_enabled.rb')
+
+RSpec.describe ReseedMergeTrainsEnabled do
+ describe 'migrate' do
+ let(:project_ci_cd_settings) { table(:project_ci_cd_settings) }
+ let(:projects) { table(:projects) }
+ let(:namespaces) { table(:namespaces) }
+
+ context 'when on Gitlab.com' do
+ before do
+ namespace = namespaces.create!(name: 'hello', path: 'hello/')
+ project1 = projects.create!(namespace_id: namespace.id)
+ project2 = projects.create!(namespace_id: namespace.id)
+ project_ci_cd_settings.create!(project_id: project1.id, merge_pipelines_enabled: true)
+ project_ci_cd_settings.create!(project_id: project2.id, merge_pipelines_enabled: false)
+ end
+
+ it 'updates merge_trains_enabled to true for where merge_pipelines_enabled is true' do
+ expect { migrate! }.to change(project_ci_cd_settings.where(merge_trains_enabled: true), :count).by(1)
+ end
+ end
+ end
+end
diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb
index bd890a71dfd..f8ebc237577 100644
--- a/spec/models/pages/lookup_path_spec.rb
+++ b/spec/models/pages/lookup_path_spec.rb
@@ -65,11 +65,18 @@ RSpec.describe Pages::LookupPath do
project.pages_metadatum.update!(pages_deployment: deployment)
end
- it 'uses deployment from object storage', :aggregate_failures do
+ it 'uses deployment from object storage' do
Timecop.freeze do
- expect(source[:type]).to eq('zip')
- expect(source[:path]).to eq(deployment.file.url(expire_at: 1.day.from_now))
- expect(source[:path]).to include("Expires=86400")
+ expect(source).to(
+ eq({
+ type: 'zip',
+ path: deployment.file.url(expire_at: 1.day.from_now),
+ global_id: "gid://gitlab/PagesDeployment/#{deployment.id}",
+ sha256: deployment.file_sha256,
+ file_size: deployment.size,
+ file_count: deployment.file_count
+ })
+ )
end
end
@@ -78,10 +85,18 @@ RSpec.describe Pages::LookupPath do
deployment.file.migrate!(::ObjectStorage::Store::LOCAL)
end
- it 'uses file protocol', :aggregate_failures do
+ it 'uses file protocol' do
Timecop.freeze do
- expect(source[:type]).to eq('zip')
- expect(source[:path]).to eq('file://' + deployment.file.path)
+ expect(source).to(
+ eq({
+ type: 'zip',
+ path: 'file://' + deployment.file.path,
+ global_id: "gid://gitlab/PagesDeployment/#{deployment.id}",
+ sha256: deployment.file_sha256,
+ file_size: deployment.size,
+ file_count: deployment.file_count
+ })
+ )
end
end
@@ -110,11 +125,18 @@ RSpec.describe Pages::LookupPath do
project.mark_pages_as_deployed(artifacts_archive: artifacts_archive)
end
- it 'uses artifacts object storage', :aggregate_failures do
+ it 'uses artifacts object storage' do
Timecop.freeze do
- expect(source[:type]).to eq('zip')
- expect(source[:path]).to eq(artifacts_archive.file.url(expire_at: 1.day.from_now))
- expect(source[:path]).to include("Expires=86400")
+ expect(source).to(
+ eq({
+ type: 'zip',
+ path: artifacts_archive.file.url(expire_at: 1.day.from_now),
+ global_id: "gid://gitlab/Ci::JobArtifact/#{artifacts_archive.id}",
+ sha256: artifacts_archive.file_sha256,
+ file_size: artifacts_archive.size,
+ file_count: nil
+ })
+ )
end
end
@@ -123,8 +145,16 @@ RSpec.describe Pages::LookupPath do
it 'uses file protocol', :aggregate_failures do
Timecop.freeze do
- expect(source[:type]).to eq('zip')
- expect(source[:path]).to eq('file://' + artifacts_archive.file.path)
+ expect(source).to(
+ eq({
+ type: 'zip',
+ path: 'file://' + artifacts_archive.file.path,
+ global_id: "gid://gitlab/Ci::JobArtifact/#{artifacts_archive.id}",
+ sha256: artifacts_archive.file_sha256,
+ file_size: artifacts_archive.size,
+ file_count: nil
+ })
+ )
end
end
diff --git a/spec/requests/api/admin/instance_clusters_spec.rb b/spec/requests/api/admin/instance_clusters_spec.rb
index 9d0661089a9..1052080aad4 100644
--- a/spec/requests/api/admin/instance_clusters_spec.rb
+++ b/spec/requests/api/admin/instance_clusters_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe ::API::Admin::InstanceClusters do
user: admin_user,
projects: [project])
end
+
let(:project_cluster_id) { project_cluster.id }
describe "GET /admin/clusters" do
diff --git a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
index ca5a9165760..72ec2b8e070 100644
--- a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
+++ b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe 'Getting Metrics Dashboard Annotations' do
let_it_be(:to_old_annotation) do
create(:metrics_dashboard_annotation, environment: environment, starting_at: Time.parse(from).advance(minutes: -5), dashboard_path: path)
end
+
let_it_be(:to_new_annotation) do
create(:metrics_dashboard_annotation, environment: environment, starting_at: to.advance(minutes: 5), dashboard_path: path)
end
diff --git a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb
index 634ce58da96..713b26a6a9b 100644
--- a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe 'Updating an image DiffNote' do
position_type: 'image'
)
end
+
let_it_be(:updated_body) { 'Updated body' }
let_it_be(:updated_width) { 50 }
let_it_be(:updated_height) { 100 }
diff --git a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
index 1b654e660e3..4bce3c7fe0f 100644
--- a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
+++ b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
create(:design_version, issue: issue,
created_designs: create_list(:design, 3, issue: issue))
end
+
let_it_be(:version) do
create(:design_version, issue: issue,
modified_designs: old_version.designs,
diff --git a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
index 640ac95cd86..ee0085718b3 100644
--- a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
+++ b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
@@ -11,12 +11,15 @@ RSpec.describe 'Getting versions related to an issue' do
let_it_be(:version_a) do
create(:design_version, issue: issue)
end
+
let_it_be(:version_b) do
create(:design_version, issue: issue)
end
+
let_it_be(:version_c) do
create(:design_version, issue: issue)
end
+
let_it_be(:version_d) do
create(:design_version, issue: issue)
end
diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb
index 1cc30b95162..98a3f08baa6 100644
--- a/spec/requests/api/graphql/project/jira_import_spec.rb
+++ b/spec/requests/api/graphql/project/jira_import_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe 'query Jira import data' do
total_issue_count: 4
)
end
+
let_it_be(:jira_import2) do
create(
:jira_import_state, :finished,
@@ -31,6 +32,7 @@ RSpec.describe 'query Jira import data' do
total_issue_count: 3
)
end
+
let(:query) do
%(
query {
diff --git a/spec/requests/api/graphql/user/group_member_query_spec.rb b/spec/requests/api/graphql/user/group_member_query_spec.rb
index 3a16d962214..e47cef8cc37 100644
--- a/spec/requests/api/graphql/user/group_member_query_spec.rb
+++ b/spec/requests/api/graphql/user/group_member_query_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe 'GroupMember' do
}
HEREDOC
end
+
let_it_be(:query) do
graphql_query_for('user', { id: member.user.to_global_id.to_s }, query_graphql_field("groupMemberships", {}, fields))
end
diff --git a/spec/requests/api/graphql/user/project_member_query_spec.rb b/spec/requests/api/graphql/user/project_member_query_spec.rb
index 0790e148caf..01827e94d5d 100644
--- a/spec/requests/api/graphql/user/project_member_query_spec.rb
+++ b/spec/requests/api/graphql/user/project_member_query_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe 'ProjectMember' do
}
HEREDOC
end
+
let_it_be(:query) do
graphql_query_for('user', { id: member.user.to_global_id.to_s }, query_graphql_field("projectMemberships", {}, fields))
end
diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb
index d64fd3868c2..738e120549e 100644
--- a/spec/requests/api/graphql/user_query_spec.rb
+++ b/spec/requests/api/graphql/user_query_spec.rb
@@ -32,22 +32,27 @@ RSpec.describe 'getting user information' do
create(:merge_request, :unique_branches, :unique_author,
source_project: project_a, assignees: [user])
end
+
let_it_be(:assigned_mr_b) do
create(:merge_request, :unique_branches, :unique_author,
source_project: project_b, assignees: [user])
end
+
let_it_be(:assigned_mr_c) do
create(:merge_request, :unique_branches, :unique_author,
source_project: project_b, assignees: [user])
end
+
let_it_be(:authored_mr) do
create(:merge_request, :unique_branches,
source_project: project_a, author: user)
end
+
let_it_be(:authored_mr_b) do
create(:merge_request, :unique_branches,
source_project: project_b, author: user)
end
+
let_it_be(:authored_mr_c) do
create(:merge_request, :unique_branches,
source_project: project_b, author: user)
diff --git a/spec/requests/api/internal/pages_spec.rb b/spec/requests/api/internal/pages_spec.rb
index 7f17f22b007..9a63e2a8ed5 100644
--- a/spec/requests/api/internal/pages_spec.rb
+++ b/spec/requests/api/internal/pages_spec.rb
@@ -191,6 +191,7 @@ RSpec.describe API::Internal::Pages do
expect(json_response['certificate']).to eq(pages_domain.certificate)
expect(json_response['key']).to eq(pages_domain.key)
+ deployment = project.pages_metadatum.pages_deployment
expect(json_response['lookup_paths']).to eq(
[
{
@@ -200,7 +201,11 @@ RSpec.describe API::Internal::Pages do
'prefix' => '/',
'source' => {
'type' => 'zip',
- 'path' => project.pages_metadatum.pages_deployment.file.url(expire_at: 1.day.from_now)
+ 'path' => deployment.file.url(expire_at: 1.day.from_now),
+ 'global_id' => "gid://gitlab/PagesDeployment/#{deployment.id}",
+ 'sha256' => deployment.file_sha256,
+ 'file_size' => deployment.size,
+ 'file_count' => deployment.file_count
}
}
]
@@ -227,6 +232,7 @@ RSpec.describe API::Internal::Pages do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
+ deployment = project.pages_metadatum.pages_deployment
expect(json_response['lookup_paths']).to eq(
[
{
@@ -236,7 +242,11 @@ RSpec.describe API::Internal::Pages do
'prefix' => '/myproject/',
'source' => {
'type' => 'zip',
- 'path' => project.pages_metadatum.pages_deployment.file.url(expire_at: 1.day.from_now)
+ 'path' => deployment.file.url(expire_at: 1.day.from_now),
+ 'global_id' => "gid://gitlab/PagesDeployment/#{deployment.id}",
+ 'sha256' => deployment.file_sha256,
+ 'file_size' => deployment.size,
+ 'file_count' => deployment.file_count
}
}
]
@@ -268,6 +278,7 @@ RSpec.describe API::Internal::Pages do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('internal/pages/virtual_domain')
+ deployment = project.pages_metadatum.pages_deployment
expect(json_response['lookup_paths']).to eq(
[
{
@@ -277,7 +288,11 @@ RSpec.describe API::Internal::Pages do
'prefix' => '/',
'source' => {
'type' => 'zip',
- 'path' => project.pages_metadatum.pages_deployment.file.url(expire_at: 1.day.from_now)
+ 'path' => deployment.file.url(expire_at: 1.day.from_now),
+ 'global_id' => "gid://gitlab/PagesDeployment/#{deployment.id}",
+ 'sha256' => deployment.file_sha256,
+ 'file_size' => deployment.size,
+ 'file_count' => deployment.file_count
}
}
]
diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb
index 4228ca2d5fd..da0bae8d5e7 100644
--- a/spec/requests/api/issues/get_project_issues_spec.rb
+++ b/spec/requests/api/issues/get_project_issues_spec.rb
@@ -54,11 +54,13 @@ RSpec.describe API::Issues do
let_it_be(:label) do
create(:label, title: 'label', color: '#FFAABB', project: project)
end
+
let!(:label_link) { create(:label_link, label: label, target: issue) }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
let_it_be(:empty_milestone) do
create(:milestone, title: '2.0.0', project: project)
end
+
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
let(:no_milestone_title) { 'None' }
diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb
index b8cbddd9ed4..0fe68be027c 100644
--- a/spec/requests/api/issues/issues_spec.rb
+++ b/spec/requests/api/issues/issues_spec.rb
@@ -54,11 +54,13 @@ RSpec.describe API::Issues do
let_it_be(:label) do
create(:label, title: 'label', color: '#FFAABB', project: project)
end
+
let!(:label_link) { create(:label_link, label: label, target: issue) }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
let_it_be(:empty_milestone) do
create(:milestone, title: '2.0.0', project: project)
end
+
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
let(:no_milestone_title) { 'None' }
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index a7fe4d4509a..5b3e2363669 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -53,11 +53,13 @@ RSpec.describe API::Issues do
let_it_be(:label) do
create(:label, title: 'label', color: '#FFAABB', project: project)
end
+
let!(:label_link) { create(:label_link, label: label, target: issue) }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
let_it_be(:empty_milestone) do
create(:milestone, title: '2.0.0', project: project)
end
+
let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
let(:no_milestone_title) { 'None' }
diff --git a/spec/requests/projects/metrics/dashboards/builder_spec.rb b/spec/requests/projects/metrics/dashboards/builder_spec.rb
index e59ed591f63..002acca2135 100644
--- a/spec/requests/projects/metrics/dashboards/builder_spec.rb
+++ b/spec/requests/projects/metrics/dashboards/builder_spec.rb
@@ -32,6 +32,7 @@ RSpec.describe 'Projects::Metrics::Dashboards::BuilderController' do
label: Legend Label
YML
end
+
let_it_be(:invalid_panel_yml) do
<<~YML
---
diff --git a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
deleted file mode 100644
index 0a26ef49e35..00000000000
--- a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
+++ /dev/null
@@ -1,454 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-require 'rubocop'
-require 'rubocop/rspec/support'
-require_relative '../../../rubocop/cop/line_break_around_conditional_block'
-
-RSpec.describe RuboCop::Cop::LineBreakAroundConditionalBlock, type: :rubocop do
- include CopHelper
-
- subject(:cop) { described_class.new }
-
- shared_examples 'examples with conditional' do |conditional|
- it "flags violation for #{conditional} without line break before" do
- source = <<~RUBY
- do_something
- #{conditional} condition
- do_something_more
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses.size).to eq(1)
- offense = cop.offenses.first
-
- expect(offense.line).to eq(2)
- expect(cop.highlights).to eq(["#{conditional} condition\n do_something_more\nend"])
- expect(offense.message).to eq('Add a line break around conditional blocks')
- end
-
- it "flags violation for #{conditional} without line break after" do
- source = <<~RUBY
- #{conditional} condition
- do_something
- end
- do_something_more
- RUBY
- inspect_source(source)
-
- expect(cop.offenses.size).to eq(1)
- offense = cop.offenses.first
-
- expect(offense.line).to eq(1)
- expect(cop.highlights).to eq(["#{conditional} condition\n do_something\nend"])
- expect(offense.message).to eq('Add a line break around conditional blocks')
- end
-
- it "doesn't flag violation for #{conditional} with line break before and after" do
- source = <<~RUBY
- #{conditional} condition
- do_something
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a method definition" do
- source = <<~RUBY
- def a_method
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a class definition" do
- source = <<~RUBY
- class Foo
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a module definition" do
- source = <<~RUBY
- module Foo
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a begin definition" do
- source = <<~RUBY
- begin
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by an assign/begin definition" do
- source = <<~RUBY
- @project ||= begin
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a block definition" do
- source = <<~RUBY
- on_block(param_a) do |item|
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a block definition with a comment" do
- source = <<~RUBY
- on_block(param_a) do |item| # a short comment
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a block definition using brackets" do
- source = <<~RUBY
- on_block(param_a) { |item|
- #{conditional} condition
- do_something
- end
- }
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a comment" do
- source = <<~RUBY
- # a short comment
- #{conditional} condition
- do_something
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by an assignment" do
- source = <<~RUBY
- foo =
- #{conditional} condition
- do_something
- else
- do_something_more
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a multiline comment" do
- source = <<~RUBY
- =begin
- a multiline comment
- =end
- #{conditional} condition
- do_something
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by another conditional" do
- source = <<~RUBY
- #{conditional} condition_a
- #{conditional} condition_b
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by an else" do
- source = <<~RUBY
- if condition_a
- do_something
- else
- #{conditional} condition_b
- do_something_extra
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by an elsif" do
- source = <<~RUBY
- if condition_a
- do_something
- elsif condition_b
- #{conditional} condition_c
- do_something_extra
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by an ensure" do
- source = <<~RUBY
- def a_method
- ensure
- #{conditional} condition_c
- do_something_extra
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a when" do
- source = <<~RUBY
- case field
- when value
- #{conditional} condition_c
- do_something_extra
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} followed by a comment" do
- source = <<~RUBY
- #{conditional} condition
- do_something
- end
- # a short comment
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} followed by an end" do
- source = <<~RUBY
- class Foo
-
- #{conditional} condition
- do_something
- end
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} followed by an else" do
- source = <<~RUBY
- #{conditional} condition_a
- #{conditional} condition_b
- do_something
- end
- else
- do_something_extra
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} followed by a when" do
- source = <<~RUBY
- case
- when condition_a
- #{conditional} condition_b
- do_something
- end
- when condition_c
- do_something_extra
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} followed by an elsif" do
- source = <<~RUBY
- if condition_a
- #{conditional} condition_b
- do_something
- end
- elsif condition_c
- do_something_extra
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} preceded by a rescue" do
- source = <<~RUBY
- def a_method
- do_something
- rescue
- #{conditional} condition
- do_something
- end
- end
- RUBY
-
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "doesn't flag violation for #{conditional} followed by a rescue" do
- source = <<~RUBY
- def a_method
- #{conditional} condition
- do_something
- end
- rescue
- do_something_extra
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-
- it "autocorrects #{conditional} without line break before" do
- source = <<~RUBY
- do_something
- #{conditional} condition
- do_something_more
- end
- RUBY
- autocorrected = autocorrect_source(source)
-
- expected_source = <<~RUBY
- do_something
-
- #{conditional} condition
- do_something_more
- end
- RUBY
- expect(autocorrected).to eql(expected_source)
- end
-
- it "autocorrects #{conditional} without line break after" do
- source = <<~RUBY
- #{conditional} condition
- do_something
- end
- do_something_more
- RUBY
- autocorrected = autocorrect_source(source)
-
- expected_source = <<~RUBY
- #{conditional} condition
- do_something
- end
-
- do_something_more
- RUBY
- expect(autocorrected).to eql(expected_source)
- end
-
- it "autocorrects #{conditional} without line break before and after" do
- source = <<~RUBY
- do_something
- #{conditional} condition
- do_something_more
- end
- do_something_extra
- RUBY
- autocorrected = autocorrect_source(source)
-
- expected_source = <<~RUBY
- do_something
-
- #{conditional} condition
- do_something_more
- end
-
- do_something_extra
- RUBY
- expect(autocorrected).to eql(expected_source)
- end
- end
-
- %w[if unless].each do |example|
- it_behaves_like 'examples with conditional', example
- end
-
- it "doesn't flag violation for if with elsif" do
- source = <<~RUBY
- if condition
- do_something
- elsif another_condition
- do_something_more
- end
- RUBY
- inspect_source(source)
-
- expect(cop.offenses).to be_empty
- end
-end
diff --git a/spec/services/admin/propagate_integration_service_spec.rb b/spec/services/admin/propagate_integration_service_spec.rb
index 5df6e5e50ff..13320528e4f 100644
--- a/spec/services/admin/propagate_integration_service_spec.rb
+++ b/spec/services/admin/propagate_integration_service_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe Admin::PropagateIntegrationService do
let_it_be(:inherited_integration) do
create(:jira_service, project: create(:project), inherit_from_id: instance_integration.id)
end
+
let_it_be(:different_type_inherited_integration) do
create(:redmine_service, project: project, inherit_from_id: instance_integration.id)
end
diff --git a/spec/services/alert_management/create_alert_issue_service_spec.rb b/spec/services/alert_management/create_alert_issue_service_spec.rb
index f2be317a13d..2834322be7b 100644
--- a/spec/services/alert_management/create_alert_issue_service_spec.rb
+++ b/spec/services/alert_management/create_alert_issue_service_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
'generatorURL' => 'http://8d467bd4607a:9090/graph?g0.expr=vector%281%29&g0.tab=1'
}
end
+
let_it_be(:generic_alert, reload: true) { create(:alert_management_alert, :triggered, project: project, payload: payload) }
let_it_be(:prometheus_alert, reload: true) { create(:alert_management_alert, :triggered, :prometheus, project: project, payload: payload) }
let(:alert) { generic_alert }
diff --git a/spec/services/metrics/dashboard/panel_preview_service_spec.rb b/spec/services/metrics/dashboard/panel_preview_service_spec.rb
index d58dee3e7a3..2877d22d1f3 100644
--- a/spec/services/metrics/dashboard/panel_preview_service_spec.rb
+++ b/spec/services/metrics/dashboard/panel_preview_service_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe Metrics::Dashboard::PanelPreviewService do
title: test panel
YML
end
+
let_it_be(:dashboard) do
{
panel_groups: [
diff --git a/spec/services/notes/destroy_service_spec.rb b/spec/services/notes/destroy_service_spec.rb
index f0e5b29ac9b..0859c28cbe7 100644
--- a/spec/services/notes/destroy_service_spec.rb
+++ b/spec/services/notes/destroy_service_spec.rb
@@ -41,6 +41,7 @@ RSpec.describe Notes::DestroyService do
create(:merge_request, source_project: repo_project,
target_project: repo_project)
end
+
let_it_be(:note) do
create(:diff_note_on_merge_request, project: repo_project,
noteable: merge_request)
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 546b8fa1443..f9a89c6281e 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -848,6 +848,7 @@ RSpec.describe NotificationService, :mailer do
noteable: design,
note: "Hello #{member_and_mentioned.to_reference}, G'day #{non_member_and_mentioned.to_reference}")
end
+
let_it_be(:note_2) do
create(:diff_note_on_design, noteable: design, author: member_and_author_of_second_note)
end
diff --git a/spec/support/shared_contexts/design_management_shared_contexts.rb b/spec/support/shared_contexts/design_management_shared_contexts.rb
index 3ff6a521338..e6ae7e03664 100644
--- a/spec/support/shared_contexts/design_management_shared_contexts.rb
+++ b/spec/support/shared_contexts/design_management_shared_contexts.rb
@@ -18,12 +18,14 @@ RSpec.shared_context 'four designs in three versions' do
modified_designs: [],
deleted_designs: [])
end
+
let_it_be(:second_version) do
create(:design_version, issue: issue,
created_designs: [design_b, design_c, design_d],
modified_designs: [design_a],
deleted_designs: [])
end
+
let_it_be(:third_version) do
create(:design_version, issue: issue,
created_designs: [],
diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
index 010c445d8df..88c31bf9cfd 100644
--- a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
@@ -23,6 +23,7 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
# We cannot use `let_it_be` here otherwise we get:
# Failure/Error: allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
# The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported.
+
let!(:project2) do
allow_gitaly_n_plus_1 do
fork_project(project1, user)
@@ -40,9 +41,11 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
let_it_be(:project4, reload: true) do
allow_gitaly_n_plus_1 { create(:project, :repository, group: subgroup) }
end
+
let_it_be(:project5, reload: true) do
allow_gitaly_n_plus_1 { create(:project, group: subgroup) }
end
+
let_it_be(:project6, reload: true) do
allow_gitaly_n_plus_1 { create(:project, group: subgroup) }
end
diff --git a/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb b/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb
index edc5b313220..de40b926a1c 100644
--- a/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb
+++ b/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb
@@ -116,6 +116,7 @@ RSpec.shared_context 'Jira projects request context' do
"uuid": "14935009-f8aa-481e-94bc-f7251f320b0e"
}]'
end
+
let_it_be(:empty_jira_projects_json) do
'{
"self": "https://your-domain.atlassian.net/rest/api/2/project/search?startAt=0&maxResults=2",
diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb
index 72845b47a53..4e661e458ad 100644
--- a/spec/uploaders/gitlab_uploader_spec.rb
+++ b/spec/uploaders/gitlab_uploader_spec.rb
@@ -141,5 +141,23 @@ RSpec.describe GitlabUploader do
end
end
end
+
+ describe '#url_or_file_path' do
+ let(:options) { { expire_at: 1.day.from_now } }
+
+ it 'returns url when in remote storage' do
+ expect(subject).to receive(:file_storage?).and_return(false)
+ expect(subject).to receive(:url).with(options).and_return("http://example.com")
+
+ expect(subject.url_or_file_path(options)).to eq("http://example.com")
+ end
+
+ it 'returns url when in remote storage' do
+ expect(subject).to receive(:file_storage?).and_return(true)
+ expect(subject).to receive(:path).and_return("/tmp/file")
+
+ expect(subject.url_or_file_path(options)).to eq("file:///tmp/file")
+ end
+ end
end
end
diff --git a/tooling/overcommit/Gemfile b/tooling/overcommit/Gemfile
index 571ea1862a5..08f08018ffb 100644
--- a/tooling/overcommit/Gemfile
+++ b/tooling/overcommit/Gemfile
@@ -4,6 +4,6 @@
source 'https://rubygems.org'
gem 'overcommit'
-gem 'gitlab-styles', '~> 5.0.0', require: false
+gem 'gitlab-styles', '~> 5.1.0', require: false
gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.34.0', require: false
diff --git a/tooling/overcommit/Gemfile.lock b/tooling/overcommit/Gemfile.lock
index 6527ad3fcda..5bada88e1dc 100644
--- a/tooling/overcommit/Gemfile.lock
+++ b/tooling/overcommit/Gemfile.lock
@@ -11,7 +11,7 @@ GEM
childprocess (3.0.0)
concurrent-ruby (1.1.7)
ffi (1.12.2)
- gitlab-styles (5.0.0)
+ gitlab-styles (5.1.0)
rubocop (~> 0.89.1)
rubocop-gitlab-security (~> 0.1.0)
rubocop-performance (~> 1.8.1)
@@ -79,7 +79,7 @@ GEM
temple (0.8.2)
thread_safe (0.3.6)
tilt (2.0.10)
- tzinfo (1.2.7)
+ tzinfo (1.2.8)
thread_safe (~> 0.1)
unicode-display_width (1.7.0)
zeitwerk (2.4.1)
@@ -88,7 +88,7 @@ PLATFORMS
ruby
DEPENDENCIES
- gitlab-styles (~> 5.0.0)
+ gitlab-styles (~> 5.1.0)
haml_lint (~> 0.34.0)
overcommit
scss_lint (~> 0.56.0)