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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-01-24 12:08:32 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-01-24 12:08:32 +0300
commit9b984f55eef568b6a15c3a125e0cf66f35678e5a (patch)
treeee7e1eb42f27400dd74bb44bb595263af2d72fc1
parent83a9f472b8b523619519a1834176165c9f1532f7 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/monitoring/components/charts/anomaly.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/charts/time_series.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue23
-rw-r--r--app/assets/javascripts/monitoring/components/embed.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/panel_type.vue4
-rw-r--r--app/assets/javascripts/monitoring/stores/utils.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue (renamed from app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue)48
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_input.vue (renamed from app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue)4
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js (renamed from app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_lib.js)20
-rw-r--r--app/assets/stylesheets/components/date_time_picker.scss5
-rw-r--r--app/assets/stylesheets/pages/prometheus.scss26
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md2
-rw-r--r--doc/administration/geo/replication/version_specific_updates.md2
-rw-r--r--doc/administration/job_artifacts.md2
-rw-r--r--doc/administration/monitoring/prometheus/index.md2
-rw-r--r--doc/administration/packages/container_registry.md2
-rw-r--r--doc/api/audit_events.md2
-rw-r--r--doc/api/award_emoji.md2
-rw-r--r--doc/api/repositories.md2
-rw-r--r--doc/development/i18n/externalization.md2
-rw-r--r--doc/gitlab-basics/start-using-git.md2
-rw-r--r--doc/user/permissions.md3
-rw-r--r--doc/user/project/integrations/prometheus.md2
-rw-r--r--doc/user/project/issues/design_management.md2
-rw-r--r--doc/user/project/members/share_project_with_groups.md2
-rw-r--r--doc/user/project/pipelines/settings.md2
-rw-r--r--doc/user/project/settings/index.md2
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js2
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_window_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/date_time_picker/date_time_picker_input_spec.js (renamed from spec/frontend/monitoring/components/date_time_picker/date_time_picker_input_spec.js)2
-rw-r--r--spec/frontend/vue_shared/components/date_time_picker/date_time_picker_lib_spec.js (renamed from spec/frontend/monitoring/components/date_time_picker/date_time_picker_lib_spec.js)2
-rw-r--r--spec/frontend/vue_shared/components/date_time_picker/date_time_picker_spec.js (renamed from spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js)4
-rw-r--r--spec/models/repository_spec.rb12
-rw-r--r--spec/services/notification_service_spec.rb146
-rw-r--r--spec/support/helpers/email_helpers.rb5
-rw-r--r--spec/support/helpers/notification_helpers.rb24
-rw-r--r--spec/support/shared_examples/services/notification_service_shared_examples.rb18
37 files changed, 227 insertions, 167 deletions
diff --git a/app/assets/javascripts/monitoring/components/charts/anomaly.vue b/app/assets/javascripts/monitoring/components/charts/anomaly.vue
index 64704701d1a..bcbc1dad89d 100644
--- a/app/assets/javascripts/monitoring/components/charts/anomaly.vue
+++ b/app/assets/javascripts/monitoring/components/charts/anomaly.vue
@@ -1,5 +1,5 @@
<script>
-import { flatten, isNumber } from 'underscore';
+import { flattenDeep, isNumber } from 'lodash';
import { GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import { roundOffFloat } from '~/lib/utils/common_utils';
import { hexToRgb } from '~/lib/utils/color_utils';
@@ -77,7 +77,7 @@ export default {
* This offset is the lowest value.
*/
yOffset() {
- const values = flatten(this.series.map(ser => ser.data.map(([, y]) => y)));
+ const values = flattenDeep(this.series.map(ser => ser.data.map(([, y]) => y)));
const min = values.length ? Math.floor(Math.min(...values)) : 0;
return min < 0 ? -min : 0;
},
diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue
index 0d442f14aea..e77bf08be7f 100644
--- a/app/assets/javascripts/monitoring/components/charts/time_series.vue
+++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue
@@ -1,5 +1,5 @@
<script>
-import _ from 'underscore';
+import { omit } from 'lodash';
import { GlLink, GlButton, GlTooltip, GlResizeObserverDirective } from '@gitlab/ui';
import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import dateFormat from 'dateformat';
@@ -140,7 +140,7 @@ export default {
return (this.option.series || []).concat(this.scatterSeries ? [this.scatterSeries] : []);
},
chartOptions() {
- const option = _.omit(this.option, 'series');
+ const option = omit(this.option, 'series');
return {
series: this.chartOptionSeries,
xAxis: {
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 211990f3d7c..14f39c50a2d 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,5 +1,5 @@
<script>
-import _ from 'underscore';
+import { debounce, pickBy } from 'lodash';
import { mapActions, mapState, mapGetters } from 'vuex';
import VueDraggable from 'vuedraggable';
import {
@@ -15,12 +15,13 @@ import {
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { s__ } from '~/locale';
import createFlash from '~/flash';
-import Icon from '~/vue_shared/components/icon.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getParameterValues, mergeUrlParams, redirectTo } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url';
+import Icon from '~/vue_shared/components/icon.vue';
+import { getTimeRange } from '~/vue_shared/components/date_time_picker/date_time_picker_lib';
+import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
-import DateTimePicker from './date_time_picker/date_time_picker.vue';
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
import GroupEmptyState from './group_empty_state.vue';
@@ -28,11 +29,10 @@ import DashboardsDropdown from './dashboards_dropdown.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getAddMetricTrackingOptions } from '../utils';
-import { getTimeRange } from './date_time_picker/date_time_picker_lib';
import { datePickerTimeWindows, metricStates } from '../constants';
-const defaultTimeDiff = getTimeRange();
+const defaultTimeRange = getTimeRange();
export default {
components: {
@@ -190,8 +190,8 @@ export default {
return {
state: 'gettingStarted',
formIsValid: null,
- startDate: getParameterValues('start')[0] || defaultTimeDiff.start,
- endDate: getParameterValues('end')[0] || defaultTimeDiff.end,
+ startDate: getParameterValues('start')[0] || defaultTimeRange.start,
+ endDate: getParameterValues('end')[0] || defaultTimeRange.end,
hasValidDates: true,
datePickerTimeWindows,
isRearrangingPanels: false,
@@ -288,13 +288,13 @@ export default {
'Metrics|Link contains an invalid time window, please verify the link to see the requested time range.',
),
);
- this.startDate = defaultTimeDiff.start;
- this.endDate = defaultTimeDiff.end;
+ this.startDate = defaultTimeRange.start;
+ this.endDate = defaultTimeRange.end;
},
generateLink(group, title, yLabel) {
const dashboard = this.currentDashboard || this.firstDashboard.path;
- const params = _.pick({ dashboard, group, title, y_label: yLabel }, value => value != null);
+ const params = pickBy({ dashboard, group, title, y_label: yLabel }, value => value != null);
return mergeUrlParams(params, window.location.href);
},
hideAddMetricModal() {
@@ -306,7 +306,7 @@ export default {
setFormValidity(isValid) {
this.formIsValid = isValid;
},
- debouncedEnvironmentsSearch: _.debounce(function environmentsSearchOnInput(searchTerm) {
+ debouncedEnvironmentsSearch: debounce(function environmentsSearchOnInput(searchTerm) {
this.setEnvironmentsSearchTerm(searchTerm);
}, 500),
submitCustomMetricsForm() {
@@ -427,6 +427,7 @@ export default {
class="col-sm-6 col-md-6 col-lg-4"
>
<date-time-picker
+ ref="dateTimePicker"
:start="startDate"
:end="endDate"
:time-windows="datePickerTimeWindows"
diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue
index 10c081070d7..c79e43c7c29 100644
--- a/app/assets/javascripts/monitoring/components/embed.vue
+++ b/app/assets/javascripts/monitoring/components/embed.vue
@@ -3,7 +3,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
import { sidebarAnimationDuration } from '../constants';
-import { getTimeRange } from './date_time_picker/date_time_picker_lib';
+import { getTimeRange } from '~/vue_shared/components/date_time_picker/date_time_picker_lib';
let sidebarMutationObserver;
diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue
index ec6a41d0540..a2849dcfddb 100644
--- a/app/assets/javascripts/monitoring/components/panel_type.vue
+++ b/app/assets/javascripts/monitoring/components/panel_type.vue
@@ -1,6 +1,6 @@
<script>
import { mapState } from 'vuex';
-import _ from 'underscore';
+import { pickBy } from 'lodash';
import {
GlDropdown,
GlDropdownItem,
@@ -90,7 +90,7 @@ export default {
getGraphAlerts(queries) {
if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId);
- return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId));
+ return pickBy(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId));
},
getGraphAlertValues(queries) {
return Object.values(this.getGraphAlerts(queries));
diff --git a/app/assets/javascripts/monitoring/stores/utils.js b/app/assets/javascripts/monitoring/stores/utils.js
index 3300d2032d0..616d2e9bfd8 100644
--- a/app/assets/javascripts/monitoring/stores/utils.js
+++ b/app/assets/javascripts/monitoring/stores/utils.js
@@ -1,4 +1,4 @@
-import _ from 'underscore';
+import { omit } from 'lodash';
export const uniqMetricsId = metric => `${metric.metric_id}_${metric.id}`;
@@ -11,7 +11,7 @@ export const uniqMetricsId = metric => `${metric.metric_id}_${metric.id}`;
*/
export const normalizeMetric = (metric = {}) =>
- _.omit(
+ omit(
{
...metric,
metric_id: uniqMetricsId(metric),
diff --git a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
index 3a18a494cad..7d4c162473f 100644
--- a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
@@ -122,30 +122,28 @@ export default {
};
</script>
<template>
- <gl-dropdown
- ref="dropdown"
- :text="timeWindowText"
- menu-class="time-window-dropdown-menu"
- class="js-time-window-dropdown"
- >
- <div class="d-flex justify-content-between time-window-dropdown-menu-container">
+ <gl-dropdown :text="timeWindowText" class="date-time-picker" menu-class="date-time-picker-menu">
+ <div class="d-flex justify-content-between gl-p-2">
<gl-form-group
:label="__('Custom range')"
label-for="custom-from-time"
- class="custom-time-range-form-group col-md-7 p-0 m-0"
+ label-class="gl-pb-1"
+ class="custom-time-range-form-group col-md-7 gl-pl-1 gl-pr-0 m-0"
>
- <date-time-picker-input
- id="custom-time-from"
- v-model="startInput"
- :label="__('From')"
- :state="startInputValid"
- />
- <date-time-picker-input
- id="custom-time-to"
- v-model="endInput"
- :label="__('To')"
- :state="endInputValid"
- />
+ <div class="gl-pt-2">
+ <date-time-picker-input
+ id="custom-time-from"
+ v-model="startInput"
+ :label="__('From')"
+ :state="startInputValid"
+ />
+ <date-time-picker-input
+ id="custom-time-to"
+ v-model="endInput"
+ :label="__('To')"
+ :state="endInputValid"
+ />
+ </div>
<gl-form-group>
<gl-button @click="closeDropdown">{{ __('Cancel') }}</gl-button>
<gl-button variant="success" :disabled="!isValid" @click="apply()">
@@ -153,12 +151,10 @@ export default {
</gl-button>
</gl-form-group>
</gl-form-group>
- <gl-form-group
- :label="__('Quick range')"
- label-for="group-id-dropdown"
- label-align="center"
- class="col-md-4 p-0 m-0"
- >
+ <gl-form-group label-for="group-id-dropdown" class="col-md-5 gl-pl-1 gl-pr-1 m-0">
+ <template #label>
+ <span class="gl-pl-5">{{ __('Quick range') }}</span>
+ </template>
<gl-dropdown-item
v-for="(timeWindow, key) in timeWindows"
:key="key"
diff --git a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_input.vue
index b27a379c46a..f19f8bd46b3 100644
--- a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_input.vue
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_input.vue
@@ -1,5 +1,5 @@
<script>
-import _ from 'underscore';
+import { uniqueId } from 'lodash';
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { dateFormats } from './date_time_picker_lib';
@@ -35,7 +35,7 @@ export default {
id: {
type: String,
required: false,
- default: () => _.uniqueId('dateTimePicker_'),
+ default: () => uniqueId('dateTimePicker_'),
},
},
data() {
diff --git a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_lib.js b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js
index 604b17baab9..685115b92dd 100644
--- a/app/assets/javascripts/monitoring/components/date_time_picker/date_time_picker_lib.js
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js
@@ -8,6 +8,15 @@ import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
*/
const dateTimePickerRegex = /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])(?: (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/;
+/**
+ * A key-value pair of "time windows".
+ *
+ * A time window is a representation of period of time that starts
+ * some time in past until now. Keys are only used for easy reference.
+ *
+ * It is represented as user friendly `label` and number of `seconds`
+ * to be substracted from now.
+ */
export const defaultTimeWindows = {
thirtyMinutes: {
label: __('30 minutes'),
@@ -58,6 +67,17 @@ export const isValidDate = dateString => {
}
};
+/**
+ * For a given time window key (e.g. `threeHours`) and key-value pair
+ * object of time windows.
+ *
+ * Returns a date time range with start and end.
+ *
+ * @param {String} timeWindowKey - A key in the object of time windows.
+ * @param {Object} timeWindows - A key-value pair of time windows,
+ * with a second duration and a label.
+ * @returns An object with time range, start and end dates, in ISO format.
+ */
export const getTimeRange = (timeWindowKey, timeWindows = defaultTimeWindows) => {
let difference;
if (timeWindows[timeWindowKey]) {
diff --git a/app/assets/stylesheets/components/date_time_picker.scss b/app/assets/stylesheets/components/date_time_picker.scss
new file mode 100644
index 00000000000..21f085cdaf1
--- /dev/null
+++ b/app/assets/stylesheets/components/date_time_picker.scss
@@ -0,0 +1,5 @@
+.date-time-picker {
+ .date-time-picker-menu {
+ width: 400px;
+ }
+}
diff --git a/app/assets/stylesheets/pages/prometheus.scss b/app/assets/stylesheets/pages/prometheus.scss
index e759423f6e5..3e6313173b8 100644
--- a/app/assets/stylesheets/pages/prometheus.scss
+++ b/app/assets/stylesheets/pages/prometheus.scss
@@ -46,32 +46,6 @@
}
}
-.prometheus-graphs-header {
- .time-window-dropdown-menu {
- padding: $gl-padding $gl-padding 0 $gl-padding-12;
- }
-
- .time-window-dropdown-menu-container {
- width: 360px;
- }
-
- .custom-time-range-form-group > label {
- padding-bottom: $gl-padding;
- }
-
- .monitor-environment-dropdown-menu {
- &.show {
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
-
- .no-matches-message {
- padding: $gl-padding-8 $gl-padding-12;
- }
- }
-}
-
.prometheus-panel {
margin-top: 20px;
}
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
index dd0e4658ee9..a5a102d888b 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
@@ -105,7 +105,7 @@ The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb`
The two Active Directory specific values are `active_directory: true` and `uid: 'sAMAccountName'`. `sAMAccountName` is an attribute returned by Active Directory used for GitLab usernames. See the example output from `ldapsearch` for a full list of attributes a "person" object (user) has in **AD** - [`ldapsearch` example](#using-ldapsearch-unix)
-> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](../how_to_configure_ldap_gitlab_ee/index.html#gitlab-enterprise-edition---ldap-features) **(STARTER ONLY)**
+> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](../how_to_configure_ldap_gitlab_ee/index.md#gitlab-enterprise-edition---ldap-features) **(STARTER ONLY)**
### Example `gitlab.rb` LDAP
diff --git a/doc/administration/geo/replication/version_specific_updates.md b/doc/administration/geo/replication/version_specific_updates.md
index 7ce38d80c88..772defe0191 100644
--- a/doc/administration/geo/replication/version_specific_updates.md
+++ b/doc/administration/geo/replication/version_specific_updates.md
@@ -134,7 +134,7 @@ sudo gitlab-ctl reconfigure
```
If you do not perform this step, you may find that two-factor authentication
-[is broken following DR](../disaster_recovery/index.html#i-followed-the-disaster-recovery-instructions-and-now-two-factor-auth-is-broken).
+[is broken following DR](../disaster_recovery/index.md#i-followed-the-disaster-recovery-instructions-and-now-two-factor-auth-is-broken).
To prevent SSH requests to the newly promoted **primary** node from failing
due to SSH host key mismatch when updating the **primary** node domain's DNS record
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 99f4033e8fd..04974c6ea8b 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -369,7 +369,7 @@ If you need to manually remove job artifacts associated with multiple jobs while
NOTE: **NOTE:**
This step will also erase artifacts that users have chosen to
- ["keep"](../user/project/pipelines/job_artifacts.html#browsing-artifacts).
+ ["keep"](../user/project/pipelines/job_artifacts.md#browsing-artifacts).
```ruby
builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.week.ago)
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 62bacf9791e..14f2d254a7c 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -133,7 +133,7 @@ To use an external Prometheus server:
```
1. Install and set up a dedicated Prometheus instance, if necessary, using the [official installation instructions](https://prometheus.io/docs/prometheus/latest/installation/).
-1. Add the Prometheus server IP address to the [monitoring IP whitelist](../ip_whitelist.html). For example:
+1. Add the Prometheus server IP address to the [monitoring IP whitelist](../ip_whitelist.md). For example:
```ruby
gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '192.168.0.1']
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index d3415913bab..3804319f60d 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -744,7 +744,7 @@ project or branch name. Special characters can include:
To get around this, you can [change the group path](../../user/group/index.md#changing-a-groups-path),
[change the project path](../../user/project/settings/index.md#renaming-a-repository) or change the
-branch name. Another option is to create a [push rule](../../push_rules/push_rules.html) to prevent
+branch name. Another option is to create a [push rule](../../push_rules/push_rules.md) to prevent
this at the instance level.
### Image push errors
diff --git a/doc/api/audit_events.md b/doc/api/audit_events.md
index e451b975d42..a06a300e149 100644
--- a/doc/api/audit_events.md
+++ b/doc/api/audit_events.md
@@ -120,7 +120,7 @@ Example response:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34078) in GitLab 12.5.
-The Group Audit Events API allows you to retrieve [group audit events](../administration/audit_events.html#group-events-starter).
+The Group Audit Events API allows you to retrieve [group audit events](../administration/audit_events.md#group-events-starter).
To retrieve group audit events using the API, you must [authenticate yourself](README.html#authentication) as an Administrator or an owner of the group.
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index 22ddab4bf79..41dbedf1ee2 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -10,7 +10,7 @@ Emoji can be awarded on the following (known as "awardables"):
- [Merge requests](../user/project/merge_requests/index.md) ([API](merge_requests.md)).
- [Snippets](../user/snippets.md) ([API](snippets.md)).
-Emoji can also [be awarded](../user/award_emojis.html#award-emoji-for-comments) on comments (also known as notes). See also [Notes API](notes.md).
+Emoji can also [be awarded](../user/award_emojis.md#award-emoji-for-comments) on comments (also known as notes). See also [Notes API](notes.md).
## Issues, merge requests, and snippets
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 4bc28dd342f..faa30043423 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -128,7 +128,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.com/api/v4/pro
## Compare branches, tags or commits
This endpoint can be accessed without authentication if the repository is
-publicly accessible. Note that diffs could have an empty diff string if [diff limits](../development/diffs.html#diff-limits) are reached.
+publicly accessible. Note that diffs could have an empty diff string if [diff limits](../development/diffs.md#diff-limits) are reached.
```
GET /projects/:id/repository/compare
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 488d7ae6762..86ac70ecef6 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -341,7 +341,7 @@ This also applies when using links in between translated sentences, otherwise th
```js
{{
sprintf(s__("ClusterIntegration|Learn more about %{linkStart}zones%{linkEnd}"), {
- linkStart: '<a href="https://cloud.google.com/compute/docs/regions-zones/regions-zones" target="_blank" rel="noopener noreferrer">'
+ linkStart: '<a href="https://cloud.google.com/compute/docs/regions-zones/regions-zones" target="_blank" rel="noopener noreferrer">',
linkEnd: '</a>',
})
}}
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index 7fa84bf45bd..38376f4334f 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -93,7 +93,7 @@ This creates a `.git` directory that contains the Git configuration files.
Once the directory has been initialized, you can [add a remote repository](#add-a-remote-repository)
and [send changes to GitLab.com](#send-changes-to-gitlabcom). You will also need to
-[create a new project in GitLab](../gitlab-basics/create-project.html#push-to-create-a-new-project)
+[create a new project in GitLab](../gitlab-basics/create-project.md#push-to-create-a-new-project)
for your Git repository.
### Clone a repository
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 9a32f0adfaa..599d65805fc 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -119,7 +119,7 @@ The following table depicts the various user permission levels in a project.
| Configure project hooks | | | | ✓ | ✓ |
| Manage Runners | | | | ✓ | ✓ |
| Manage job triggers | | | | ✓ | ✓ |
-| Manage variables | | | | ✓ | ✓ |
+| Manage CI/CD variables | | | | ✓ | ✓ |
| Manage GitLab Pages | | | | ✓ | ✓ |
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
| Remove GitLab Pages | | | | ✓ | ✓ |
@@ -223,6 +223,7 @@ group.
| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Create subgroup | | | | ✓ (1) | ✓ |
| Edit group | | | | | ✓ |
+| Manage group level CI/CD variables | | | | | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
| Delete group epic **(ULTIMATE)** | | | | | ✓ |
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 6c6bb4034cf..d8938d81637 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -134,7 +134,7 @@ Multiple metrics can be displayed on the same chart if the fields **Name**, **Ty
#### Query Variables
-GitLab supports a limited set of [CI variables](../../../ci/variables/README.html) in the Prometheus query. This is particularly useful for identifying a specific environment, for example with `CI_ENVIRONMENT_SLUG`. The supported variables are:
+GitLab supports a limited set of [CI variables](../../../ci/variables/README.md) in the Prometheus query. This is particularly useful for identifying a specific environment, for example with `CI_ENVIRONMENT_SLUG`. The supported variables are:
- CI_ENVIRONMENT_SLUG
- KUBE_NAMESPACE
diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md
index fd4a01043ef..b8983a06180 100644
--- a/doc/user/project/issues/design_management.md
+++ b/doc/user/project/issues/design_management.md
@@ -34,7 +34,7 @@ to be enabled:
and enable **Git Large File Storage**.
Design Management requires that projects are using
-[hashed storage](../../../administration/repository_storage_types.html#hashed-storage)
+[hashed storage](../../../administration/repository_storage_types.md#hashed-storage)
(the default storage type since v10.0).
### Feature Flags
diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md
index 79fb2ea50a0..214937d4db2 100644
--- a/doc/user/project/members/share_project_with_groups.md
+++ b/doc/user/project/members/share_project_with_groups.md
@@ -54,4 +54,4 @@ It is possible to prevent projects in a group from [sharing
a project with another group](../members/share_project_with_groups.md).
This allows for tighter control over project access.
-Learn more about [Share with group lock](../../group/index.html#share-with-group-lock).
+Learn more about [Share with group lock](../../group/index.md#share-with-group-lock).
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index ca888c69b37..3998dc69ad5 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -58,7 +58,7 @@ if the job surpasses the threshold, it is marked as failed.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/17221) in GitLab 10.7.
Project defined timeout (either specific timeout set by user or the default
-60 minutes timeout) may be [overridden on Runner level](../../../ci/runners/README.html#setting-maximum-job-timeout-for-a-runner).
+60 minutes timeout) may be [overridden on Runner level](../../../ci/runners/README.md#setting-maximum-job-timeout-for-a-runner).
## Maximum artifacts size **(CORE ONLY)**
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 4e55f55dd28..bc1053f5bf2 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -86,7 +86,7 @@ related to the project by selecting the **Disable email notifications** checkbox
Set up your project's merge request settings:
-- Set up the merge request method (merge commit, [fast-forward merge](../merge_requests/fast_forward_merge.html)).
+- Set up the merge request method (merge commit, [fast-forward merge](../merge_requests/fast_forward_merge.md)).
- Add merge request [description templates](../description_templates.md#description-templates).
- Enable [merge request approvals](../merge_requests/merge_request_approvals.md). **(STARTER)**
- Enable [merge only if pipeline succeeds](../merge_requests/merge_when_pipeline_succeeds.md).
diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
index 195abae3783..c2cd6deb502 100644
--- a/spec/frontend/monitoring/components/dashboard_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -7,8 +7,8 @@ import statusCodes from '~/lib/utils/http_status';
import { metricStates } from '~/monitoring/constants';
import Dashboard from '~/monitoring/components/dashboard.vue';
+import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
-import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue';
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import { createStore } from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types';
diff --git a/spec/frontend/monitoring/components/dashboard_time_window_spec.js b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
index 29cca695093..e9f2a67983a 100644
--- a/spec/frontend/monitoring/components/dashboard_time_window_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
@@ -53,7 +53,7 @@ describe('dashboard time window', () => {
.$nextTick()
.then(() => {
const timeWindowDropdownItems = wrapper
- .find('.js-time-window-dropdown')
+ .find({ ref: 'dateTimePicker' })
.findAll(GlDropdownItem);
const activeItem = timeWindowDropdownItems.wrappers.filter(itemWrapper =>
diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_input_spec.js b/spec/frontend/vue_shared/components/date_time_picker/date_time_picker_input_spec.js
index 9cac63ad725..2c5bb86d8a5 100644
--- a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_input_spec.js
+++ b/spec/frontend/vue_shared/components/date_time_picker/date_time_picker_input_spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import DateTimePickerInput from '~/monitoring/components/date_time_picker/date_time_picker_input.vue';
+import DateTimePickerInput from '~/vue_shared/components/date_time_picker/date_time_picker_input.vue';
const inputLabel = 'This is a label';
const inputValue = 'something';
diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_lib_spec.js b/spec/frontend/vue_shared/components/date_time_picker/date_time_picker_lib_spec.js
index 9c0f66427ae..b7b024183e1 100644
--- a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_lib_spec.js
+++ b/spec/frontend/vue_shared/components/date_time_picker/date_time_picker_lib_spec.js
@@ -1,4 +1,4 @@
-import * as dateTimePickerLib from '~/monitoring/components/date_time_picker/date_time_picker_lib';
+import * as dateTimePickerLib from '~/vue_shared/components/date_time_picker/date_time_picker_lib';
describe('date time picker lib', () => {
describe('isValidDate', () => {
diff --git a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js b/spec/frontend/vue_shared/components/date_time_picker/date_time_picker_spec.js
index 3b37da5bcd6..98dfbe9cd14 100644
--- a/spec/frontend/monitoring/components/date_time_picker/date_time_picker_spec.js
+++ b/spec/frontend/vue_shared/components/date_time_picker/date_time_picker_spec.js
@@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils';
-import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue';
-import { defaultTimeWindows } from '~/monitoring/components/date_time_picker/date_time_picker_lib';
+import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
+import { defaultTimeWindows } from '~/vue_shared/components/date_time_picker/date_time_picker_lib';
const timeWindowsCount = Object.entries(defaultTimeWindows).length;
const start = '2019-10-10T07:00:00.000Z';
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index d08ca3983af..8b62e332407 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -482,6 +482,18 @@ describe Repository do
end
end
+ describe "#root_ref_sha" do
+ let(:commit) { double("commit", sha: "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3") }
+
+ subject { repository.root_ref_sha }
+
+ before do
+ allow(repository).to receive(:commit).with(repository.root_ref) { commit }
+ end
+
+ it { is_expected.to eq(commit.sha) }
+ end
+
describe '#can_be_merged?' do
context 'mergeable branches' do
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 80b8d36aa07..1f1ccff2ba8 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -10,10 +10,12 @@ describe NotificationService, :mailer do
let(:notification) { described_class.new }
let(:assignee) { create(:user) }
- around do |example|
- perform_enqueued_jobs do
- example.run
- end
+ around(:example, :deliver_mails_inline) do |example|
+ # This is a temporary `around` hook until all the examples check the
+ # background jobs queue instead of the delivered emails array.
+ # `perform_enqueued_jobs` makes the ActiveJob jobs (e.g. mailer jobs) run inline
+ # compared to `Sidekiq::Testing.inline!` which makes the Sidekiq jobs run inline.
+ perform_enqueued_jobs { example.run }
end
shared_examples 'altered milestone notification on issue' do
@@ -187,26 +189,41 @@ describe NotificationService, :mailer do
describe 'Keys' do
describe '#new_key' do
let(:key_options) { {} }
- let!(:key) { create(:personal_key, key_options) }
+ let!(:key) { build_stubbed(:personal_key, key_options) }
+
+ subject { notification.new_key(key) }
- it { expect(notification.new_key(key)).to be_truthy }
+ it "sends email to key owner" do
+ expect { subject }.to have_enqueued_email(key.id, mail: "new_ssh_key_email")
+ end
- describe 'never emails the ghost user' do
+ describe "never emails the ghost user" do
let(:key_options) { { user: User.ghost } }
- it { should_not_email_anyone }
+ it "does not send email to key owner" do
+ expect { subject }.not_to have_enqueued_email(key.id, mail: "new_ssh_key_email")
+ end
end
end
end
describe 'GpgKeys' do
describe '#new_gpg_key' do
- let!(:key) { create(:gpg_key) }
+ let(:key_options) { {} }
+ let(:key) { create(:gpg_key, key_options) }
+
+ subject { notification.new_gpg_key(key) }
+
+ it "sends email to key owner" do
+ expect { subject }.to have_enqueued_email(key.id, mail: "new_gpg_key_email")
+ end
- it { expect(notification.new_gpg_key(key)).to be_truthy }
+ describe "never emails the ghost user" do
+ let(:key_options) { { user: User.ghost } }
- it 'sends email to key owner' do
- expect { notification.new_gpg_key(key) }.to change { ActionMailer::Base.deliveries.size }.by(1)
+ it "does not send email to key owner" do
+ expect { subject }.not_to have_enqueued_email(key.id, mail: "new_gpg_key_email")
+ end
end
end
end
@@ -215,10 +232,10 @@ describe NotificationService, :mailer do
describe '#access_token_about_to_expire' do
let_it_be(:user) { create(:user) }
- it 'sends email to the token owner' do
- expect(notification.access_token_about_to_expire(user)).to be_truthy
+ subject { notification.access_token_about_to_expire(user) }
- should_email user
+ it 'sends email to the token owner' do
+ expect { subject }.to have_enqueued_email(user, mail: "access_token_about_to_expire_email")
end
end
end
@@ -231,6 +248,8 @@ describe NotificationService, :mailer do
let(:author) { create(:user) }
let(:note) { create(:note_on_issue, author: author, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @unsubscribed_mentioned and @outsider also') }
+ subject { notification.new_note(note) }
+
before do
build_team(project)
project.add_maintainer(issue.author)
@@ -260,32 +279,23 @@ describe NotificationService, :mailer do
reset_delivered_emails!
end
- it do
- expect(SentNotification).to receive(:record).with(issue, any_args).exactly(10).times
-
- notification.new_note(note)
-
- should_email(@u_watcher)
- should_email(note.noteable.author)
- should_email(note.noteable.assignees.first)
- should_email(@u_custom_global)
- should_email(@u_mentioned)
- should_email(@subscriber)
- should_email(@watcher_and_subscriber)
- should_email(@subscribed_participant)
- should_email(@u_custom_off)
- should_email(@unsubscribed_mentioned)
- should_not_email(@u_guest_custom)
- should_not_email(@u_guest_watcher)
- should_not_email(note.author)
- should_not_email(@u_participating)
- should_not_email(@u_disabled)
- should_not_email(@unsubscriber)
- should_not_email(@u_outsider_mentioned)
- should_not_email(@u_lazy_participant)
+ it 'sends emails to recipients' do
+ subject
+
+ expect_delivery_jobs_count(10)
+ expect_enqueud_email(@u_watcher.id, note.id, nil, mail: "note_issue_email")
+ expect_enqueud_email(note.noteable.author.id, note.id, nil, mail: "note_issue_email")
+ expect_enqueud_email(note.noteable.assignees.first.id, note.id, nil, mail: "note_issue_email")
+ expect_enqueud_email(@u_custom_global.id, note.id, nil, mail: "note_issue_email")
+ expect_enqueud_email(@u_mentioned.id, note.id, "mentioned", mail: "note_issue_email")
+ expect_enqueud_email(@subscriber.id, note.id, "subscribed", mail: "note_issue_email")
+ expect_enqueud_email(@watcher_and_subscriber.id, note.id, "subscribed", mail: "note_issue_email")
+ expect_enqueud_email(@subscribed_participant.id, note.id, "subscribed", mail: "note_issue_email")
+ expect_enqueud_email(@u_custom_off.id, note.id, nil, mail: "note_issue_email")
+ expect_enqueud_email(@unsubscribed_mentioned.id, note.id, "mentioned", mail: "note_issue_email")
end
- it "emails the note author if they've opted into notifications about their activity" do
+ it "emails the note author if they've opted into notifications about their activity", :deliver_mails_inline do
note.author.notified_of_own_activity = true
notification.new_note(note)
@@ -294,7 +304,7 @@ describe NotificationService, :mailer do
expect(find_email_for(note.author)).to have_header('X-GitLab-NotificationReason', 'own_activity')
end
- it_behaves_like 'project emails are disabled' do
+ it_behaves_like 'project emails are disabled', check_delivery_jobs_queue: true do
let(:notification_target) { note }
let(:notification_trigger) { notification.new_note(note) }
end
@@ -302,21 +312,21 @@ describe NotificationService, :mailer do
it 'filters out "mentioned in" notes' do
mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author)
+ reset_delivered_emails!
- expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note)
+
+ expect_no_delivery_jobs
end
context 'participating' do
context 'by note' do
before do
- reset_delivered_emails!
note.author = @u_lazy_participant
note.save
- notification.new_note(note)
end
- it { should_not_email(@u_lazy_participant) }
+ it { expect { subject }.not_to have_enqueued_email(@u_lazy_participant.id, note.id, mail: "note_issue_email") }
end
end
end
@@ -335,7 +345,7 @@ describe NotificationService, :mailer do
end
shared_examples 'new note notifications' do
- it do
+ it 'sends notifications', :deliver_mails_inline do
notification.new_note(note)
should_email(note.noteable.author)
@@ -359,7 +369,7 @@ describe NotificationService, :mailer do
it_behaves_like 'new note notifications'
- it_behaves_like 'project emails are disabled' do
+ it_behaves_like 'project emails are disabled', check_delivery_jobs_queue: true do
let(:notification_target) { note }
let(:notification_trigger) { notification.new_note(note) }
end
@@ -378,13 +388,13 @@ describe NotificationService, :mailer do
notification.new_note(note)
- should_email(user)
+ expect_enqueud_email(user.id, note.id, nil, mail: "note_issue_email")
end
end
end
end
- context 'confidential issue note' do
+ context 'confidential issue note', :deliver_mails_inline do
let(:project) { create(:project, :public) }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
@@ -441,7 +451,7 @@ describe NotificationService, :mailer do
end
end
- context 'issue note mention' do
+ context 'issue note mention', :deliver_mails_inline do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, assignees: [assignee]) }
let(:mentioned_issue) { create(:issue, assignees: issue.assignees) }
@@ -507,7 +517,7 @@ describe NotificationService, :mailer do
end
end
- context 'project snippet note' do
+ context 'project snippet note', :deliver_mails_inline do
let!(:project) { create(:project, :public) }
let(:snippet) { create(:project_snippet, project: project, author: create(:user)) }
let(:author) { create(:user) }
@@ -551,7 +561,7 @@ describe NotificationService, :mailer do
end
end
- context 'personal snippet note' do
+ context 'personal snippet note', :deliver_mails_inline do
let(:snippet) { create(:personal_snippet, :public, author: @u_snippet_author) }
let(:note) { create(:note_on_personal_snippet, noteable: snippet, note: '@mentioned note', author: @u_note_author) }
@@ -600,7 +610,7 @@ describe NotificationService, :mailer do
end
end
- context 'commit note' do
+ context 'commit note', :deliver_mails_inline do
let(:project) { create(:project, :public, :repository) }
let(:note) { create(:note_on_commit, project: project) }
@@ -659,7 +669,7 @@ describe NotificationService, :mailer do
end
end
- context "merge request diff note" do
+ context "merge request diff note", :deliver_mails_inline do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, source_project: project, assignees: [user], author: create(:user)) }
@@ -691,11 +701,11 @@ describe NotificationService, :mailer do
end
end
- describe '#send_new_release_notifications' do
+ describe '#send_new_release_notifications', :deliver_mails_inline, :sidekiq_inline do
context 'when recipients for a new release exist' do
let(:release) { create(:release) }
- it 'calls new_release_email for each relevant recipient', :sidekiq_might_not_need_inline do
+ it 'calls new_release_email for each relevant recipient' do
user_1 = create(:user)
user_2 = create(:user)
user_3 = create(:user)
@@ -712,7 +722,7 @@ describe NotificationService, :mailer do
end
end
- describe 'Participating project notification settings have priority over group and global settings if available' do
+ describe 'Participating project notification settings have priority over group and global settings if available', :deliver_mails_inline do
let!(:group) { create(:group) }
let!(:maintainer) { group.add_owner(create(:user, username: 'maintainer')).user }
let!(:user1) { group.add_developer(create(:user, username: 'user_with_project_and_custom_setting')).user }
@@ -770,7 +780,7 @@ describe NotificationService, :mailer do
end
end
- describe 'Issues' do
+ describe 'Issues', :deliver_mails_inline do
let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) }
let(:another_project) { create(:project, :public, namespace: group) }
@@ -1423,7 +1433,7 @@ describe NotificationService, :mailer do
end
end
- describe 'Merge Requests' do
+ describe 'Merge Requests', :deliver_mails_inline do
let(:group) { create(:group) }
let(:project) { create(:project, :public, :repository, namespace: group) }
let(:another_project) { create(:project, :public, namespace: group) }
@@ -1898,7 +1908,7 @@ describe NotificationService, :mailer do
end
end
- describe 'Projects' do
+ describe 'Projects', :deliver_mails_inline do
let(:project) { create(:project) }
before do
@@ -1989,7 +1999,7 @@ describe NotificationService, :mailer do
end
end
- describe 'GroupMember' do
+ describe 'GroupMember', :deliver_mails_inline do
let(:added_user) { create(:user) }
describe '#new_access_request' do
@@ -2075,7 +2085,7 @@ describe NotificationService, :mailer do
end
end
- describe 'ProjectMember' do
+ describe 'ProjectMember', :deliver_mails_inline do
let(:project) { create(:project) }
let(:added_user) { create(:user) }
@@ -2236,7 +2246,7 @@ describe NotificationService, :mailer do
end
end
- context 'guest user in private project' do
+ context 'guest user in private project', :deliver_mails_inline do
let(:private_project) { create(:project, :private) }
let(:guest) { create(:user) }
let(:developer) { create(:user) }
@@ -2291,7 +2301,7 @@ describe NotificationService, :mailer do
end
end
- describe 'Pipelines' do
+ describe 'Pipelines', :deliver_mails_inline do
describe '#pipeline_finished' do
let(:project) { create(:project, :public, :repository) }
let(:u_member) { create(:user) }
@@ -2507,7 +2517,7 @@ describe NotificationService, :mailer do
end
end
- describe 'Pages domains' do
+ describe 'Pages domains', :deliver_mails_inline do
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:domain, reload: true) { create(:pages_domain, project: project) }
let_it_be(:u_blocked) { create(:user, :blocked) }
@@ -2560,7 +2570,7 @@ describe NotificationService, :mailer do
end
end
- context 'Auto DevOps notifications' do
+ context 'Auto DevOps notifications', :deliver_mails_inline do
describe '#autodevops_disabled' do
let(:owner) { create(:user) }
let(:namespace) { create(:namespace, owner: owner) }
@@ -2584,7 +2594,7 @@ describe NotificationService, :mailer do
end
end
- describe 'Repository cleanup' do
+ describe 'Repository cleanup', :deliver_mails_inline do
let(:user) { create(:user) }
let(:project) { create(:project) }
@@ -2615,7 +2625,7 @@ describe NotificationService, :mailer do
end
end
- context 'Remote mirror notifications' do
+ context 'Remote mirror notifications', :deliver_mails_inline do
describe '#remote_mirror_update_failed' do
let(:project) { create(:project) }
let(:remote_mirror) { create(:remote_mirror, project: project) }
@@ -2653,7 +2663,7 @@ describe NotificationService, :mailer do
end
end
- context 'with external authorization service' do
+ context 'with external authorization service', :deliver_mails_inline do
let(:issue) { create(:issue) }
let(:project) { issue.project }
let(:note) { create(:note, noteable: issue, project: project) }
diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb
index 024340310a1..6df33e68629 100644
--- a/spec/support/helpers/email_helpers.rb
+++ b/spec/support/helpers/email_helpers.rb
@@ -6,7 +6,12 @@ module EmailHelpers
end
def reset_delivered_emails!
+ # We shouldn't actually send the emails, but we keep the following line for
+ # back-compatibility until we only check the mailer jobs enqueued in Sidekiq
ActionMailer::Base.deliveries.clear
+ # We should only check that the mailer jobs are enqueued in Sidekiq, hence
+ # clearing the background jobs queue
+ ActiveJob::Base.queue_adapter.enqueued_jobs.clear
end
def should_only_email(*users, kind: :to)
diff --git a/spec/support/helpers/notification_helpers.rb b/spec/support/helpers/notification_helpers.rb
index 16ecb338f6e..aee76b8be4a 100644
--- a/spec/support/helpers/notification_helpers.rb
+++ b/spec/support/helpers/notification_helpers.rb
@@ -36,4 +36,28 @@ module NotificationHelpers
setting = user.notification_settings_for(resource)
setting.update!(event => value)
end
+
+ def expect_delivery_jobs_count(count)
+ expect(ActionMailer::DeliveryJob).to have_been_enqueued.exactly(count).times
+ end
+
+ def expect_no_delivery_jobs
+ expect(ActionMailer::DeliveryJob).not_to have_been_enqueued
+ end
+
+ def expect_any_delivery_jobs
+ expect(ActionMailer::DeliveryJob).to have_been_enqueued.at_least(:once)
+ end
+
+ def have_enqueued_email(*args, mailer: "Notify", mail: "", delivery: "deliver_now")
+ have_enqueued_job(ActionMailer::DeliveryJob).with(mailer, mail, delivery, *args)
+ end
+
+ def expect_enqueud_email(*args, mailer: "Notify", mail: "", delivery: "deliver_now")
+ expect(ActionMailer::DeliveryJob).to have_been_enqueued.with(mailer, mail, delivery, *args)
+ end
+
+ def expect_not_enqueud_email(*args, mailer: "Notify", mail: "", delivery: "deliver_now")
+ expect(ActionMailer::DeliveryJob).not_to have_been_enqueued.with(mailer, mail, *args, any_args)
+ end
end
diff --git a/spec/support/shared_examples/services/notification_service_shared_examples.rb b/spec/support/shared_examples/services/notification_service_shared_examples.rb
index b4d52d24de4..43fe6789145 100644
--- a/spec/support/shared_examples/services/notification_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/notification_service_shared_examples.rb
@@ -3,7 +3,7 @@
# Note that we actually update the attribute on the target_project/group, rather than
# using `allow`. This is because there are some specs where, based on how the notification
# is done, using an `allow` doesn't change the correct object.
-RSpec.shared_examples 'project emails are disabled' do
+RSpec.shared_examples 'project emails are disabled' do |check_delivery_jobs_queue: false|
let(:target_project) { notification_target.is_a?(Project) ? notification_target : notification_target.project }
before do
@@ -16,7 +16,13 @@ RSpec.shared_examples 'project emails are disabled' do
notification_trigger
- should_not_email_anyone
+ if check_delivery_jobs_queue
+ # Only check enqueud jobs, not delivered emails
+ expect_no_delivery_jobs
+ else
+ # Deprecated: Check actual delivered emails
+ should_not_email_anyone
+ end
end
it 'sends emails to someone' do
@@ -24,7 +30,13 @@ RSpec.shared_examples 'project emails are disabled' do
notification_trigger
- should_email_anyone
+ if check_delivery_jobs_queue
+ # Only check enqueud jobs, not delivered emails
+ expect_any_delivery_jobs
+ else
+ # Deprecated: Check actual delivered emails
+ should_email_anyone
+ end
end
end