summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-07 21:09:26 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-07 21:09:26 +0000
commit17c8111494f51e79744c782db023804f5e4a7410 (patch)
tree8aebe53b8aea72f9d4abac1bf9131203302a5b6e
parent4b7575da97d88ef4c7b2ec599b0c3fc457b4f561 (diff)
downloadgitlab-ce-17c8111494f51e79744c782db023804f5e4a7410.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/api.js47
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js2
-rw-r--r--app/assets/javascripts/blob/blob_fork_suggestion.js2
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js5
-rw-r--r--app/assets/javascripts/close_reopen_report_toggle.js2
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js2
-rw-r--r--app/assets/javascripts/comment_type_toggle.js2
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js2
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_store.js2
-rw-r--r--app/assets/javascripts/design_management/components/design_note_pin.vue4
-rw-r--r--app/assets/javascripts/diffs/store/utils.js2
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js9
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js2
-rw-r--r--app/assets/javascripts/filtered_search/stores/recent_searches_store.js14
-rw-r--r--app/assets/javascripts/gl_dropdown.js5
-rw-r--r--app/assets/javascripts/gl_form.js2
-rw-r--r--app/assets/javascripts/groups/new_group_child.js2
-rw-r--r--app/assets/javascripts/ide/components/nav_form.vue4
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff.js13
-rw-r--r--app/assets/javascripts/ide/stores/mutations/project.js4
-rw-r--r--app/assets/javascripts/ide/stores/mutations/tree.js5
-rw-r--r--app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js4
-rw-r--r--app/assets/javascripts/image_diff/helpers/dom_helper.js7
-rw-r--r--app/assets/javascripts/image_diff/image_diff.js4
-rw-r--r--app/assets/javascripts/jobs/store/actions.js4
-rw-r--r--app/assets/javascripts/milestones/project_milestone_combobox.vue228
-rw-r--r--app/assets/javascripts/mr_notes/init_notes.js31
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue5
-rw-r--r--app/assets/javascripts/notes/index.js50
-rw-r--r--app/assets/javascripts/notes/stores/actions.js2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue5
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_summary.vue9
-rw-r--r--app/assets/javascripts/pipelines/stores/pipeline_store.js2
-rw-r--r--app/assets/javascripts/registry/settings/store/mutations.js2
-rw-r--r--app/assets/javascripts/registry/shared/constants.js2
-rw-r--r--app/assets/javascripts/releases/components/app_edit.vue47
-rw-r--r--app/assets/javascripts/releases/components/app_index.vue11
-rw-r--r--app/assets/javascripts/releases/components/release_block_header.vue11
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/actions.js11
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/mutation_types.js1
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/mutations.js4
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/state.js4
-rw-r--r--app/assets/javascripts/terminal/terminal.js13
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue6
-rw-r--r--app/assets/stylesheets/components/milestone_combobox.scss13
-rw-r--r--app/controllers/projects/alert_management_controller.rb1
-rw-r--r--app/finders/alert_management/alerts_finder.rb2
-rw-r--r--app/graphql/mutations/alert_management/base.rb2
-rw-r--r--app/graphql/types/alert_management/alert_type.rb2
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/releases_helper.rb4
-rw-r--r--app/policies/project_policy.rb5
-rw-r--r--app/services/search_service.rb19
-rw-r--r--changelogs/unreleased/207267-expiration-policy-copy.yml5
-rw-r--r--changelogs/unreleased/207324-search-api-scoped-to-blobs-does-not-honor-per_page.yml5
-rw-r--r--changelogs/unreleased/216477-gllink-updates.yml5
-rw-r--r--changelogs/unreleased/39467-allow-a-release-s-associated-milestones-to-be-edited-through-the-ed.yml5
-rw-r--r--changelogs/unreleased/rw-exclude-skipped-tests-from-success.yml4
-rw-r--r--doc/administration/audit_events.md6
-rw-r--r--doc/administration/logs.md6
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_exporter.md6
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md6
-rw-r--r--doc/administration/monitoring/prometheus/index.md6
-rw-r--r--doc/administration/monitoring/prometheus/node_exporter.md6
-rw-r--r--doc/administration/monitoring/prometheus/pgbouncer_exporter.md6
-rw-r--r--doc/administration/monitoring/prometheus/postgres_exporter.md6
-rw-r--r--doc/administration/monitoring/prometheus/redis_exporter.md6
-rw-r--r--doc/administration/monitoring/prometheus/registry_exporter.md6
-rw-r--r--doc/administration/reference_architectures/index.md69
-rw-r--r--doc/api/projects.md8
-rw-r--r--doc/development/code_review.md4
-rw-r--r--doc/user/incident_management/index.md3
-rw-r--r--doc/user/packages/container_registry/img/expiration_policy_app_v13_0.pngbin59340 -> 106289 bytes
-rw-r--r--doc/user/packages/container_registry/index.md12
-rw-r--r--doc/user/project/clusters/index.md6
-rw-r--r--doc/user/project/integrations/generic_alerts.md6
-rw-r--r--doc/user/project/integrations/prometheus.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/cloudwatch.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/haproxy.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/index.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/kubernetes.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress.md6
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md6
-rw-r--r--doc/user/project/integrations/prometheus_units.md6
-rw-r--r--doc/user/project/operations/error_tracking.md6
-rw-r--r--doc/user/project/operations/tracing.md6
-rw-r--r--doc/user/project/status_page/index.md6
-rw-r--r--lib/gitlab/group_search_results.rb4
-rw-r--r--lib/gitlab/project_search_results.rb29
-rw-r--r--lib/gitlab/search_results.rb13
-rw-r--r--lib/gitlab/sidekiq_logging/json_formatter.rb15
-rw-r--r--lib/gitlab/snippet_search_results.rb6
-rw-r--r--locale/gitlab.pot37
-rw-r--r--package.json4
-rw-r--r--spec/controllers/projects/alert_management_controller_spec.rb22
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/frontend/api_spec.js2
-rw-r--r--spec/frontend/blob/components/blob_content_spec.js2
-rw-r--r--spec/frontend/blob/components/blob_header_filepath_spec.js2
-rw-r--r--spec/frontend/blob/components/blob_header_spec.js2
-rw-r--r--spec/frontend/boards/board_list_spec.js2
-rw-r--r--spec/frontend/clusters/components/knative_domain_editor_spec.js2
-rw-r--r--spec/frontend/commit/pipelines/pipelines_spec.js44
-rw-r--r--spec/frontend/diffs/components/diff_discussions_spec.js2
-rw-r--r--spec/frontend/diffs/components/diff_expansion_cell_spec.js2
-rw-r--r--spec/frontend/diffs/components/diff_gutter_avatars_spec.js2
-rw-r--r--spec/frontend/diffs/components/diff_line_note_form_spec.js2
-rw-r--r--spec/frontend/diffs/components/inline_diff_expansion_row_spec.js2
-rw-r--r--spec/frontend/diffs/components/inline_diff_view_spec.js4
-rw-r--r--spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js2
-rw-r--r--spec/frontend/diffs/components/parallel_diff_view_spec.js2
-rw-r--r--spec/frontend/diffs/store/getters_spec.js4
-rw-r--r--spec/frontend/diffs/store/utils_spec.js6
-rw-r--r--spec/frontend/groups/components/app_spec.js10
-rw-r--r--spec/frontend/groups/components/group_folder_spec.js2
-rw-r--r--spec/frontend/groups/components/group_item_spec.js8
-rw-r--r--spec/frontend/groups/components/item_actions_spec.js4
-rw-r--r--spec/frontend/groups/components/item_stats_spec.js25
-rw-r--r--spec/frontend/groups/components/item_stats_value_spec.js2
-rw-r--r--spec/frontend/groups/store/groups_store_spec.js4
-rw-r--r--spec/frontend/ide/components/ide_review_spec.js2
-rw-r--r--spec/frontend/ide/components/ide_spec.js4
-rw-r--r--spec/frontend/ide/components/ide_tree_list_spec.js2
-rw-r--r--spec/frontend/ide/components/ide_tree_spec.js2
-rw-r--r--spec/frontend/import_projects/components/import_projects_table_spec.js5
-rw-r--r--spec/frontend/import_projects/components/provider_repo_table_row_spec.js4
-rw-r--r--spec/frontend/integrations/edit/components/active_toggle_spec.js2
-rw-r--r--spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js2
-rw-r--r--spec/frontend/issue_show/components/description_spec.js7
-rw-r--r--spec/frontend/jobs/components/log/line_header_spec.js2
-rw-r--r--spec/frontend/jobs/store/mutations_spec.js2
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js2
-rw-r--r--spec/frontend/milestones/mock_data.js82
-rw-r--r--spec/frontend/milestones/project_milestone_combobox_spec.js150
-rw-r--r--spec/frontend/notes/components/noteable_discussion_spec.js2
-rw-r--r--spec/frontend/notes/old_notes_spec.js2
-rw-r--r--spec/frontend/notes/stores/collapse_utils_spec.js4
-rw-r--r--spec/frontend/notes/stores/mutation_spec.js20
-rw-r--r--spec/frontend/pipelines/graph/stage_column_component_spec.js2
-rw-r--r--spec/frontend/pipelines/linked_pipelines_mock.json (renamed from spec/javascripts/pipelines/linked_pipelines_mock.json)3
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js2
-rw-r--r--spec/frontend/pipelines/pipelines_table_row_spec.js2
-rw-r--r--spec/frontend/pipelines/stage_spec.js2
-rw-r--r--spec/frontend/pipelines/stores/pipeline_store_spec.js (renamed from spec/javascripts/pipelines/stores/pipeline_store.js)40
-rw-r--r--spec/frontend/pipelines/test_reports/stores/mutations_spec.js6
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_spec.js18
-rw-r--r--spec/frontend/releases/components/app_edit_spec.js9
-rw-r--r--spec/frontend/releases/stores/modules/detail/actions_spec.js10
-rw-r--r--spec/frontend/snippets/components/snippet_blob_view_spec.js4
-rw-r--r--spec/frontend/snippets/components/snippet_title_spec.js2
-rw-r--r--spec/frontend/tracking_spec.js5
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js4
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/file_row_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/issue/issue_milestone_spec.js44
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js15
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js2
-rw-r--r--spec/graphql/mutations/alert_management/update_alert_status_spec.rb2
-rw-r--r--spec/graphql/types/alert_management/alert_type_spec.rb2
-rw-r--r--spec/helpers/releases_helper_spec.rb4
-rw-r--r--spec/javascripts/emoji_spec.js25
-rw-r--r--spec/javascripts/gl_dropdown_spec.js22
-rw-r--r--spec/javascripts/image_diff/image_badge_spec.js18
-rw-r--r--spec/javascripts/jobs/components/commit_block_spec.js2
-rw-r--r--spec/javascripts/jobs/components/sidebar_spec.js7
-rw-r--r--spec/javascripts/pipelines/mock_data.js423
-rw-r--r--spec/javascripts/pipelines/stores/pipeline.json167
-rw-r--r--spec/javascripts/pipelines/stores/pipeline_with_triggered.json381
-rw-r--r--spec/javascripts/pipelines/stores/pipeline_with_triggered_by.json379
-rw-r--r--spec/javascripts/pipelines/stores/pipeline_with_triggered_triggered_by.json452
-rw-r--r--spec/javascripts/reports/components/test_issue_body_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js51
-rw-r--r--spec/javascripts/vue_shared/components/markdown/toolbar_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js21
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb97
-rw-r--r--spec/lib/gitlab/search_results_spec.rb10
-rw-r--r--spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb23
-rw-r--r--spec/lib/gitlab/snippet_search_results_spec.rb10
-rw-r--r--spec/policies/alert_management/alert_policy_spec.rb8
-rw-r--r--spec/requests/api/search_spec.rb240
-rw-r--r--spec/services/search_service_spec.rb74
-rw-r--r--yarn.lock18
184 files changed, 1646 insertions, 2459 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 904bf117dc0..e527659a939 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -23,6 +23,8 @@ const Api = {
projectMergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
projectRunnersPath: '/api/:version/projects/:id/runners',
projectProtectedBranchesPath: '/api/:version/projects/:id/protected_branches',
+ projectSearchPath: '/api/:version/projects/:id/search',
+ projectMilestonesPath: '/api/:version/projects/:id/milestones',
mergeRequestsPath: '/api/:version/merge_requests',
groupLabelsPath: '/groups/:namespace_path/-/labels',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
@@ -75,13 +77,11 @@ const Api = {
const url = Api.buildUrl(Api.groupsPath);
return axios
.get(url, {
- params: Object.assign(
- {
- search: query,
- per_page: DEFAULT_PER_PAGE,
- },
- options,
- ),
+ params: {
+ search: query,
+ per_page: DEFAULT_PER_PAGE,
+ ...options,
+ },
})
.then(({ data }) => {
callback(data);
@@ -248,6 +248,23 @@ const Api = {
.then(({ data }) => data);
},
+ projectSearch(id, options = {}) {
+ const url = Api.buildUrl(Api.projectSearchPath).replace(':id', encodeURIComponent(id));
+
+ return axios.get(url, {
+ params: {
+ search: options.search,
+ scope: options.scope,
+ },
+ });
+ },
+
+ projectMilestones(id) {
+ const url = Api.buildUrl(Api.projectMilestonesPath).replace(':id', encodeURIComponent(id));
+
+ return axios.get(url);
+ },
+
mergeRequests(params = {}) {
const url = Api.buildUrl(Api.mergeRequestsPath);
@@ -282,7 +299,7 @@ const Api = {
};
return axios
.get(url, {
- params: Object.assign({}, defaults, options),
+ params: { ...defaults, ...options },
})
.then(({ data }) => callback(data))
.catch(() => flash(__('Something went wrong while fetching projects')));
@@ -365,13 +382,11 @@ const Api = {
users(query, options) {
const url = Api.buildUrl(this.usersPath);
return axios.get(url, {
- params: Object.assign(
- {
- search: query,
- per_page: DEFAULT_PER_PAGE,
- },
- options,
- ),
+ params: {
+ search: query,
+ per_page: DEFAULT_PER_PAGE,
+ ...options,
+ },
});
},
@@ -402,7 +417,7 @@ const Api = {
};
return axios
.get(url, {
- params: Object.assign({}, defaults, options),
+ params: { ...defaults, ...options },
})
.then(({ data }) => callback(data))
.catch(() => flash(__('Something went wrong while fetching projects')));
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
index d5d8edd5ac0..c35a073b291 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
@@ -22,7 +22,7 @@ function eventHasModifierKeys(event) {
export default class ShortcutsBlob extends Shortcuts {
constructor(opts) {
- const options = Object.assign({}, defaults, opts);
+ const options = { ...defaults, ...opts };
super(options.skipResetBindings);
this.options = options;
diff --git a/app/assets/javascripts/blob/blob_fork_suggestion.js b/app/assets/javascripts/blob/blob_fork_suggestion.js
index 476b9405a9e..44dfbfcfe1c 100644
--- a/app/assets/javascripts/blob/blob_fork_suggestion.js
+++ b/app/assets/javascripts/blob/blob_fork_suggestion.js
@@ -17,7 +17,7 @@ const defaults = {
class BlobForkSuggestion {
constructor(options) {
- this.elementMap = Object.assign({}, defaults, options);
+ this.elementMap = { ...defaults, ...options };
this.onOpenButtonClick = this.onOpenButtonClick.bind(this);
this.onCancelButtonClick = this.onCancelButtonClick.bind(this);
}
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js b/app/assets/javascripts/boards/mixins/sortable_default_options.js
index 68ea28e68d9..fceb8c9d48e 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js
@@ -19,14 +19,15 @@ export function getBoardSortableDefaultOptions(obj) {
const touchEnabled =
'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);
- const defaultSortOptions = Object.assign({}, sortableConfig, {
+ const defaultSortOptions = {
+ ...sortableConfig,
filter: '.no-drag',
delay: touchEnabled ? 100 : 0,
scrollSensitivity: touchEnabled ? 60 : 100,
scrollSpeed: 20,
onStart: sortableStart,
onEnd: sortableEnd,
- });
+ };
Object.keys(obj).forEach(key => {
defaultSortOptions[key] = obj[key];
diff --git a/app/assets/javascripts/close_reopen_report_toggle.js b/app/assets/javascripts/close_reopen_report_toggle.js
index 882d20671cc..bcddce6e727 100644
--- a/app/assets/javascripts/close_reopen_report_toggle.js
+++ b/app/assets/javascripts/close_reopen_report_toggle.js
@@ -2,7 +2,7 @@ import DropLab from './droplab/drop_lab';
import ISetter from './droplab/plugins/input_setter';
// Todo: Remove this when fixing issue in input_setter plugin
-const InputSetter = Object.assign({}, ISetter);
+const InputSetter = { ...ISetter };
class CloseReopenReportToggle {
constructor(opts = {}) {
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index d46525def06..3699a3b8b2b 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -325,7 +325,7 @@ export default class Clusters {
handleClusterStatusSuccess(data) {
const prevStatus = this.store.state.status;
- const prevApplicationMap = Object.assign({}, this.store.state.applications);
+ const prevApplicationMap = { ...this.store.state.applications };
this.store.updateStateFromServer(data.data);
diff --git a/app/assets/javascripts/comment_type_toggle.js b/app/assets/javascripts/comment_type_toggle.js
index a259667bb75..2fcd40a901d 100644
--- a/app/assets/javascripts/comment_type_toggle.js
+++ b/app/assets/javascripts/comment_type_toggle.js
@@ -2,7 +2,7 @@ import DropLab from './droplab/drop_lab';
import ISetter from './droplab/plugins/input_setter';
// Todo: Remove this when fixing issue in input_setter plugin
-const InputSetter = Object.assign({}, ISetter);
+const InputSetter = { ...ISetter };
class CommentTypeToggle {
constructor(opts = {}) {
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index ba585444ba5..801566d2f2f 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -13,7 +13,7 @@ import {
import confidentialMergeRequestState from './confidential_merge_request/state';
// Todo: Remove this when fixing issue in input_setter plugin
-const InputSetter = Object.assign({}, ISetter);
+const InputSetter = { ...ISetter };
const CREATE_MERGE_REQUEST = 'create-mr';
const CREATE_BRANCH = 'create-branch';
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
index 304a0726597..4f9069f61a5 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
@@ -84,7 +84,7 @@ export default {
events.forEach(item => {
if (!item) return;
- const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
+ const eventItem = { ...DEFAULT_EVENT_OBJECTS[stage.slug], ...item };
eventItem.totalTime = eventItem.total_time;
diff --git a/app/assets/javascripts/design_management/components/design_note_pin.vue b/app/assets/javascripts/design_management/components/design_note_pin.vue
index b6b587a12e6..50ea69d52ce 100644
--- a/app/assets/javascripts/design_management/components/design_note_pin.vue
+++ b/app/assets/javascripts/design_management/components/design_note_pin.vue
@@ -28,9 +28,7 @@ export default {
return this.label === null;
},
pinStyle() {
- return this.repositioning
- ? Object.assign({}, this.position, { cursor: 'move' })
- : this.position;
+ return this.repositioning ? { ...this.position, cursor: 'move' } : this.position;
},
pinLabel() {
return this.isNewNote
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index b46b8d95d5f..eb4c6683035 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -233,7 +233,7 @@ export function trimFirstCharOfLineContent(line = {}) {
// eslint-disable-next-line no-param-reassign
delete line.text;
- const parsedLine = Object.assign({}, line);
+ const parsedLine = { ...line };
if (line.rich_text) {
const firstChar = parsedLine.rich_text.charAt(0);
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index e07ec693948..1992e753255 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -58,13 +58,14 @@ export default class EnvironmentsStore {
let filtered = {};
if (env.size > 1) {
- filtered = Object.assign({}, env, {
+ filtered = {
+ ...env,
isFolder: true,
isLoadingFolderContent: oldEnvironmentState.isLoading || false,
folderName: env.name,
isOpen: oldEnvironmentState.isOpen || false,
children: oldEnvironmentState.children || [],
- });
+ };
}
if (env.latest) {
@@ -166,7 +167,7 @@ export default class EnvironmentsStore {
let updated = env;
if (env.latest) {
- updated = Object.assign({}, env, env.latest);
+ updated = { ...env, ...env.latest };
delete updated.latest;
} else {
updated = env;
@@ -192,7 +193,7 @@ export default class EnvironmentsStore {
const { environments } = this.state;
const updatedEnvironments = environments.map(env => {
- const updateEnv = Object.assign({}, env);
+ const updateEnv = { ...env };
if (env.id === environment.id) {
updateEnv[prop] = newValue;
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index d051b60814e..161a65c511d 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -120,7 +120,7 @@ export default class FilteredSearchDropdownManager {
filter: key,
};
const extraArguments = mappingKey.extraArguments || {};
- const glArguments = Object.assign({}, defaultArguments, extraArguments);
+ const glArguments = { ...defaultArguments, ...extraArguments };
// Passing glArguments to `new glClass(<arguments>)`
mappingKey.reference = new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
diff --git a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
index b3eb0475d6f..cdbc9ec84bd 100644
--- a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
+++ b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
@@ -2,14 +2,12 @@ import { uniq } from 'lodash';
class RecentSearchesStore {
constructor(initialState = {}, allowedKeys) {
- this.state = Object.assign(
- {
- isLocalStorageAvailable: true,
- recentSearches: [],
- allowedKeys,
- },
- initialState,
- );
+ this.state = {
+ isLocalStorageAvailable: true,
+ recentSearches: [],
+ allowedKeys,
+ ...initialState,
+ };
}
addRecentSearch(newSearch) {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 1490498c511..be4b4b5f87d 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -595,13 +595,14 @@ class GitLabDropdown {
return renderItem({
instance: this,
- options: Object.assign({}, this.options, {
+ options: {
+ ...this.options,
icon: this.icon,
highlight: this.highlight,
highlightText: text => this.highlightTextMatches(text, this.filterInput.val()),
highlightTemplate: this.highlightTemplate.bind(this),
parent,
- }),
+ },
data,
group,
index,
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index ced10fff129..0b7735a7db9 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -8,7 +8,7 @@ export default class GLForm {
constructor(form, enableGFM = {}) {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
- this.enableGFM = Object.assign({}, defaultAutocompleteConfig, enableGFM);
+ this.enableGFM = { ...defaultAutocompleteConfig, ...enableGFM };
// Disable autocomplete for keywords which do not have dataSources available
const dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
Object.keys(this.enableGFM).forEach(item => {
diff --git a/app/assets/javascripts/groups/new_group_child.js b/app/assets/javascripts/groups/new_group_child.js
index 012177479c6..bb2aea3ea76 100644
--- a/app/assets/javascripts/groups/new_group_child.js
+++ b/app/assets/javascripts/groups/new_group_child.js
@@ -2,7 +2,7 @@ import { visitUrl } from '../lib/utils/url_utility';
import DropLab from '../droplab/drop_lab';
import ISetter from '../droplab/plugins/input_setter';
-const InputSetter = Object.assign({}, ISetter);
+const InputSetter = { ...ISetter };
const NEW_PROJECT = 'new-project';
const NEW_SUBGROUP = 'new-subgroup';
diff --git a/app/assets/javascripts/ide/components/nav_form.vue b/app/assets/javascripts/ide/components/nav_form.vue
index 195504a6861..70a92b8d3ab 100644
--- a/app/assets/javascripts/ide/components/nav_form.vue
+++ b/app/assets/javascripts/ide/components/nav_form.vue
@@ -25,13 +25,13 @@ export default {
<div class="ide-nav-form p-0">
<tabs v-if="showMergeRequests" stop-propagation>
<tab active>
- <template slot="title">
+ <template #title>
{{ __('Branches') }}
</template>
<branches-search-list />
</tab>
<tab>
- <template slot="title">
+ <template #title>
{{ __('Merge Requests') }}
</template>
<merge-request-search-list />
diff --git a/app/assets/javascripts/ide/lib/diff/diff.js b/app/assets/javascripts/ide/lib/diff/diff.js
index 9b7ed68b893..29e29d7fcd3 100644
--- a/app/assets/javascripts/ide/lib/diff/diff.js
+++ b/app/assets/javascripts/ide/lib/diff/diff.js
@@ -14,13 +14,12 @@ export const computeDiff = (originalContent, newContent) => {
endLineNumber: lineNumber + change.count - 1,
});
} else if ('added' in change || 'removed' in change) {
- acc.push(
- Object.assign({}, change, {
- lineNumber,
- modified: undefined,
- endLineNumber: lineNumber + change.count - 1,
- }),
- );
+ acc.push({
+ ...change,
+ lineNumber,
+ modified: undefined,
+ endLineNumber: lineNumber + change.count - 1,
+ });
}
if (!change.removed) {
diff --git a/app/assets/javascripts/ide/stores/mutations/project.js b/app/assets/javascripts/ide/stores/mutations/project.js
index 9230f3839c1..034fdad4305 100644
--- a/app/assets/javascripts/ide/stores/mutations/project.js
+++ b/app/assets/javascripts/ide/stores/mutations/project.js
@@ -16,9 +16,7 @@ export default {
});
Object.assign(state, {
- projects: Object.assign({}, state.projects, {
- [projectPath]: project,
- }),
+ projects: { ...state.projects, [projectPath]: project },
});
},
[types.TOGGLE_EMPTY_STATE](state, { projectPath, value }) {
diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js
index 359943b4ab7..c8f14a680c2 100644
--- a/app/assets/javascripts/ide/stores/mutations/tree.js
+++ b/app/assets/javascripts/ide/stores/mutations/tree.js
@@ -14,12 +14,13 @@ export default {
},
[types.CREATE_TREE](state, { treePath }) {
Object.assign(state, {
- trees: Object.assign({}, state.trees, {
+ trees: {
+ ...state.trees,
[treePath]: {
tree: [],
loading: true,
},
- }),
+ },
});
},
[types.SET_DIRECTORY_DATA](state, { data, treePath }) {
diff --git a/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js b/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
index df3d90cff68..deaef686f59 100644
--- a/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
@@ -32,9 +32,7 @@ export function removeCommentIndicator(imageFrameEl) {
commentIndicatorEl.remove();
}
- return Object.assign({}, meta, {
- removed: willRemove,
- });
+ return { ...meta, removed: willRemove };
}
export function showCommentIndicator(imageFrameEl, coordinate) {
diff --git a/app/assets/javascripts/image_diff/helpers/dom_helper.js b/app/assets/javascripts/image_diff/helpers/dom_helper.js
index a319bcccb8f..74ca907c99f 100644
--- a/app/assets/javascripts/image_diff/helpers/dom_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/dom_helper.js
@@ -4,12 +4,7 @@ export function setPositionDataAttribute(el, options) {
const { x, y, width, height } = options;
const { position } = el.dataset;
- const positionObject = Object.assign({}, JSON.parse(position), {
- x,
- y,
- width,
- height,
- });
+ const positionObject = { ...JSON.parse(position), x, y, width, height };
el.setAttribute('data-position', JSON.stringify(positionObject));
}
diff --git a/app/assets/javascripts/image_diff/image_diff.js b/app/assets/javascripts/image_diff/image_diff.js
index 26c1b0ec7be..89f696dd1d8 100644
--- a/app/assets/javascripts/image_diff/image_diff.js
+++ b/app/assets/javascripts/image_diff/image_diff.js
@@ -75,9 +75,7 @@ export default class ImageDiff {
if (this.renderCommentBadge) {
imageDiffHelper.addImageCommentBadge(this.imageFrameEl, options);
} else {
- const numberBadgeOptions = Object.assign({}, options, {
- badgeText: index + 1,
- });
+ const numberBadgeOptions = { ...options, badgeText: index + 1 };
imageDiffHelper.addImageBadge(this.imageFrameEl, numberBadgeOptions);
}
diff --git a/app/assets/javascripts/jobs/store/actions.js b/app/assets/javascripts/jobs/store/actions.js
index f4030939f2c..0ce8dfe4442 100644
--- a/app/assets/javascripts/jobs/store/actions.js
+++ b/app/assets/javascripts/jobs/store/actions.js
@@ -220,7 +220,7 @@ export const fetchJobsForStage = ({ dispatch }, stage = {}) => {
},
})
.then(({ data }) => {
- const retriedJobs = data.retried.map(job => Object.assign({}, job, { retried: true }));
+ const retriedJobs = data.retried.map(job => ({ ...job, retried: true }));
const jobs = data.latest_statuses.concat(retriedJobs);
dispatch('receiveJobsForStageSuccess', jobs);
@@ -236,7 +236,7 @@ export const receiveJobsForStageError = ({ commit }) => {
export const triggerManualJob = ({ state }, variables) => {
const parsedVariables = variables.map(variable => {
- const copyVar = Object.assign({}, variable);
+ const copyVar = { ...variable };
delete copyVar.id;
return copyVar;
});
diff --git a/app/assets/javascripts/milestones/project_milestone_combobox.vue b/app/assets/javascripts/milestones/project_milestone_combobox.vue
new file mode 100644
index 00000000000..19148d6184f
--- /dev/null
+++ b/app/assets/javascripts/milestones/project_milestone_combobox.vue
@@ -0,0 +1,228 @@
+<script>
+import {
+ GlNewDropdown,
+ GlNewDropdownDivider,
+ GlNewDropdownHeader,
+ GlNewDropdownItem,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlIcon,
+} from '@gitlab/ui';
+import { __, sprintf } from '~/locale';
+import Api from '~/api';
+import createFlash from '~/flash';
+import { intersection, debounce } from 'lodash';
+
+export default {
+ components: {
+ GlNewDropdown,
+ GlNewDropdownDivider,
+ GlNewDropdownHeader,
+ GlNewDropdownItem,
+ GlLoadingIcon,
+ GlSearchBoxByType,
+ GlIcon,
+ },
+ model: {
+ prop: 'preselectedMilestones',
+ event: 'change',
+ },
+ props: {
+ projectId: {
+ type: String,
+ required: true,
+ },
+ preselectedMilestones: {
+ type: Array,
+ default: () => [],
+ required: false,
+ },
+ extraLinks: {
+ type: Array,
+ default: () => [],
+ required: false,
+ },
+ },
+ data() {
+ return {
+ searchQuery: '',
+ projectMilestones: [],
+ searchResults: [],
+ selectedMilestones: [],
+ requestCount: 0,
+ };
+ },
+ translations: {
+ milestone: __('Milestone'),
+ selectMilestone: __('Select milestone'),
+ noMilestone: __('No milestone'),
+ noResultsLabel: __('No matching results'),
+ searchMilestones: __('Search Milestones'),
+ },
+ computed: {
+ selectedMilestonesLabel() {
+ if (this.milestoneTitles.length === 1) {
+ return this.milestoneTitles[0];
+ }
+
+ if (this.milestoneTitles.length > 1) {
+ const firstMilestoneName = this.milestoneTitles[0];
+ const numberOfOtherMilestones = this.milestoneTitles.length - 1;
+ return sprintf(__('%{firstMilestoneName} + %{numberOfOtherMilestones} more'), {
+ firstMilestoneName,
+ numberOfOtherMilestones,
+ });
+ }
+
+ return this.$options.translations.noMilestone;
+ },
+ milestoneTitles() {
+ return this.preselectedMilestones.map(milestone => milestone.title);
+ },
+ dropdownItems() {
+ return this.searchResults.length ? this.searchResults : this.projectMilestones;
+ },
+ noResults() {
+ return this.searchQuery.length > 2 && this.searchResults.length === 0;
+ },
+ isLoading() {
+ return this.requestCount !== 0;
+ },
+ },
+ mounted() {
+ this.fetchMilestones();
+ },
+ methods: {
+ fetchMilestones() {
+ this.requestCount += 1;
+
+ Api.projectMilestones(this.projectId)
+ .then(({ data }) => {
+ this.projectMilestones = this.getTitles(data);
+ this.selectedMilestones = intersection(this.projectMilestones, this.milestoneTitles);
+ })
+ .catch(() => {
+ createFlash(__('An error occurred while loading milestones'));
+ })
+ .finally(() => {
+ this.requestCount -= 1;
+ });
+ },
+ searchMilestones: debounce(function searchMilestones() {
+ this.requestCount += 1;
+ const options = {
+ search: this.searchQuery,
+ scope: 'milestones',
+ };
+
+ if (this.searchQuery.length < 3) {
+ this.requestCount -= 1;
+ this.searchResults = [];
+ return;
+ }
+
+ Api.projectSearch(this.projectId, options)
+ .then(({ data }) => {
+ const searchResults = this.getTitles(data);
+
+ this.searchResults = searchResults.length ? searchResults : [];
+ })
+ .catch(() => {
+ createFlash(__('An error occurred while searching for milestones'));
+ })
+ .finally(() => {
+ this.requestCount -= 1;
+ });
+ }, 100),
+ toggleMilestoneSelection(clickedMilestone) {
+ if (!clickedMilestone) return [];
+
+ let milestones = [...this.preselectedMilestones];
+ const hasMilestone = this.milestoneTitles.includes(clickedMilestone);
+
+ if (hasMilestone) {
+ milestones = milestones.filter(({ title }) => title !== clickedMilestone);
+ } else {
+ milestones.push({ title: clickedMilestone });
+ }
+
+ return milestones;
+ },
+ onMilestoneClicked(clickedMilestone) {
+ const milestones = this.toggleMilestoneSelection(clickedMilestone);
+ this.$emit('change', milestones);
+
+ this.selectedMilestones = intersection(
+ this.projectMilestones,
+ milestones.map(milestone => milestone.title),
+ );
+ },
+ isSelectedMilestone(milestoneTitle) {
+ return this.selectedMilestones.includes(milestoneTitle);
+ },
+ getTitles(milestones) {
+ return milestones.filter(({ state }) => state === 'active').map(({ title }) => title);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-new-dropdown>
+ <template slot="button-content">
+ <span ref="buttonText" class="flex-grow-1 ml-1 text-muted">{{
+ selectedMilestonesLabel
+ }}</span>
+ <gl-icon name="chevron-down" />
+ </template>
+
+ <gl-new-dropdown-header>
+ <span class="text-center d-block">{{ $options.translations.selectMilestone }}</span>
+ </gl-new-dropdown-header>
+
+ <gl-new-dropdown-divider />
+
+ <gl-search-box-by-type
+ v-model.trim="searchQuery"
+ class="m-2"
+ :placeholder="this.$options.translations.searchMilestones"
+ @input="searchMilestones"
+ />
+
+ <gl-new-dropdown-item @click="onMilestoneClicked(null)">
+ <span :class="{ 'pl-4': true, 'selected-item': selectedMilestones.length === 0 }">
+ {{ $options.translations.noMilestone }}
+ </span>
+ </gl-new-dropdown-item>
+
+ <gl-new-dropdown-divider />
+
+ <template v-if="isLoading">
+ <gl-loading-icon />
+ <gl-new-dropdown-divider />
+ </template>
+ <template v-else-if="noResults">
+ <div class="dropdown-item-space">
+ <span ref="noResults" class="pl-4">{{ $options.translations.noResultsLabel }}</span>
+ </div>
+ <gl-new-dropdown-divider />
+ </template>
+ <template v-else-if="dropdownItems.length">
+ <gl-new-dropdown-item
+ v-for="item in dropdownItems"
+ :key="item"
+ role="milestone option"
+ @click="onMilestoneClicked(item)"
+ >
+ <span :class="{ 'pl-4': true, 'selected-item': isSelectedMilestone(item) }">
+ {{ item }}
+ </span>
+ </gl-new-dropdown-item>
+ <gl-new-dropdown-divider />
+ </template>
+
+ <gl-new-dropdown-item v-for="(item, idx) in extraLinks" :key="idx" :href="item.url">
+ <span class="pl-4">{{ item.text }}</span>
+ </gl-new-dropdown-item>
+ </gl-new-dropdown>
+</template>
diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js
index ec9c800b7a2..2580f8e86b1 100644
--- a/app/assets/javascripts/mr_notes/init_notes.js
+++ b/app/assets/javascripts/mr_notes/init_notes.js
@@ -15,6 +15,19 @@ export default () => {
notesApp,
},
store,
+ data() {
+ const notesDataset = document.getElementById('js-vue-mr-discussions').dataset;
+ const noteableData = JSON.parse(notesDataset.noteableData);
+ noteableData.noteableType = notesDataset.noteableType;
+ noteableData.targetType = notesDataset.targetType;
+
+ return {
+ noteableData,
+ currentUserData: JSON.parse(notesDataset.currentUserData),
+ notesData: JSON.parse(notesDataset.notesData),
+ helpPagePath: notesDataset.helpPagePath,
+ };
+ },
computed: {
...mapGetters(['discussionTabCounter']),
...mapState({
@@ -54,19 +67,6 @@ export default () => {
updateDiscussionTabCounter() {
this.notesCountBadge.text(this.discussionTabCounter);
},
- dataset() {
- const data = this.$el.dataset;
- const noteableData = JSON.parse(data.noteableData);
- noteableData.noteableType = data.noteableType;
- noteableData.targetType = data.targetType;
-
- return {
- noteableData,
- notesData: JSON.parse(data.notesData),
- userData: JSON.parse(data.currentUserData),
- helpPagePath: data.helpPagePath,
- };
- },
},
render(createElement) {
// NOTE: Even though `discussionKeyboardNavigator` is added to the `notes-app`,
@@ -76,8 +76,11 @@ export default () => {
return createElement(discussionKeyboardNavigator, [
createElement('notes-app', {
props: {
- ...this.dataset(),
+ noteableData: this.noteableData,
+ notesData: this.notesData,
+ userData: this.currentUserData,
shouldShow: this.isShowTabActive,
+ helpPagePath: this.helpPagePath,
},
}),
]);
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index c1dd56aedf2..faa6006945d 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -230,10 +230,11 @@ export default {
const defaultConfig = { path: this.getNotesDataByProp('discussionsPath') };
if (doesHashExistInUrl(constants.NOTE_UNDERSCORE)) {
- return Object.assign({}, defaultConfig, {
+ return {
+ ...defaultConfig,
filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
persistFilter: false,
- });
+ };
}
return defaultConfig;
},
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 6fd3cee5340..8f9e2359e0d 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -14,36 +14,38 @@ document.addEventListener('DOMContentLoaded', () => {
notesApp,
},
store,
- methods: {
- setData() {
- const notesDataset = this.$el.dataset;
- const parsedUserData = JSON.parse(notesDataset.currentUserData);
- const noteableData = JSON.parse(notesDataset.noteableData);
- let currentUserData = {};
+ data() {
+ const notesDataset = document.getElementById('js-vue-notes').dataset;
+ const parsedUserData = JSON.parse(notesDataset.currentUserData);
+ const noteableData = JSON.parse(notesDataset.noteableData);
+ let currentUserData = {};
- noteableData.noteableType = notesDataset.noteableType;
- noteableData.targetType = notesDataset.targetType;
+ noteableData.noteableType = notesDataset.noteableType;
+ noteableData.targetType = notesDataset.targetType;
- if (parsedUserData) {
- currentUserData = {
- id: parsedUserData.id,
- name: parsedUserData.name,
- username: parsedUserData.username,
- avatar_url: parsedUserData.avatar_path || parsedUserData.avatar_url,
- path: parsedUserData.path,
- };
- }
-
- return {
- noteableData,
- userData: currentUserData,
- notesData: JSON.parse(notesDataset.notesData),
+ if (parsedUserData) {
+ currentUserData = {
+ id: parsedUserData.id,
+ name: parsedUserData.name,
+ username: parsedUserData.username,
+ avatar_url: parsedUserData.avatar_path || parsedUserData.avatar_url,
+ path: parsedUserData.path,
};
- },
+ }
+
+ return {
+ noteableData,
+ currentUserData,
+ notesData: JSON.parse(notesDataset.notesData),
+ };
},
render(createElement) {
return createElement('notes-app', {
- props: { ...this.setData() },
+ props: {
+ noteableData: this.noteableData,
+ notesData: this.notesData,
+ userData: this.currentUserData,
+ },
});
},
});
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index a358515c2ec..0999d0aa7ac 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -248,7 +248,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
const hasQuickActions = utils.hasQuickActions(placeholderText);
const replyId = noteData.data.in_reply_to_discussion_id;
let methodToDispatch;
- const postData = Object.assign({}, noteData);
+ const postData = { ...noteData };
if (postData.isDraft === true) {
methodToDispatch = replyId
? 'batchComments/addDraftToDiscussion'
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index e25f8ab4790..981914dd046 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -99,9 +99,10 @@ export default {
// 3. If GitLab user does not have avatar, they might have a Gravatar
} else if (this.pipeline.commit.author_gravatar_url) {
- commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, {
+ commitAuthorInformation = {
+ ...this.pipeline.commit.author,
avatar_url: this.pipeline.commit.author_gravatar_url,
- });
+ };
}
// 4. If committer is not a GitLab User, they can have a Gravatar
} else {
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
index 9739ef76867..80a1c83f171 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
@@ -29,7 +29,14 @@ export default {
successPercentage() {
// Returns a full number when the decimals equal .00.
// Otherwise returns a float to two decimal points
- return Number(((this.report.success_count / this.report.total_count) * 100 || 0).toFixed(2));
+ // Do not include skipped tests as part of the total when doing success calculations.
+
+ const totalCompletedCount = this.report.total_count - this.report.skipped_count;
+
+ if (totalCompletedCount > 0) {
+ return Number(((this.report.success_count / totalCompletedCount) * 100 || 0).toFixed(2));
+ }
+ return 0;
},
formattedDuration() {
return formatTime(secondsToMilliseconds(this.report.total_time));
diff --git a/app/assets/javascripts/pipelines/stores/pipeline_store.js b/app/assets/javascripts/pipelines/stores/pipeline_store.js
index 1ef73760e02..c6f65277c8d 100644
--- a/app/assets/javascripts/pipelines/stores/pipeline_store.js
+++ b/app/assets/javascripts/pipelines/stores/pipeline_store.js
@@ -15,7 +15,7 @@ export default class PipelineStore {
* @param {Object} pipeline
*/
storePipeline(pipeline = {}) {
- const pipelineCopy = Object.assign({}, pipeline);
+ const pipelineCopy = { ...pipeline };
if (pipelineCopy.triggered_by) {
pipelineCopy.triggered_by = [pipelineCopy.triggered_by];
diff --git a/app/assets/javascripts/registry/settings/store/mutations.js b/app/assets/javascripts/registry/settings/store/mutations.js
index bb7071b020b..3ba13419b98 100644
--- a/app/assets/javascripts/registry/settings/store/mutations.js
+++ b/app/assets/javascripts/registry/settings/store/mutations.js
@@ -21,7 +21,7 @@ export default {
state.original = Object.freeze(settings);
},
[types.RESET_SETTINGS](state) {
- state.settings = Object.assign({}, state.original);
+ state.settings = { ...state.original };
},
[types.TOGGLE_LOADING](state) {
state.isLoading = !state.isLoading;
diff --git a/app/assets/javascripts/registry/shared/constants.js b/app/assets/javascripts/registry/shared/constants.js
index 7a839e4a3ed..4689d01b1c8 100644
--- a/app/assets/javascripts/registry/shared/constants.js
+++ b/app/assets/javascripts/registry/shared/constants.js
@@ -41,5 +41,5 @@ export const NAME_REGEX_KEEP_LABEL = s__(
);
export const NAME_REGEX_KEEP_PLACEHOLDER = '';
export const NAME_REGEX_KEEP_DESCRIPTION = s__(
- 'ContainerRegistry|Regular expressions such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported',
+ 'ContainerRegistry|Regular expressions such as %{codeStart}.*-master%{codeEnd} or %{codeStart}release-.*%{codeEnd} are supported',
);
diff --git a/app/assets/javascripts/releases/components/app_edit.vue b/app/assets/javascripts/releases/components/app_edit.vue
index 1a1b2591cc8..433df839acb 100644
--- a/app/assets/javascripts/releases/components/app_edit.vue
+++ b/app/assets/javascripts/releases/components/app_edit.vue
@@ -9,6 +9,7 @@ import { BACK_URL_PARAM } from '~/releases/constants';
import { getParameterByName } from '~/lib/utils/common_utils';
import AssetLinksForm from './asset_links_form.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import MilestoneCombobox from '~/milestones/project_milestone_combobox.vue';
export default {
name: 'ReleaseEditApp',
@@ -18,6 +19,7 @@ export default {
GlButton,
MarkdownField,
AssetLinksForm,
+ MilestoneCombobox,
},
directives: {
autofocusonshow,
@@ -32,6 +34,10 @@ export default {
'markdownPreviewPath',
'releasesPagePath',
'updateReleaseApiDocsPath',
+ 'release',
+ 'newMilestonePath',
+ 'manageMilestonesPath',
+ 'projectId',
]),
...mapGetters('detail', ['isValid']),
showForm() {
@@ -82,6 +88,14 @@ export default {
this.updateReleaseNotes(notes);
},
},
+ releaseMilestones: {
+ get() {
+ return this.$store.state.detail.release.milestones;
+ },
+ set(milestones) {
+ this.updateReleaseMilestones(milestones);
+ },
+ },
cancelPath() {
return getParameterByName(BACK_URL_PARAM) || this.releasesPagePath;
},
@@ -91,6 +105,18 @@ export default {
isSaveChangesDisabled() {
return this.isUpdatingRelease || !this.isValid;
},
+ milestoneComboboxExtraLinks() {
+ return [
+ {
+ text: __('Create new'),
+ url: this.newMilestonePath,
+ },
+ {
+ text: __('Manage milestones'),
+ url: this.manageMilestonesPath,
+ },
+ ];
+ },
},
created() {
this.fetchRelease();
@@ -101,6 +127,7 @@ export default {
'updateRelease',
'updateReleaseTitle',
'updateReleaseNotes',
+ 'updateReleaseMilestones',
]),
},
};
@@ -137,6 +164,16 @@ export default {
class="form-control"
/>
</gl-form-group>
+ <gl-form-group class="w-50">
+ <label>{{ __('Milestones') }}</label>
+ <div class="d-flex flex-column col-md-6 col-sm-10 pl-0">
+ <milestone-combobox
+ v-model="releaseMilestones"
+ :project-id="projectId"
+ :extra-links="milestoneComboboxExtraLinks"
+ />
+ </div>
+ </gl-form-group>
<gl-form-group>
<label for="release-notes">{{ __('Release notes') }}</label>
<div class="bordered-box pr-3 pl-3">
@@ -158,8 +195,7 @@ export default {
:placeholder="__('Write your release notes or drag your files here…')"
@keydown.meta.enter="updateRelease()"
@keydown.ctrl.enter="updateRelease()"
- >
- </textarea>
+ ></textarea>
</markdown-field>
</div>
</gl-form-group>
@@ -174,12 +210,9 @@ export default {
type="submit"
:aria-label="__('Save changes')"
:disabled="isSaveChangesDisabled"
+ >{{ __('Save changes') }}</gl-button
>
- {{ __('Save changes') }}
- </gl-button>
- <gl-button :href="cancelPath" class="js-cancel-button">
- {{ __('Cancel') }}
- </gl-button>
+ <gl-button :href="cancelPath" class="js-cancel-button">{{ __('Cancel') }}</gl-button>
</div>
</form>
</div>
diff --git a/app/assets/javascripts/releases/components/app_index.vue b/app/assets/javascripts/releases/components/app_index.vue
index 215a376fc76..67085ecca2b 100644
--- a/app/assets/javascripts/releases/components/app_index.vue
+++ b/app/assets/javascripts/releases/components/app_index.vue
@@ -1,6 +1,6 @@
<script>
import { mapState, mapActions } from 'vuex';
-import { GlSkeletonLoading, GlEmptyState, GlLink } from '@gitlab/ui';
+import { GlSkeletonLoading, GlEmptyState, GlLink, GlButton } from '@gitlab/ui';
import {
getParameterByName,
historyPushState,
@@ -18,6 +18,7 @@ export default {
ReleaseBlock,
TablePagination,
GlLink,
+ GlButton,
},
props: {
projectId: {
@@ -69,14 +70,16 @@ export default {
</script>
<template>
<div class="flex flex-column mt-2">
- <gl-link
+ <gl-button
v-if="newReleasePath"
:href="newReleasePath"
:aria-describedby="shouldRenderEmptyState && 'releases-description'"
- class="btn btn-success align-self-end mb-2 js-new-release-btn"
+ category="primary"
+ variant="success"
+ class="align-self-end mb-2 js-new-release-btn"
>
{{ __('New release') }}
- </gl-link>
+ </gl-button>
<gl-skeleton-loading v-if="isLoading" class="js-loading" />
diff --git a/app/assets/javascripts/releases/components/release_block_header.vue b/app/assets/javascripts/releases/components/release_block_header.vue
index 1e703c247ae..ed49841757a 100644
--- a/app/assets/javascripts/releases/components/release_block_header.vue
+++ b/app/assets/javascripts/releases/components/release_block_header.vue
@@ -1,5 +1,5 @@
<script>
-import { GlTooltipDirective, GlLink, GlBadge } from '@gitlab/ui';
+import { GlTooltipDirective, GlLink, GlBadge, GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import { setUrlParams } from '~/lib/utils/url_utility';
@@ -10,6 +10,7 @@ export default {
GlLink,
GlBadge,
Icon,
+ GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -50,14 +51,16 @@ export default {
__('Upcoming Release')
}}</gl-badge>
</h2>
- <gl-link
+ <gl-button
v-if="editLink"
v-gl-tooltip
- class="btn btn-default append-right-10 js-edit-button ml-2"
+ category="primary"
+ variant="default"
+ class="append-right-10 js-edit-button ml-2 pb-2"
:title="__('Edit this release')"
:href="editLink"
>
<icon name="pencil" />
- </gl-link>
+ </gl-button>
</div>
</template>
diff --git a/app/assets/javascripts/releases/stores/modules/detail/actions.js b/app/assets/javascripts/releases/stores/modules/detail/actions.js
index 7b84c18242c..3bc427dfa16 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/actions.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/actions.js
@@ -18,7 +18,12 @@ export const fetchRelease = ({ dispatch, state }) => {
return api
.release(state.projectId, state.tagName)
- .then(({ data: release }) => {
+ .then(({ data }) => {
+ const release = {
+ ...data,
+ milestones: data.milestones || [],
+ };
+
dispatch('receiveReleaseSuccess', convertObjectPropsToCamelCase(release, { deep: true }));
})
.catch(error => {
@@ -28,6 +33,8 @@ export const fetchRelease = ({ dispatch, state }) => {
export const updateReleaseTitle = ({ commit }, title) => commit(types.UPDATE_RELEASE_TITLE, title);
export const updateReleaseNotes = ({ commit }, notes) => commit(types.UPDATE_RELEASE_NOTES, notes);
+export const updateReleaseMilestones = ({ commit }, milestones) =>
+ commit(types.UPDATE_RELEASE_MILESTONES, milestones);
export const requestUpdateRelease = ({ commit }) => commit(types.REQUEST_UPDATE_RELEASE);
export const receiveUpdateReleaseSuccess = ({ commit, state, rootState }) => {
@@ -45,12 +52,14 @@ export const updateRelease = ({ dispatch, state, getters }) => {
dispatch('requestUpdateRelease');
const { release } = state;
+ const milestones = release.milestones ? release.milestones.map(milestone => milestone.title) : [];
return (
api
.updateRelease(state.projectId, state.tagName, {
name: release.name,
description: release.description,
+ milestones,
})
/**
diff --git a/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js b/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js
index 04944b76e42..1d6356990ce 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js
@@ -4,6 +4,7 @@ export const RECEIVE_RELEASE_ERROR = 'RECEIVE_RELEASE_ERROR';
export const UPDATE_RELEASE_TITLE = 'UPDATE_RELEASE_TITLE';
export const UPDATE_RELEASE_NOTES = 'UPDATE_RELEASE_NOTES';
+export const UPDATE_RELEASE_MILESTONES = 'UPDATE_RELEASE_MILESTONES';
export const REQUEST_UPDATE_RELEASE = 'REQUEST_UPDATE_RELEASE';
export const RECEIVE_UPDATE_RELEASE_SUCCESS = 'RECEIVE_UPDATE_RELEASE_SUCCESS';
diff --git a/app/assets/javascripts/releases/stores/modules/detail/mutations.js b/app/assets/javascripts/releases/stores/modules/detail/mutations.js
index 3d97e3a75c2..5c29b402cba 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/mutations.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/mutations.js
@@ -28,6 +28,10 @@ export default {
state.release.description = notes;
},
+ [types.UPDATE_RELEASE_MILESTONES](state, milestones) {
+ state.release.milestones = milestones;
+ },
+
[types.REQUEST_UPDATE_RELEASE](state) {
state.isUpdatingRelease = true;
},
diff --git a/app/assets/javascripts/releases/stores/modules/detail/state.js b/app/assets/javascripts/releases/stores/modules/detail/state.js
index b513e1bed79..6d0d102c719 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/state.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/state.js
@@ -6,6 +6,8 @@ export default ({
markdownPreviewPath,
updateReleaseApiDocsPath,
releaseAssetsDocsPath,
+ manageMilestonesPath,
+ newMilestonePath,
}) => ({
projectId,
tagName,
@@ -14,6 +16,8 @@ export default ({
markdownPreviewPath,
updateReleaseApiDocsPath,
releaseAssetsDocsPath,
+ manageMilestonesPath,
+ newMilestonePath,
/** The Release object */
release: null,
diff --git a/app/assets/javascripts/terminal/terminal.js b/app/assets/javascripts/terminal/terminal.js
index f4e546e4d4e..cf9064aba57 100644
--- a/app/assets/javascripts/terminal/terminal.js
+++ b/app/assets/javascripts/terminal/terminal.js
@@ -13,14 +13,11 @@ Terminal.applyAddon(webLinks);
export default class GLTerminal {
constructor(element, options = {}) {
- this.options = Object.assign(
- {},
- {
- cursorBlink: true,
- screenKeys: true,
- },
- options,
- );
+ this.options = {
+ cursorBlink: true,
+ screenKeys: true,
+ ...options,
+ };
this.container = element;
this.onDispose = [];
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
index a95a5a50a8b..f38b66fdfdf 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
@@ -164,7 +164,11 @@ export default {
'js-dropdown-button',
'js-btn-cancel-create',
'js-sidebar-dropdown-toggle',
- ].some(className => target?.classList.contains(className));
+ ].some(
+ className =>
+ target?.classList.contains(className) ||
+ target?.parentElement.classList.contains(className),
+ );
const hadExceptionParent = ['.js-btn-back', '.js-labels-list'].some(
className => $(target).parents(className).length,
diff --git a/app/assets/stylesheets/components/milestone_combobox.scss b/app/assets/stylesheets/components/milestone_combobox.scss
new file mode 100644
index 00000000000..e0637088bbb
--- /dev/null
+++ b/app/assets/stylesheets/components/milestone_combobox.scss
@@ -0,0 +1,13 @@
+.selected-item::before {
+ content: '\f00c';
+ color: $green-500;
+ position: absolute;
+ left: 16px;
+ top: 16px;
+ transform: translateY(-50%);
+ font: 14px FontAwesome;
+}
+
+.dropdown-item-space {
+ padding: 8px 12px;
+}
diff --git a/app/controllers/projects/alert_management_controller.rb b/app/controllers/projects/alert_management_controller.rb
index 9be8a89fc02..9c919186c42 100644
--- a/app/controllers/projects/alert_management_controller.rb
+++ b/app/controllers/projects/alert_management_controller.rb
@@ -3,6 +3,7 @@
class Projects::AlertManagementController < Projects::ApplicationController
before_action :ensure_list_feature_enabled, only: :index
before_action :ensure_detail_feature_enabled, only: :details
+ before_action :authorize_read_alert_management_alert!
before_action do
push_frontend_feature_flag(:alert_list_status_filtering_enabled)
end
diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb
index 061cfb1fde1..a48dadc7fdb 100644
--- a/app/finders/alert_management/alerts_finder.rb
+++ b/app/finders/alert_management/alerts_finder.rb
@@ -31,7 +31,7 @@ module AlertManagement
end
def authorized?
- Ability.allowed?(current_user, :read_alert_management_alerts, project)
+ Ability.allowed?(current_user, :read_alert_management_alert, project)
end
end
end
diff --git a/app/graphql/mutations/alert_management/base.rb b/app/graphql/mutations/alert_management/base.rb
index eb0d2304ba3..37103c63a3d 100644
--- a/app/graphql/mutations/alert_management/base.rb
+++ b/app/graphql/mutations/alert_management/base.rb
@@ -18,7 +18,7 @@ module Mutations
null: true,
description: "The alert after mutation"
- authorize :update_alert_management_alerts
+ authorize :update_alert_management_alert
private
diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb
index 69fc2718f1e..c0283e6d476 100644
--- a/app/graphql/types/alert_management/alert_type.rb
+++ b/app/graphql/types/alert_management/alert_type.rb
@@ -6,7 +6,7 @@ module Types
graphql_name 'AlertManagementAlert'
description "Describes an alert from the project's Alert Management"
- authorize :read_alert_management_alerts
+ authorize :read_alert_management_alert
field :iid,
GraphQL::ID_TYPE,
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 3fb0e600465..2151d1a85d7 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -448,7 +448,7 @@ module ProjectsHelper
clusters: :read_cluster,
serverless: :read_cluster,
error_tracking: :read_sentry_issue,
- alert_management: :read_alert_management,
+ alert_management: :read_alert_management_alert,
labels: :read_label,
issues: :read_issue,
project_members: :read_project_member,
diff --git a/app/helpers/releases_helper.rb b/app/helpers/releases_helper.rb
index af51427dc91..1238567a4ed 100644
--- a/app/helpers/releases_helper.rb
+++ b/app/helpers/releases_helper.rb
@@ -30,7 +30,9 @@ module ReleasesHelper
markdown_docs_path: help_page_path('user/markdown'),
releases_page_path: project_releases_path(@project, anchor: @release.tag),
update_release_api_docs_path: help_page_path('api/releases/index.md', anchor: 'update-a-release'),
- release_assets_docs_path: help_page(anchor: 'release-assets')
+ release_assets_docs_path: help_page(anchor: 'release-assets'),
+ manage_milestones_path: project_milestones_path(@project),
+ new_milestone_path: new_project_milestone_url(@project)
}
end
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 6aa3d791a0f..a8105ae6f7c 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -236,11 +236,8 @@ class ProjectPolicy < BasePolicy
enable :read_merge_request
enable :read_sentry_issue
enable :update_sentry_issue
- enable :read_alert_management
enable :read_prometheus
enable :read_metrics_dashboard_annotation
- enable :read_alert_management_alerts
- enable :update_alert_management_alerts
enable :metrics_dashboard
end
@@ -306,6 +303,8 @@ class ProjectPolicy < BasePolicy
enable :create_metrics_dashboard_annotation
enable :delete_metrics_dashboard_annotation
enable :update_metrics_dashboard_annotation
+ enable :read_alert_management_alert
+ enable :update_alert_management_alert
enable :create_design
enable :destroy_design
end
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index c96599f9958..bf21eba28f7 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -6,6 +6,9 @@ class SearchService
SEARCH_TERM_LIMIT = 64
SEARCH_CHAR_LIMIT = 4096
+ DEFAULT_PER_PAGE = Gitlab::SearchResults::DEFAULT_PER_PAGE
+ MAX_PER_PAGE = 200
+
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
@@ -60,11 +63,19 @@ class SearchService
end
def search_objects
- @search_objects ||= redact_unauthorized_results(search_results.objects(scope, params[:page]))
+ @search_objects ||= redact_unauthorized_results(search_results.objects(scope, page: params[:page], per_page: per_page))
end
private
+ def per_page
+ per_page_param = params[:per_page].to_i
+
+ return DEFAULT_PER_PAGE unless per_page_param.positive?
+
+ [MAX_PER_PAGE, per_page_param].min
+ end
+
def visible_result?(object)
return true unless object.respond_to?(:to_ability_name) && DeclarativePolicy.has_policy?(object)
@@ -75,13 +86,13 @@ class SearchService
results = results_collection.to_a
permitted_results = results.select { |object| visible_result?(object) }
- filtered_results = (results - permitted_results).each_with_object({}) do |object, memo|
+ redacted_results = (results - permitted_results).each_with_object({}) do |object, memo|
memo[object.id] = { ability: :"read_#{object.to_ability_name}", id: object.id, class_name: object.class.name }
end
- log_redacted_search_results(filtered_results.values) if filtered_results.any?
+ log_redacted_search_results(redacted_results.values) if redacted_results.any?
- return results_collection.id_not_in(filtered_results.keys) if results_collection.is_a?(ActiveRecord::Relation)
+ return results_collection.id_not_in(redacted_results.keys) if results_collection.is_a?(ActiveRecord::Relation)
Kaminari.paginate_array(
permitted_results,
diff --git a/changelogs/unreleased/207267-expiration-policy-copy.yml b/changelogs/unreleased/207267-expiration-policy-copy.yml
new file mode 100644
index 00000000000..9ddff8a76fb
--- /dev/null
+++ b/changelogs/unreleased/207267-expiration-policy-copy.yml
@@ -0,0 +1,5 @@
+---
+title: Update the example regex in the image expiration policy UI
+merge_request: 31104
+author:
+type: changed
diff --git a/changelogs/unreleased/207324-search-api-scoped-to-blobs-does-not-honor-per_page.yml b/changelogs/unreleased/207324-search-api-scoped-to-blobs-does-not-honor-per_page.yml
new file mode 100644
index 00000000000..e1e24bfdd7d
--- /dev/null
+++ b/changelogs/unreleased/207324-search-api-scoped-to-blobs-does-not-honor-per_page.yml
@@ -0,0 +1,5 @@
+---
+title: Honor per_page in Search API
+merge_request: 29197
+author:
+type: fixed
diff --git a/changelogs/unreleased/216477-gllink-updates.yml b/changelogs/unreleased/216477-gllink-updates.yml
new file mode 100644
index 00000000000..3a786fa479e
--- /dev/null
+++ b/changelogs/unreleased/216477-gllink-updates.yml
@@ -0,0 +1,5 @@
+---
+title: Update style of buttons on the Releases page
+merge_request: 31129
+author: Özgür Adem Işıklı @iozguradem
+type: changed
diff --git a/changelogs/unreleased/39467-allow-a-release-s-associated-milestones-to-be-edited-through-the-ed.yml b/changelogs/unreleased/39467-allow-a-release-s-associated-milestones-to-be-edited-through-the-ed.yml
new file mode 100644
index 00000000000..f182418b0e1
--- /dev/null
+++ b/changelogs/unreleased/39467-allow-a-release-s-associated-milestones-to-be-edited-through-the-ed.yml
@@ -0,0 +1,5 @@
+---
+title: 'Allow to assign milestones to a release on the "Edit Release page"'
+merge_request: 28583
+author:
+type: added
diff --git a/changelogs/unreleased/rw-exclude-skipped-tests-from-success.yml b/changelogs/unreleased/rw-exclude-skipped-tests-from-success.yml
new file mode 100644
index 00000000000..c5d348f104e
--- /dev/null
+++ b/changelogs/unreleased/rw-exclude-skipped-tests-from-success.yml
@@ -0,0 +1,4 @@
+---
+title: Changed test success calculation to exclude skipped tests
+merge_request: 31154
+type: changed
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 9019cf0a630..525348464db 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Audit Events **(STARTER)**
GitLab offers a way to view the changes made within the GitLab server for owners and administrators on a [paid plan](https://about.gitlab.com/pricing/).
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index fd9556278a1..1af3d93edb6 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Log system
GitLab has an advanced log system where everything is logged, so you
diff --git a/doc/administration/monitoring/prometheus/gitlab_exporter.md b/doc/administration/monitoring/prometheus/gitlab_exporter.md
index 9c5c67e7f67..3effca4a2bf 100644
--- a/doc/administration/monitoring/prometheus/gitlab_exporter.md
+++ b/doc/administration/monitoring/prometheus/gitlab_exporter.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# GitLab exporter
>- Available since [Omnibus GitLab 8.17](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/1132).
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 9391971fd3e..62f9ebd0168 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# GitLab Prometheus metrics
>**Note:**
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 8749e79f3fe..1fd6dfca802 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Monitoring GitLab with Prometheus
> **Notes:**
diff --git a/doc/administration/monitoring/prometheus/node_exporter.md b/doc/administration/monitoring/prometheus/node_exporter.md
index d75b04f1ccd..357303ee4e1 100644
--- a/doc/administration/monitoring/prometheus/node_exporter.md
+++ b/doc/administration/monitoring/prometheus/node_exporter.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Node exporter
>**Note:**
diff --git a/doc/administration/monitoring/prometheus/pgbouncer_exporter.md b/doc/administration/monitoring/prometheus/pgbouncer_exporter.md
index ba8e464efcb..92ba2d9bb52 100644
--- a/doc/administration/monitoring/prometheus/pgbouncer_exporter.md
+++ b/doc/administration/monitoring/prometheus/pgbouncer_exporter.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# PgBouncer exporter
>**Note:**
diff --git a/doc/administration/monitoring/prometheus/postgres_exporter.md b/doc/administration/monitoring/prometheus/postgres_exporter.md
index 853e3837184..77ca502b21d 100644
--- a/doc/administration/monitoring/prometheus/postgres_exporter.md
+++ b/doc/administration/monitoring/prometheus/postgres_exporter.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# PostgreSQL Server Exporter
>**Note:**
diff --git a/doc/administration/monitoring/prometheus/redis_exporter.md b/doc/administration/monitoring/prometheus/redis_exporter.md
index 76f4add0c1b..bef87400f5a 100644
--- a/doc/administration/monitoring/prometheus/redis_exporter.md
+++ b/doc/administration/monitoring/prometheus/redis_exporter.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Redis exporter
>**Note:**
diff --git a/doc/administration/monitoring/prometheus/registry_exporter.md b/doc/administration/monitoring/prometheus/registry_exporter.md
index 2e2440389ed..3d28b26b685 100644
--- a/doc/administration/monitoring/prometheus/registry_exporter.md
+++ b/doc/administration/monitoring/prometheus/registry_exporter.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Registry exporter
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/2884) in GitLab 11.9.
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index 79d4a3328b1..9e52896d013 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -50,7 +50,8 @@ For different cloud vendors, attempt to select options that best match the provi
## Up to 1,000 users
-From 1 to 1,000 users, a [single-node setup with frequent backups](#automated-backups-core-only) is adequate.
+> - **Supported users (approximate):** 1,000
+> - **High Availability:** False
| Users | Configuration([8](#footnotes)) | GCP type | AWS type([9](#footnotes)) |
|-------|--------------------------------|---------------|---------------------------|
@@ -58,9 +59,20 @@ From 1 to 1,000 users, a [single-node setup with frequent backups](#automated-ba
| 500 | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge |
| 1000 | 8 vCPU, 30GB Memory | n1-standard-8 | m5.2xlarge |
-This solution is appropriate for many teams that have a single server at their disposal. With automatic backup of the GitLab repositories, configuration, and the database, this can be an optimal solution if you don't have strict availability requirements.
+For situations where you need to serve up to 1,000 users, a single-node
+solution with [frequent backups](#automated-backups-core-only) is appropriate
+for many organizations. With automatic backup of the GitLab repositories,
+configuration, and the database, if you don't have strict availability
+requirements, this is the ideal solution.
+
+For this default reference architecture, use the standard
+[installation instructions](../../install/README.md) to install GitLab.
-You can also optionally configure GitLab to use an [external PostgreSQL service](../external_database.md) or an [external object storage service](../high_availability/object_storage.md) for added performance and reliability at a relatively low complexity cost.
+NOTE: **Note:**
+You can also optionally configure GitLab to use an
+[external PostgreSQL service](../external_database.md) or an
+[external object storage service](../high_availability/object_storage.md) for
+added performance and reliability at a reduced complexity cost.
## Up to 2,000 users
@@ -70,12 +82,32 @@ You can also optionally configure GitLab to use an [external PostgreSQL service]
| Service | Nodes | Configuration ([8](#footnotes)) | GCP type | AWS type ([9](#footnotes)) |
|--------------------------------------------------------------|-------|---------------------------------|---------------|----------------------------|
-| GitLab Rails, Sidekiq, Consul ([1](#footnotes)) | 2 | 8 vCPU, 7.2GB Memory | n1-highcpu-8 | c5.2xlarge |
-| PostgreSQL | 1 | 2 vCPU, 7.5GB Memory | n1-standard-2 | m5.large |
-| Gitaly ([2](#footnotes)) ([5](#footnotes)) ([7](#footnotes)) | X | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge |
-| Cloud Object Storage ([4](#footnotes)) | - | - | - | - |
-| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| External load balancing node ([6](#footnotes)) | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
+| Object Storage ([4](#footnotes)) | - | - | - | - |
+| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
+| PostgreSQL | 1 | 2 vCPU, 7.5GB Memory | n1-standard-2 | m5.large |
+| Redis ([3](#footnotes)) | 1 | 1 vCPU, 3.75GB Memory | n1-standard-1 | m5.large |
+| Gitaly ([5](#footnotes)) ([7](#footnotes)) | X ([2](#footnotes)) | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge |
+| GitLab Rails ([1](#footnotes)) | 2 | 8 vCPU, 7.2GB Memory | n1-highcpu-8 | c5.2xlarge |
+| Monitoring node | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
+
+To set up GitLab for up to 2000 users:
+
+1. [Configure the external load balancing node](../high_availability/load_balancer.md)
+ that will handle the load balancing of the two GitLab application services nodes.
+1. [Configure the Object Storage](../object_storage.md) ([4](#footnotes)) used for shared data objects.
+1. (Optional) [Configure NFS](../high_availability/nfs.md) to have
+ shared disk storage service as an alternative to Gitaly and/or
+ [Object Storage](../object_storage.md) (although not recommended).
+ NFS is required for GitLab Pages, you can skip this step if you're not using that feature.
+1. [Configure PostgreSQL](../high_availability/load_balancer.md), the database for GitLab.
+1. [Configure Redis](../high_availability/redis.md).
+1. [Configure Gitaly](../gitaly/index.md#running-gitaly-on-its-own-server),
+ which is used to provide access to the Git repositories.
+1. [Configure the main GitLab Rails application](../high_availability/gitlab.md)
+ to run Puma/Unicorn, Workhorse, GitLab Shell, and to serve all
+ frontend requests (UI, API, Git over HTTP/SSH).
+1. [Configure Prometheus](../high_availability/monitoring_node.md) to monitor your GitLab environment.
## Up to 3,000 users
@@ -83,9 +115,7 @@ NOTE: **Note:** The 3,000-user reference architecture documented below is
designed to help your organization achieve a highly-available GitLab deployment.
If you do not have the expertise or need to maintain a highly-available
environment, you can have a simpler and less costly-to-operate environment by
-deploying two or more GitLab Rails servers, external load balancing, an NFS
-server, a PostgreSQL server and a Redis server. A reference architecture with
-this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/quality/performance/-/issues/223).
+following the [2,000-user reference architecture](#up-to-2000-users).
> - **Supported users (approximate):** 3,000
> - **High Availability:** True
@@ -100,7 +130,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
| Redis ([3](#footnotes)) | 3 | 2 vCPU, 7.5GB Memory | n1-standard-2 | m5.large |
| Consul + Sentinel ([3](#footnotes)) | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| Sidekiq | 4 | 2 vCPU, 7.5GB Memory | n1-standard-2 | m5.large |
-| Cloud Object Storage ([4](#footnotes)) | - | - | - | - |
+| Object Storage ([4](#footnotes)) | - | - | - | - |
| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| Monitoring node | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| External load balancing node ([6](#footnotes)) | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
@@ -121,7 +151,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
| Redis ([3](#footnotes)) | 3 | 2 vCPU, 7.5GB Memory | n1-standard-2 | m5.large |
| Consul + Sentinel ([3](#footnotes)) | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| Sidekiq | 4 | 2 vCPU, 7.5GB Memory | n1-standard-2 | m5.large |
-| Cloud Object Storage ([4](#footnotes)) | - | - | - | - |
+| Object Storage ([4](#footnotes)) | - | - | - | - |
| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| Monitoring node | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| External load balancing node ([6](#footnotes)) | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
@@ -145,7 +175,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
| Redis Sentinel ([3](#footnotes)) - Queues / Shared State | 3 | 1 vCPU, 1.7GB Memory | g1-small | t2.small |
| Consul | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| Sidekiq | 4 | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge |
-| Cloud Object Storage ([4](#footnotes)) | - | - | - | - |
+| Object Storage ([4](#footnotes)) | - | - | - | - |
| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| Monitoring node | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| External load balancing node ([6](#footnotes)) | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
@@ -169,7 +199,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
| Redis Sentinel ([3](#footnotes)) - Queues / Shared State | 3 | 1 vCPU, 1.7GB Memory | g1-small | t2.small |
| Consul | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| Sidekiq | 4 | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge |
-| Cloud Object Storage ([4](#footnotes)) | - | - | - | - |
+| Object Storage ([4](#footnotes)) | - | - | - | - |
| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| Monitoring node | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| External load balancing node ([6](#footnotes)) | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
@@ -194,7 +224,7 @@ this alternative in mind is [being worked on](https://gitlab.com/gitlab-org/qual
| Consul | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| Sidekiq | 4 | 4 vCPU, 15GB Memory | n1-standard-4 | m5.xlarge |
| NFS Server ([5](#footnotes)) ([7](#footnotes)) | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
-| Cloud Object Storage ([4](#footnotes)) | - | - | - | - |
+| Object Storage ([4](#footnotes)) | - | - | - | - |
| Monitoring node | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 | c5.xlarge |
| External load balancing node ([6](#footnotes)) | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 | c5.large |
| Internal load balancing node ([6](#footnotes)) | 1 | 8 vCPU, 7.2GB Memory | n1-highcpu-8 | c5.2xlarge |
@@ -286,7 +316,7 @@ column.
| Component | Description | Configuration instructions | Bundled with Omnibus GitLab |
|-----------|-------------|----------------------------|
| Load balancer(s) ([6](#footnotes)) | Handles load balancing, typically when you have multiple GitLab application services nodes | [Load balancer configuration](../high_availability/load_balancer.md) ([6](#footnotes)) | No |
-| Object storage service ([4](#footnotes)) | Recommended store for shared data objects | [Cloud Object Storage configuration](../object_storage.md) | No |
+| Object storage service ([4](#footnotes)) | Recommended store for shared data objects | [Object Storage configuration](../object_storage.md) | No |
| NFS ([5](#footnotes)) ([7](#footnotes)) | Shared disk storage service. Can be used as an alternative for Gitaly or Object Storage. Required for GitLab Pages | [NFS configuration](../high_availability/nfs.md) | No |
| [Consul](../../development/architecture.md#consul) ([3](#footnotes)) | Service discovery and health checks/failover | [Consul HA configuration](../high_availability/consul.md) **(PREMIUM ONLY)** | Yes |
| [PostgreSQL](../../development/architecture.md#postgresql) | Database | [PostgreSQL configuration](https://docs.gitlab.com/omnibus/settings/database.html) | Yes |
@@ -316,14 +346,15 @@ column.
with a review of expected data size and spread based on the recommendations above.
1. Recommended Redis setup differs depending on the size of the architecture.
- For smaller architectures (less than 5,000 users), we suggest one Redis cluster for all
+ For smaller architectures (less than 3,000 users) a single instance should suffice.
+ For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
[Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
-1. For data objects such as LFS, Uploads, Artifacts, etc. We recommend a [Cloud Object Storage service](../object_storage.md)
+1. For data objects such as LFS, Uploads, Artifacts, etc. We recommend an [Object Storage service](../object_storage.md)
over NFS where possible, due to better performance and availability.
1. NFS can be used as an alternative for both repository data (replacing Gitaly) and
diff --git a/doc/api/projects.md b/doc/api/projects.md
index aa5e9ef6e43..beb69e1aeee 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -798,7 +798,9 @@ GET /projects/:id
"enabled": false,
"keep_n": null,
"older_than": null,
- "name_regex": null,
+ "name_regex": null, // to be deprecated in GitLab 13.0 in favor of `name_regex_delete`
+ "name_regex_delete": null,
+ "name_regex_keep": null,
"next_run_at": "2020-01-07T21:42:58.658Z"
},
"created_at": "2013-09-30T13:46:02Z",
@@ -1033,7 +1035,7 @@ POST /projects
| `emails_disabled` | boolean | no | Disable email notifications |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
-| `container_expiration_policy_attributes` | hash | no | Update the container expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `enabled` (boolean) |
+| `container_expiration_policy_attributes` | hash | no | Update the image expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean) |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
@@ -1170,7 +1172,7 @@ PUT /projects/:id
| `emails_disabled` | boolean | no | Disable email notifications |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
-| `container_expiration_policy_attributes` | hash | no | Update the container expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `enabled` (boolean) |
+| `container_expiration_policy_attributes` | hash | no | Update the image expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean) |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 07bf6bf5fbf..cd9ebc71ba7 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -468,13 +468,13 @@ When a merge request author has been blocked for longer than
the `Review-response` SLO, they are free to remind the reviewer through Slack or assign
another reviewer.
-#### Customer critical merge requests
+### Customer critical merge requests
A merge request may benefit from being considered a customer critical priority because there is a significant benefit to the business in doing so.
Properties of customer critical merge requests:
-- The [Senior Director of Development](https://about.gitlab.com/job-families/engineering/engineering-management/#senior-director-engineering) [@clefelhocz1](https://gitlab.com/clefelhocz1) is the DRI for deciding if a merge request will be customer critical.
+- The [Senior Director of Development](https://about.gitlab.com/job-families/engineering/engineering-management/#senior-director-engineering) ([@clefelhocz1](https://gitlab.com/clefelhocz1)) is the DRI for deciding if a merge request will be customer critical.
- The DRI will assign the `customer-critical-merge-request` label to the merge request.
- It is required that the reviewer(s) and maintainer(s) involved with a customer critical merge request are engaged as soon as this decision is made.
- It is required to prioritize work for those involved on a customer critical merge request so that they have the time available necessary to focus on it.
diff --git a/doc/user/incident_management/index.md b/doc/user/incident_management/index.md
index 8b898a7b516..d8294d35d2a 100644
--- a/doc/user/incident_management/index.md
+++ b/doc/user/incident_management/index.md
@@ -1,5 +1,8 @@
---
description: "GitLab - Incident Management. GitLab offers solutions for handling incidents in your applications and services"
+stage: Monitor
+group: Health
+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
---
# Incident Management
diff --git a/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png b/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png
index a38014a2b9b..81e516e833b 100644
--- a/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png
+++ b/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png
Binary files differ
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index 5ad476c9510..7603e99e578 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -505,6 +505,7 @@ then goes through a process of excluding tags from it until only the ones to be
1. Orders the remaining tags by `created_date`.
1. Excludes from the list the N tags based on the `keep_n` value (Number of tags to retain).
1. Excludes from the list the tags older than the `older_than` value (Expiration interval).
+1. Excludes from the list any tags matching the `name_regex_keep` value (Images to preserve).
1. Finally, the remaining tags in the list are deleted from the Container Registry.
### Managing project expiration policy through the UI
@@ -520,6 +521,7 @@ The UI allows you to configure the following:
- **Expiration schedule:** how often the cron job checking the tags should run.
- **Number of tags to retain:** how many tags to _always_ keep for each image.
- **Docker tags with names matching this regex pattern will expire:** the regex used to determine what tags should be expired. To qualify all tags for expiration, use the default value of `.*`.
+- **Docker tags with names matching this regex pattern will be preserved:** the regex used to determine what tags should be preserved. To preserve all tags, use the default value of `.*`.
### Managing project expiration policy through the API
@@ -527,16 +529,10 @@ You can set, update, and disable the expiration policies using the GitLab API.
Examples:
-- Select all tags, keep at least 1 tag per image, expire any tag older than 14 days, run once a month, and the policy is enabled:
+- Select all tags, keep at least 1 tag per image, expire any tag older than 14 days, run once a month, preserve any images with the name `master` and the policy is enabled:
```shell
- curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":".*"}}' 'https://gitlab.example.com/api/v4/projects/2'
- ```
-
-- Select only tags with a name that contains `stable`, keep at least 50 tag per image, expire any tag older than 7 days, run every day, and the policy is enabled:
-
- ```shell
- curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" --data-binary '{"container_expiration_policy_attributes":{"cadence":"1day","enabled":true,"keep_n":50"older_than":"7d","name_regex":"*stable"}}' 'https://gitlab.example.com/api/v4/projects/2'
+ curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":"","name_regex_delete":".*","name_regex_keep":".*-master"}}' 'https://gitlab.example.com/api/v4/projects/2'
```
See the API documentation for further details: [Edit project](../../../api/projects.md#edit-project).
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index bb8cefd755d..51fe3fbf168 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Kubernetes clusters
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/35954) in GitLab 10.1 for projects.
diff --git a/doc/user/project/integrations/generic_alerts.md b/doc/user/project/integrations/generic_alerts.md
index 1a000fd1c44..f89922edb7a 100644
--- a/doc/user/project/integrations/generic_alerts.md
+++ b/doc/user/project/integrations/generic_alerts.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: Health
+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
+---
+
# Generic alerts integration
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/13203) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.4.
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 4a4c536c80f..8fca670eb41 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Prometheus integration
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8935) in GitLab 9.0.
diff --git a/doc/user/project/integrations/prometheus_library/cloudwatch.md b/doc/user/project/integrations/prometheus_library/cloudwatch.md
index 143130aebea..911493cdae9 100644
--- a/doc/user/project/integrations/prometheus_library/cloudwatch.md
+++ b/doc/user/project/integrations/prometheus_library/cloudwatch.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Monitoring AWS Resources
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12621) in GitLab 9.4
diff --git a/doc/user/project/integrations/prometheus_library/haproxy.md b/doc/user/project/integrations/prometheus_library/haproxy.md
index fa3590af8cf..712805b75f2 100644
--- a/doc/user/project/integrations/prometheus_library/haproxy.md
+++ b/doc/user/project/integrations/prometheus_library/haproxy.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Monitoring HAProxy
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12621) in GitLab 9.4
diff --git a/doc/user/project/integrations/prometheus_library/index.md b/doc/user/project/integrations/prometheus_library/index.md
index c2b3676b23f..6f2c2477eee 100644
--- a/doc/user/project/integrations/prometheus_library/index.md
+++ b/doc/user/project/integrations/prometheus_library/index.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Prometheus Metrics library
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8935) in GitLab 9.0.
diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md
index ca1555c793b..29efe08e53d 100644
--- a/doc/user/project/integrations/prometheus_library/kubernetes.md
+++ b/doc/user/project/integrations/prometheus_library/kubernetes.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Monitoring Kubernetes
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8935) in GitLab 9.0.
diff --git a/doc/user/project/integrations/prometheus_library/nginx.md b/doc/user/project/integrations/prometheus_library/nginx.md
index d5f078f3ddf..eda6f64ccac 100644
--- a/doc/user/project/integrations/prometheus_library/nginx.md
+++ b/doc/user/project/integrations/prometheus_library/nginx.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Monitoring NGINX
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/12621) in GitLab 9.4
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
index 62f8c08e298..b2bc217e8bf 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Monitoring NGINX Ingress Controller
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22133) in GitLab 11.7.
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
index af3b725deb6..6ba0a7610f6 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Monitoring NGINX Ingress Controller with VTS metrics
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13438) in GitLab 9.5.
diff --git a/doc/user/project/integrations/prometheus_units.md b/doc/user/project/integrations/prometheus_units.md
index d649a14ecf2..691d20e5de2 100644
--- a/doc/user/project/integrations/prometheus_units.md
+++ b/doc/user/project/integrations/prometheus_units.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Unit formats reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/201999) in GitLab 12.9.
diff --git a/doc/user/project/operations/error_tracking.md b/doc/user/project/operations/error_tracking.md
index fde4f7e64b3..23a50fd7766 100644
--- a/doc/user/project/operations/error_tracking.md
+++ b/doc/user/project/operations/error_tracking.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: Health
+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
+---
+
# Error Tracking
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/169) in GitLab 11.8.
diff --git a/doc/user/project/operations/tracing.md b/doc/user/project/operations/tracing.md
index 8282a980ced..07f60c37f1b 100644
--- a/doc/user/project/operations/tracing.md
+++ b/doc/user/project/operations/tracing.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: APM
+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
+---
+
# Tracing **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7903) in GitLab Ultimate 11.5.
diff --git a/doc/user/project/status_page/index.md b/doc/user/project/status_page/index.md
index 02570ff912c..1acf978c81d 100644
--- a/doc/user/project/status_page/index.md
+++ b/doc/user/project/status_page/index.md
@@ -1,3 +1,9 @@
+---
+stage: Monitor
+group: Health
+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
+---
+
# GitLab Status Page
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2479) in GitLab 12.10.
diff --git a/lib/gitlab/group_search_results.rb b/lib/gitlab/group_search_results.rb
index 8597903ad00..eb4361cdc53 100644
--- a/lib/gitlab/group_search_results.rb
+++ b/lib/gitlab/group_search_results.rb
@@ -4,8 +4,8 @@ module Gitlab
class GroupSearchResults < SearchResults
attr_reader :group
- def initialize(current_user, limit_projects, group, query, default_project_filter: false, per_page: 20)
- super(current_user, limit_projects, query, default_project_filter: default_project_filter, per_page: per_page)
+ def initialize(current_user, limit_projects, group, query, default_project_filter: false)
+ super(current_user, limit_projects, query, default_project_filter: default_project_filter)
@group = group
end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index eb7ca80dd60..123dcf79065 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -2,30 +2,29 @@
module Gitlab
class ProjectSearchResults < SearchResults
- attr_reader :project, :repository_ref, :per_page
+ attr_reader :project, :repository_ref
- def initialize(current_user, project, query, repository_ref = nil, per_page: 20)
+ def initialize(current_user, project, query, repository_ref = nil)
@current_user = current_user
@project = project
@repository_ref = repository_ref.presence
@query = query
- @per_page = per_page
end
- def objects(scope, page = nil)
+ def objects(scope, page: nil, per_page: DEFAULT_PER_PAGE)
case scope
when 'notes'
notes.page(page).per(per_page)
when 'blobs'
- paginated_blobs(blobs(page), page)
+ paginated_blobs(blobs(limit: limit_up_to_page(page, per_page)), page, per_page)
when 'wiki_blobs'
- paginated_blobs(wiki_blobs, page)
+ paginated_blobs(wiki_blobs(limit: limit_up_to_page(page, per_page)), page, per_page)
when 'commits'
Kaminari.paginate_array(commits).page(page).per(per_page)
when 'users'
users.page(page).per(per_page)
else
- super(scope, page, false)
+ super(scope, page: page, per_page: per_page, without_count: false)
end
end
@@ -49,7 +48,7 @@ module Gitlab
end
def limited_blobs_count
- @limited_blobs_count ||= blobs.count
+ @limited_blobs_count ||= blobs(limit: count_limit).count
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -69,7 +68,7 @@ module Gitlab
# rubocop: enable CodeReuse/ActiveRecord
def wiki_blobs_count
- @wiki_blobs_count ||= wiki_blobs.count
+ @wiki_blobs_count ||= wiki_blobs(limit: count_limit).count
end
def commits_count
@@ -87,7 +86,7 @@ module Gitlab
private
- def paginated_blobs(blobs, page)
+ def paginated_blobs(blobs, page, per_page)
results = Kaminari.paginate_array(blobs).page(page).per(per_page)
Gitlab::Search::FoundBlob.preload_blobs(results)
@@ -95,19 +94,19 @@ module Gitlab
results
end
- def limit_up_to_page(page)
+ def limit_up_to_page(page, per_page)
current_page = page&.to_i || 1
offset = per_page * (current_page - 1)
count_limit + offset
end
- def blobs(page = 1)
+ def blobs(limit: count_limit)
return [] unless Ability.allowed?(@current_user, :download_code, @project)
- @blobs ||= Gitlab::FileFinder.new(project, repository_project_ref).find(query, content_match_cutoff: limit_up_to_page(page))
+ @blobs ||= Gitlab::FileFinder.new(project, repository_project_ref).find(query, content_match_cutoff: limit)
end
- def wiki_blobs
+ def wiki_blobs(limit: count_limit)
return [] unless Ability.allowed?(@current_user, :read_wiki, @project)
@wiki_blobs ||= begin
@@ -115,7 +114,7 @@ module Gitlab
if project.wiki.empty?
[]
else
- Gitlab::WikiFileFinder.new(project, repository_wiki_ref).find(query)
+ Gitlab::WikiFileFinder.new(project, repository_wiki_ref).find(query, content_match_cutoff: limit)
end
else
[]
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 0473fa89a0d..c35ee62163a 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -4,8 +4,10 @@ module Gitlab
class SearchResults
COUNT_LIMIT = 100
COUNT_LIMIT_MESSAGE = "#{COUNT_LIMIT - 1}+"
+ DEFAULT_PAGE = 1
+ DEFAULT_PER_PAGE = 20
- attr_reader :current_user, :query, :per_page
+ attr_reader :current_user, :query
# Limit search results by passed projects
# It allows us to search only for projects user has access to
@@ -17,15 +19,14 @@ module Gitlab
# query
attr_reader :default_project_filter
- def initialize(current_user, limit_projects, query, default_project_filter: false, per_page: 20)
+ def initialize(current_user, limit_projects, query, default_project_filter: false)
@current_user = current_user
@limit_projects = limit_projects || Project.all
@query = query
@default_project_filter = default_project_filter
- @per_page = per_page
end
- def objects(scope, page = nil, without_count = true)
+ def objects(scope, page: nil, per_page: DEFAULT_PER_PAGE, without_count: true)
collection = case scope
when 'projects'
projects
@@ -39,7 +40,9 @@ module Gitlab
users
else
Kaminari.paginate_array([])
- end.page(page).per(per_page)
+ end
+
+ collection = collection.page(page).per(per_page)
without_count ? collection.without_count : collection
end
diff --git a/lib/gitlab/sidekiq_logging/json_formatter.rb b/lib/gitlab/sidekiq_logging/json_formatter.rb
index 45c6842c59b..64782e1e1d1 100644
--- a/lib/gitlab/sidekiq_logging/json_formatter.rb
+++ b/lib/gitlab/sidekiq_logging/json_formatter.rb
@@ -19,6 +19,7 @@ module Gitlab
output[:message] = data
when Hash
convert_to_iso8601!(data)
+ convert_retry_to_integer!(data)
stringify_args!(data)
output.merge!(data)
end
@@ -41,6 +42,20 @@ module Gitlab
Time.at(timestamp).utc.iso8601(3)
end
+ def convert_retry_to_integer!(payload)
+ payload['retry'] =
+ case payload['retry']
+ when Integer
+ payload['retry']
+ when false, nil
+ 0
+ when true
+ Sidekiq::JobRetry::DEFAULT_MAX_RETRY_ATTEMPTS
+ else
+ -1
+ end
+ end
+
def stringify_args!(payload)
payload['args'] = Gitlab::Utils::LogLimitedArray.log_limited_array(payload['args'].map(&:to_s)) if payload['args']
end
diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb
index 7fcae04c14f..9911f9e62a6 100644
--- a/lib/gitlab/snippet_search_results.rb
+++ b/lib/gitlab/snippet_search_results.rb
@@ -11,8 +11,8 @@ module Gitlab
@query = query
end
- def objects(scope, page = nil)
- paginated_objects(snippet_titles, page)
+ def objects(scope, page: nil, per_page: DEFAULT_PER_PAGE)
+ paginated_objects(snippet_titles, page, per_page)
end
def formatted_count(scope)
@@ -38,7 +38,7 @@ module Gitlab
snippets.search(query)
end
- def paginated_objects(relation, page)
+ def paginated_objects(relation, page, per_page)
relation.page(page).per(per_page)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 11bebabf468..7cec0bbee5b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -321,6 +321,9 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
+msgid "%{firstMilestoneName} + %{numberOfOtherMilestones} more"
+msgstr ""
+
msgid "%{global_id} is not a valid id for %{expected_type}."
msgstr ""
@@ -2141,6 +2144,9 @@ msgstr ""
msgid "An error occurred while loading merge requests."
msgstr ""
+msgid "An error occurred while loading milestones"
+msgstr ""
+
msgid "An error occurred while loading terraform report"
msgstr ""
@@ -2216,6 +2222,9 @@ msgstr ""
msgid "An error occurred while saving the template. Please check if the template exists."
msgstr ""
+msgid "An error occurred while searching for milestones"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
@@ -5710,7 +5719,7 @@ msgstr ""
msgid "ContainerRegistry|Quick Start"
msgstr ""
-msgid "ContainerRegistry|Regular expressions such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported"
+msgid "ContainerRegistry|Regular expressions such as %{codeStart}.*-master%{codeEnd} or %{codeStart}release-.*%{codeEnd} are supported"
msgstr ""
msgid "ContainerRegistry|Regular expressions such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}"
@@ -6213,6 +6222,9 @@ msgstr ""
msgid "Create milestone"
msgstr ""
+msgid "Create new"
+msgstr ""
+
msgid "Create new board"
msgstr ""
@@ -6889,11 +6901,6 @@ msgid_plural "Dependencies|%d additional vulnerabilities not shown"
msgstr[0] ""
msgstr[1] ""
-msgid "Dependencies|%d vulnerability"
-msgid_plural "Dependencies|%d vulnerabilities"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Dependencies|%d vulnerability detected"
msgid_plural "Dependencies|%d vulnerabilities detected"
msgstr[0] ""
@@ -6929,12 +6936,6 @@ msgstr ""
msgid "Dependencies|Packager"
msgstr ""
-msgid "Dependencies|Safe"
-msgstr ""
-
-msgid "Dependencies|Status"
-msgstr ""
-
msgid "Dependencies|The %{codeStartTag}dependency_scanning%{codeEndTag} job has failed and cannot generate the list. Please ensure the job is running properly and run the pipeline again."
msgstr ""
@@ -6944,9 +6945,6 @@ msgstr ""
msgid "Dependencies|Unsupported file(s) detected"
msgstr ""
-msgid "Dependencies|Version"
-msgstr ""
-
msgid "Dependencies|Vulnerable components"
msgstr ""
@@ -12739,6 +12737,9 @@ msgstr ""
msgid "Manage labels"
msgstr ""
+msgid "Manage milestones"
+msgstr ""
+
msgid "Manage project labels"
msgstr ""
@@ -14005,6 +14006,9 @@ msgstr ""
msgid "No messages were logged"
msgstr ""
+msgid "No milestone"
+msgstr ""
+
msgid "No milestones to show"
msgstr ""
@@ -18171,6 +18175,9 @@ msgstr ""
msgid "Search Button"
msgstr ""
+msgid "Search Milestones"
+msgstr ""
+
msgid "Search an environment spec"
msgstr ""
diff --git a/package.json b/package.json
index acac9f5f36b..82646e20d11 100644
--- a/package.json
+++ b/package.json
@@ -39,8 +39,8 @@
"@babel/plugin-syntax-import-meta": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@gitlab/at.js": "1.5.5",
- "@gitlab/svgs": "1.125.0",
- "@gitlab/ui": "14.0.0",
+ "@gitlab/svgs": "1.127.0",
+ "@gitlab/ui": "14.2.1",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.2-2",
"@sentry/browser": "^5.10.2",
diff --git a/spec/controllers/projects/alert_management_controller_spec.rb b/spec/controllers/projects/alert_management_controller_spec.rb
index ccb9bfc3001..31185aa948a 100644
--- a/spec/controllers/projects/alert_management_controller_spec.rb
+++ b/spec/controllers/projects/alert_management_controller_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe Projects::AlertManagementController do
let_it_be(:project) { create(:project) }
- let_it_be(:role) { :reporter }
+ let_it_be(:role) { :developer }
let_it_be(:user) { create(:user) }
let_it_be(:id) { 1 }
@@ -24,6 +24,16 @@ describe Projects::AlertManagementController do
expect(response).to have_gitlab_http_status(:ok)
end
+
+ context 'when user is unauthorized' do
+ let(:role) { :reporter }
+
+ it 'shows 404' do
+ get :index, params: { namespace_id: project.namespace, project_id: project }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
context 'when alert_management_minimal is disabled' do
@@ -50,6 +60,16 @@ describe Projects::AlertManagementController do
expect(response).to have_gitlab_http_status(:ok)
end
+
+ context 'when user is unauthorized' do
+ let(:role) { :reporter }
+
+ it 'shows 404' do
+ get :index, params: { namespace_id: project.namespace, project_id: project }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
context 'when alert_management_detail is disabled' do
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index c499fac6bc0..a7c8c29517e 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -21,7 +21,7 @@ describe 'Global search' do
describe 'I search through the issues and I see pagination' do
before do
- allow_next_instance_of(Gitlab::SearchResults) do |instance|
+ allow_next_instance_of(SearchService) do |instance|
allow(instance).to receive(:per_page).and_return(1)
end
create_list(:issue, 2, project: project, title: 'initial')
diff --git a/spec/frontend/api_spec.js b/spec/frontend/api_spec.js
index f34c2fb69eb..d365048ab0b 100644
--- a/spec/frontend/api_spec.js
+++ b/spec/frontend/api_spec.js
@@ -15,7 +15,7 @@ describe('Api', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
originalGon = window.gon;
- window.gon = Object.assign({}, dummyGon);
+ window.gon = { ...dummyGon };
});
afterEach(() => {
diff --git a/spec/frontend/blob/components/blob_content_spec.js b/spec/frontend/blob/components/blob_content_spec.js
index 6a130c9c43d..ff153007be9 100644
--- a/spec/frontend/blob/components/blob_content_spec.js
+++ b/spec/frontend/blob/components/blob_content_spec.js
@@ -38,7 +38,7 @@ describe('Blob Content component', () => {
it('renders error if there is any in the viewer', () => {
const renderError = 'Oops';
- const viewer = Object.assign({}, SimpleViewerMock, { renderError });
+ const viewer = { ...SimpleViewerMock, renderError };
createComponent({}, viewer);
expect(wrapper.contains(GlLoadingIcon)).toBe(false);
expect(wrapper.contains(BlobContentError)).toBe(true);
diff --git a/spec/frontend/blob/components/blob_header_filepath_spec.js b/spec/frontend/blob/components/blob_header_filepath_spec.js
index 6f38167d4bd..3a53208f357 100644
--- a/spec/frontend/blob/components/blob_header_filepath_spec.js
+++ b/spec/frontend/blob/components/blob_header_filepath_spec.js
@@ -15,7 +15,7 @@ describe('Blob Header Filepath', () => {
function createComponent(blobProps = {}, options = {}) {
wrapper = shallowMount(BlobHeaderFilepath, {
propsData: {
- blob: Object.assign({}, MockBlob, blobProps),
+ blob: { ...MockBlob, ...blobProps },
},
...options,
});
diff --git a/spec/frontend/blob/components/blob_header_spec.js b/spec/frontend/blob/components/blob_header_spec.js
index d410ef10fc9..0e7d2f6516a 100644
--- a/spec/frontend/blob/components/blob_header_spec.js
+++ b/spec/frontend/blob/components/blob_header_spec.js
@@ -13,7 +13,7 @@ describe('Blob Header Default Actions', () => {
const method = shouldMount ? mount : shallowMount;
wrapper = method.call(this, BlobHeader, {
propsData: {
- blob: Object.assign({}, Blob, blobProps),
+ blob: { ...Blob, ...blobProps },
...propsData,
},
...options,
diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js
index 882310030f8..fa21053e2de 100644
--- a/spec/frontend/boards/board_list_spec.js
+++ b/spec/frontend/boards/board_list_spec.js
@@ -64,7 +64,7 @@ describe('Board list component', () => {
let getIssues;
function generateIssues(compWrapper) {
for (let i = 1; i < 20; i += 1) {
- const issue = Object.assign({}, compWrapper.list.issues[0]);
+ const issue = { ...compWrapper.list.issues[0] };
issue.id += i;
compWrapper.list.issues.push(issue);
}
diff --git a/spec/frontend/clusters/components/knative_domain_editor_spec.js b/spec/frontend/clusters/components/knative_domain_editor_spec.js
index 2de04f7da1f..73d08661199 100644
--- a/spec/frontend/clusters/components/knative_domain_editor_spec.js
+++ b/spec/frontend/clusters/components/knative_domain_editor_spec.js
@@ -93,7 +93,7 @@ describe('KnativeDomainEditor', () => {
it('displays toast indicating a successful update', () => {
wrapper.vm.$toast = { show: jest.fn() };
- wrapper.setProps({ knative: Object.assign({ updateSuccessful: true }, knative) });
+ wrapper.setProps({ knative: { updateSuccessful: true, ...knative } });
return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(
diff --git a/spec/frontend/commit/pipelines/pipelines_spec.js b/spec/frontend/commit/pipelines/pipelines_spec.js
index b88cba90b87..86ae207e7b7 100644
--- a/spec/frontend/commit/pipelines/pipelines_spec.js
+++ b/spec/frontend/commit/pipelines/pipelines_spec.js
@@ -118,7 +118,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
let pipelineCopy;
beforeEach(() => {
- pipelineCopy = Object.assign({}, pipeline);
+ pipelineCopy = { ...pipeline };
});
describe('when latest pipeline has detached flag and canRunPipeline is true', () => {
@@ -128,12 +128,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
mock.onGet('endpoint.json').reply(200, [pipelineCopy]);
- vm = mountComponent(
- PipelinesTable,
- Object.assign({}, props, {
- canRunPipeline: true,
- }),
- );
+ vm = mountComponent(PipelinesTable, { ...props, canRunPipeline: true });
setImmediate(() => {
expect(vm.$el.querySelector('.js-run-mr-pipeline')).not.toBeNull();
@@ -149,12 +144,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
mock.onGet('endpoint.json').reply(200, [pipelineCopy]);
- vm = mountComponent(
- PipelinesTable,
- Object.assign({}, props, {
- canRunPipeline: false,
- }),
- );
+ vm = mountComponent(PipelinesTable, { ...props, canRunPipeline: false });
setImmediate(() => {
expect(vm.$el.querySelector('.js-run-mr-pipeline')).toBeNull();
@@ -170,12 +160,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
mock.onGet('endpoint.json').reply(200, [pipelineCopy]);
- vm = mountComponent(
- PipelinesTable,
- Object.assign({}, props, {
- canRunPipeline: true,
- }),
- );
+ vm = mountComponent(PipelinesTable, { ...props, canRunPipeline: true });
setImmediate(() => {
expect(vm.$el.querySelector('.js-run-mr-pipeline')).toBeNull();
@@ -191,12 +176,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
mock.onGet('endpoint.json').reply(200, [pipelineCopy]);
- vm = mountComponent(
- PipelinesTable,
- Object.assign({}, props, {
- canRunPipeline: false,
- }),
- );
+ vm = mountComponent(PipelinesTable, { ...props, canRunPipeline: false });
setImmediate(() => {
expect(vm.$el.querySelector('.js-run-mr-pipeline')).toBeNull();
@@ -211,14 +191,12 @@ describe('Pipelines table in Commits and Merge requests', () => {
mock.onGet('endpoint.json').reply(200, [pipelineCopy]);
- vm = mountComponent(
- PipelinesTable,
- Object.assign({}, props, {
- canRunPipeline: true,
- projectId: '5',
- mergeRequestId: 3,
- }),
- );
+ vm = mountComponent(PipelinesTable, {
+ ...props,
+ canRunPipeline: true,
+ projectId: '5',
+ mergeRequestId: 3,
+ });
});
it('updates the loading state', done => {
diff --git a/spec/frontend/diffs/components/diff_discussions_spec.js b/spec/frontend/diffs/components/diff_discussions_spec.js
index ba5a4f96204..83becc7a20a 100644
--- a/spec/frontend/diffs/components/diff_discussions_spec.js
+++ b/spec/frontend/diffs/components/diff_discussions_spec.js
@@ -13,7 +13,7 @@ const localVue = createLocalVue();
describe('DiffDiscussions', () => {
let store;
let wrapper;
- const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+ const getDiscussionsMockData = () => [{ ...discussionsMockData }];
const createComponent = props => {
store = createStore();
diff --git a/spec/frontend/diffs/components/diff_expansion_cell_spec.js b/spec/frontend/diffs/components/diff_expansion_cell_spec.js
index 31c6a4d5b60..0504f3933e0 100644
--- a/spec/frontend/diffs/components/diff_expansion_cell_spec.js
+++ b/spec/frontend/diffs/components/diff_expansion_cell_spec.js
@@ -81,7 +81,7 @@ describe('DiffExpansionCell', () => {
isTop: false,
isBottom: false,
};
- const props = Object.assign({}, defaults, options);
+ const props = { ...defaults, ...options };
vm = createComponentWithStore(cmp, store, props).$mount();
};
diff --git a/spec/frontend/diffs/components/diff_gutter_avatars_spec.js b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
index 4d8345d494d..da18d8e7894 100644
--- a/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
+++ b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
import discussionsMockData from '../mock_data/diff_discussions';
-const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+const getDiscussionsMockData = () => [{ ...discussionsMockData }];
describe('DiffGutterAvatars', () => {
let wrapper;
diff --git a/spec/frontend/diffs/components/diff_line_note_form_spec.js b/spec/frontend/diffs/components/diff_line_note_form_spec.js
index 9b032d10fdc..3e0acd0dace 100644
--- a/spec/frontend/diffs/components/diff_line_note_form_spec.js
+++ b/spec/frontend/diffs/components/diff_line_note_form_spec.js
@@ -9,7 +9,7 @@ describe('DiffLineNoteForm', () => {
let wrapper;
let diffFile;
let diffLines;
- const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+ const getDiffFileMock = () => ({ ...diffFileMockData });
beforeEach(() => {
diffFile = getDiffFileMock();
diff --git a/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js b/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js
index f423c3b111e..90f012fbafe 100644
--- a/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js
+++ b/spec/frontend/diffs/components/inline_diff_expansion_row_spec.js
@@ -16,7 +16,7 @@ describe('InlineDiffExpansionRow', () => {
isTop: false,
isBottom: false,
};
- const props = Object.assign({}, defaults, options);
+ const props = { ...defaults, ...options };
return createComponentWithStore(cmp, createStore(), props).$mount();
};
diff --git a/spec/frontend/diffs/components/inline_diff_view_spec.js b/spec/frontend/diffs/components/inline_diff_view_spec.js
index a63c13fb271..9b0cf6a84d9 100644
--- a/spec/frontend/diffs/components/inline_diff_view_spec.js
+++ b/spec/frontend/diffs/components/inline_diff_view_spec.js
@@ -8,8 +8,8 @@ import discussionsMockData from '../mock_data/diff_discussions';
describe('InlineDiffView', () => {
let component;
- const getDiffFileMock = () => Object.assign({}, diffFileMockData);
- const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+ const getDiffFileMock = () => ({ ...diffFileMockData });
+ const getDiscussionsMockData = () => [{ ...discussionsMockData }];
const notesLength = getDiscussionsMockData()[0].notes.length;
beforeEach(done => {
diff --git a/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js b/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js
index 15b2a824697..38112445e8d 100644
--- a/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js
+++ b/spec/frontend/diffs/components/parallel_diff_expansion_row_spec.js
@@ -16,7 +16,7 @@ describe('ParallelDiffExpansionRow', () => {
isTop: false,
isBottom: false,
};
- const props = Object.assign({}, defaults, options);
+ const props = { ...defaults, ...options };
return createComponentWithStore(cmp, createStore(), props).$mount();
};
diff --git a/spec/frontend/diffs/components/parallel_diff_view_spec.js b/spec/frontend/diffs/components/parallel_diff_view_spec.js
index 0eefbc7ec08..03cf1b72b62 100644
--- a/spec/frontend/diffs/components/parallel_diff_view_spec.js
+++ b/spec/frontend/diffs/components/parallel_diff_view_spec.js
@@ -7,7 +7,7 @@ import diffFileMockData from '../mock_data/diff_file';
describe('ParallelDiffView', () => {
let component;
- const getDiffFileMock = () => Object.assign({}, diffFileMockData);
+ const getDiffFileMock = () => ({ ...diffFileMockData });
beforeEach(() => {
const diffFile = getDiffFileMock();
diff --git a/spec/frontend/diffs/store/getters_spec.js b/spec/frontend/diffs/store/getters_spec.js
index ca47f51cb15..dac5be2d656 100644
--- a/spec/frontend/diffs/store/getters_spec.js
+++ b/spec/frontend/diffs/store/getters_spec.js
@@ -14,10 +14,10 @@ describe('Diffs Module Getters', () => {
beforeEach(() => {
localState = state();
- discussionMock = Object.assign({}, discussion);
+ discussionMock = { ...discussion };
discussionMock.diff_file.file_hash = diffFileMock.fileHash;
- discussionMock1 = Object.assign({}, discussion);
+ discussionMock1 = { ...discussion };
discussionMock1.diff_file.file_hash = diffFileMock.fileHash;
});
diff --git a/spec/frontend/diffs/store/utils_spec.js b/spec/frontend/diffs/store/utils_spec.js
index 422332bab28..31053a3055a 100644
--- a/spec/frontend/diffs/store/utils_spec.js
+++ b/spec/frontend/diffs/store/utils_spec.js
@@ -372,13 +372,13 @@ describe('DiffsStoreUtils', () => {
mock = getDiffFileMock();
preparedDiff = { diff_files: [mock] };
splitInlineDiff = {
- diff_files: [Object.assign({}, mock, { parallel_diff_lines: undefined })],
+ diff_files: [{ ...mock, parallel_diff_lines: undefined }],
};
splitParallelDiff = {
- diff_files: [Object.assign({}, mock, { highlighted_diff_lines: undefined })],
+ diff_files: [{ ...mock, highlighted_diff_lines: undefined }],
};
completedDiff = {
- diff_files: [Object.assign({}, mock, { highlighted_diff_lines: undefined })],
+ diff_files: [{ ...mock, highlighted_diff_lines: undefined }],
};
preparedDiff.diff_files = utils.prepareDiffData(preparedDiff);
diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js
index 6b2a814d721..35eda21e047 100644
--- a/spec/frontend/groups/components/app_spec.js
+++ b/spec/frontend/groups/components/app_spec.js
@@ -216,7 +216,7 @@ describe('AppComponent', () => {
let groupItem;
beforeEach(() => {
- groupItem = Object.assign({}, mockParentGroupItem);
+ groupItem = { ...mockParentGroupItem };
groupItem.isOpen = false;
groupItem.isChildrenLoading = false;
});
@@ -271,7 +271,7 @@ describe('AppComponent', () => {
describe('showLeaveGroupModal', () => {
it('caches candidate group (as props) which is to be left', () => {
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
expect(vm.targetGroup).toBe(null);
expect(vm.targetParentGroup).toBe(null);
@@ -282,7 +282,7 @@ describe('AppComponent', () => {
});
it('updates props which show modal confirmation dialog', () => {
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
expect(vm.showModal).toBe(false);
expect(vm.groupLeaveConfirmationMessage).toBe('');
@@ -297,7 +297,7 @@ describe('AppComponent', () => {
describe('hideLeaveGroupModal', () => {
it('hides modal confirmation which is shown before leaving the group', () => {
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
vm.showLeaveGroupModal(group, mockParentGroupItem);
expect(vm.showModal).toBe(true);
@@ -312,7 +312,7 @@ describe('AppComponent', () => {
let childGroupItem;
beforeEach(() => {
- groupItem = Object.assign({}, mockParentGroupItem);
+ groupItem = { ...mockParentGroupItem };
groupItem.children = mockChildren;
[childGroupItem] = groupItem.children;
groupItem.isChildrenLoading = false;
diff --git a/spec/frontend/groups/components/group_folder_spec.js b/spec/frontend/groups/components/group_folder_spec.js
index 4b545f05c58..a40fa9bece8 100644
--- a/spec/frontend/groups/components/group_folder_spec.js
+++ b/spec/frontend/groups/components/group_folder_spec.js
@@ -52,7 +52,7 @@ describe('GroupFolderComponent', () => {
});
it('should render more children link when groups list has children over MAX_CHILDREN_COUNT limit', () => {
- const parentGroup = Object.assign({}, mockParentGroupItem);
+ const parentGroup = { ...mockParentGroupItem };
parentGroup.childrenCount = 21;
const newVm = createComponent(mockGroups, parentGroup);
diff --git a/spec/frontend/groups/components/group_item_spec.js b/spec/frontend/groups/components/group_item_spec.js
index d1f7653923a..7eb1c54ddb2 100644
--- a/spec/frontend/groups/components/group_item_spec.js
+++ b/spec/frontend/groups/components/group_item_spec.js
@@ -52,7 +52,7 @@ describe('GroupItemComponent', () => {
describe('hasChildren', () => {
it('should return boolean value representing if group has any children present', () => {
let newVm;
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
group.childrenCount = 5;
newVm = createComponent(group);
@@ -71,7 +71,7 @@ describe('GroupItemComponent', () => {
describe('hasAvatar', () => {
it('should return boolean value representing if group has any avatar present', () => {
let newVm;
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
group.avatarUrl = null;
newVm = createComponent(group);
@@ -90,7 +90,7 @@ describe('GroupItemComponent', () => {
describe('isGroup', () => {
it('should return boolean value representing if group item is of type `group` or not', () => {
let newVm;
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
group.type = 'group';
newVm = createComponent(group);
@@ -138,7 +138,7 @@ describe('GroupItemComponent', () => {
it('should navigate page to group homepage if group does not have any children present', () => {
jest.spyOn(urlUtilities, 'visitUrl').mockImplementation();
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
group.childrenCount = 0;
const newVm = createComponent(group);
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
diff --git a/spec/frontend/groups/components/item_actions_spec.js b/spec/frontend/groups/components/item_actions_spec.js
index 2e0738bd1b4..c0dc1a816e6 100644
--- a/spec/frontend/groups/components/item_actions_spec.js
+++ b/spec/frontend/groups/components/item_actions_spec.js
@@ -46,7 +46,7 @@ describe('ItemActionsComponent', () => {
});
it('should render Edit Group button with correct attribute values', () => {
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
group.canEdit = true;
const newVm = createComponent(group);
@@ -64,7 +64,7 @@ describe('ItemActionsComponent', () => {
});
it('should render Leave Group button with correct attribute values', () => {
- const group = Object.assign({}, mockParentGroupItem);
+ const group = { ...mockParentGroupItem };
group.canLeave = true;
const newVm = createComponent(group);
diff --git a/spec/frontend/groups/components/item_stats_spec.js b/spec/frontend/groups/components/item_stats_spec.js
index fb4285a2b04..771643609ec 100644
--- a/spec/frontend/groups/components/item_stats_spec.js
+++ b/spec/frontend/groups/components/item_stats_spec.js
@@ -23,7 +23,7 @@ describe('ItemStatsComponent', () => {
describe('visibilityIcon', () => {
it('should return icon class based on `item.visibility` value', () => {
Object.keys(VISIBILITY_TYPE_ICON).forEach(visibility => {
- const item = Object.assign({}, mockParentGroupItem, { visibility });
+ const item = { ...mockParentGroupItem, visibility };
const vm = createComponent(item);
expect(vm.visibilityIcon).toBe(VISIBILITY_TYPE_ICON[visibility]);
@@ -35,10 +35,7 @@ describe('ItemStatsComponent', () => {
describe('visibilityTooltip', () => {
it('should return tooltip string for Group based on `item.visibility` value', () => {
Object.keys(GROUP_VISIBILITY_TYPE).forEach(visibility => {
- const item = Object.assign({}, mockParentGroupItem, {
- visibility,
- type: ITEM_TYPE.GROUP,
- });
+ const item = { ...mockParentGroupItem, visibility, type: ITEM_TYPE.GROUP };
const vm = createComponent(item);
expect(vm.visibilityTooltip).toBe(GROUP_VISIBILITY_TYPE[visibility]);
@@ -48,10 +45,7 @@ describe('ItemStatsComponent', () => {
it('should return tooltip string for Project based on `item.visibility` value', () => {
Object.keys(PROJECT_VISIBILITY_TYPE).forEach(visibility => {
- const item = Object.assign({}, mockParentGroupItem, {
- visibility,
- type: ITEM_TYPE.PROJECT,
- });
+ const item = { ...mockParentGroupItem, visibility, type: ITEM_TYPE.PROJECT };
const vm = createComponent(item);
expect(vm.visibilityTooltip).toBe(PROJECT_VISIBILITY_TYPE[visibility]);
@@ -65,13 +59,13 @@ describe('ItemStatsComponent', () => {
let item;
let vm;
- item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
+ item = { ...mockParentGroupItem, type: ITEM_TYPE.PROJECT };
vm = createComponent(item);
expect(vm.isProject).toBeTruthy();
vm.$destroy();
- item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
+ item = { ...mockParentGroupItem, type: ITEM_TYPE.GROUP };
vm = createComponent(item);
expect(vm.isProject).toBeFalsy();
@@ -84,13 +78,13 @@ describe('ItemStatsComponent', () => {
let item;
let vm;
- item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP });
+ item = { ...mockParentGroupItem, type: ITEM_TYPE.GROUP };
vm = createComponent(item);
expect(vm.isGroup).toBeTruthy();
vm.$destroy();
- item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT });
+ item = { ...mockParentGroupItem, type: ITEM_TYPE.PROJECT };
vm = createComponent(item);
expect(vm.isGroup).toBeFalsy();
@@ -109,10 +103,7 @@ describe('ItemStatsComponent', () => {
});
it('renders start count and last updated information for project item correctly', () => {
- const item = Object.assign({}, mockParentGroupItem, {
- type: ITEM_TYPE.PROJECT,
- starCount: 4,
- });
+ const item = { ...mockParentGroupItem, type: ITEM_TYPE.PROJECT, starCount: 4 };
const vm = createComponent(item);
const projectStarIconEl = vm.$el.querySelector('.project-stars');
diff --git a/spec/frontend/groups/components/item_stats_value_spec.js b/spec/frontend/groups/components/item_stats_value_spec.js
index 9561a329887..da6f145fa19 100644
--- a/spec/frontend/groups/components/item_stats_value_spec.js
+++ b/spec/frontend/groups/components/item_stats_value_spec.js
@@ -27,7 +27,7 @@ describe('ItemStatsValueComponent', () => {
describe('isValuePresent', () => {
it('returns true if non-empty `value` is present', () => {
- vm = createComponent(Object.assign({}, itemConfig, { value: 10 }));
+ vm = createComponent({ ...itemConfig, value: 10 });
expect(vm.isValuePresent).toBeTruthy();
});
diff --git a/spec/frontend/groups/store/groups_store_spec.js b/spec/frontend/groups/store/groups_store_spec.js
index 9eefcbe0275..7d12f73d270 100644
--- a/spec/frontend/groups/store/groups_store_spec.js
+++ b/spec/frontend/groups/store/groups_store_spec.js
@@ -108,8 +108,8 @@ describe('ProjectsStore', () => {
describe('removeGroup', () => {
it('should remove children from group item in state', () => {
const store = new GroupsStore();
- const rawParentGroup = Object.assign({}, mockGroups[0]);
- const rawChildGroup = Object.assign({}, mockGroups[1]);
+ const rawParentGroup = { ...mockGroups[0] };
+ const rawChildGroup = { ...mockGroups[1] };
store.setGroups([rawParentGroup]);
store.setGroupChildren(store.state.groups[0], [rawChildGroup]);
diff --git a/spec/frontend/ide/components/ide_review_spec.js b/spec/frontend/ide/components/ide_review_spec.js
index 30a09092f70..b56957e1f6d 100644
--- a/spec/frontend/ide/components/ide_review_spec.js
+++ b/spec/frontend/ide/components/ide_review_spec.js
@@ -15,7 +15,7 @@ describe('IDE review mode', () => {
store = createStore();
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master';
- store.state.projects.abcproject = Object.assign({}, projectData);
+ store.state.projects.abcproject = { ...projectData };
Vue.set(store.state.trees, 'abcproject/master', {
tree: [file('fileName')],
loading: false,
diff --git a/spec/frontend/ide/components/ide_spec.js b/spec/frontend/ide/components/ide_spec.js
index ad2b3c8c01a..78a280e6304 100644
--- a/spec/frontend/ide/components/ide_spec.js
+++ b/spec/frontend/ide/components/ide_spec.js
@@ -10,7 +10,7 @@ function bootstrap(projData) {
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master';
- store.state.projects.abcproject = Object.assign({}, projData);
+ store.state.projects.abcproject = { ...projData };
Vue.set(store.state.trees, 'abcproject/master', {
tree: [],
loading: false,
@@ -27,7 +27,7 @@ describe('ide component, empty repo', () => {
let vm;
beforeEach(() => {
- const emptyProjData = Object.assign({}, projectData, { empty_repo: true, branches: {} });
+ const emptyProjData = { ...projectData, empty_repo: true, branches: {} };
vm = bootstrap(emptyProjData);
vm.$mount();
});
diff --git a/spec/frontend/ide/components/ide_tree_list_spec.js b/spec/frontend/ide/components/ide_tree_list_spec.js
index 6694ac497fb..30f11db3153 100644
--- a/spec/frontend/ide/components/ide_tree_list_spec.js
+++ b/spec/frontend/ide/components/ide_tree_list_spec.js
@@ -14,7 +14,7 @@ describe('IDE tree list', () => {
const bootstrapWithTree = (tree = normalBranchTree) => {
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master';
- store.state.projects.abcproject = Object.assign({}, projectData);
+ store.state.projects.abcproject = { ...projectData };
Vue.set(store.state.trees, 'abcproject/master', {
tree,
loading: false,
diff --git a/spec/frontend/ide/components/ide_tree_spec.js b/spec/frontend/ide/components/ide_tree_spec.js
index 97a0a2432f1..01f007f09c3 100644
--- a/spec/frontend/ide/components/ide_tree_spec.js
+++ b/spec/frontend/ide/components/ide_tree_spec.js
@@ -13,7 +13,7 @@ describe('IdeRepoTree', () => {
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master';
- store.state.projects.abcproject = Object.assign({}, projectData);
+ store.state.projects.abcproject = { ...projectData };
Vue.set(store.state.trees, 'abcproject/master', {
tree: [file('fileName')],
loading: false,
diff --git a/spec/frontend/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_projects/components/import_projects_table_spec.js
index 8f60823ee72..9491b52c888 100644
--- a/spec/frontend/import_projects/components/import_projects_table_spec.js
+++ b/spec/frontend/import_projects/components/import_projects_table_spec.js
@@ -17,11 +17,12 @@ describe('ImportProjectsTable', () => {
};
function initStore() {
- const stubbedActions = Object.assign({}, actions, {
+ const stubbedActions = {
+ ...actions,
fetchJobs: jest.fn(),
fetchRepos: jest.fn(actions.requestRepos),
fetchImport: jest.fn(actions.requestImport),
- });
+ };
const store = new Vuex.Store({
state: state(),
diff --git a/spec/frontend/import_projects/components/provider_repo_table_row_spec.js b/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
index 8efd526e360..8be645c496f 100644
--- a/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
+++ b/spec/frontend/import_projects/components/provider_repo_table_row_spec.js
@@ -18,9 +18,7 @@ describe('ProviderRepoTableRow', () => {
};
function initStore() {
- const stubbedActions = Object.assign({}, actions, {
- fetchImport,
- });
+ const stubbedActions = { ...actions, fetchImport };
const store = new Vuex.Store({
state: state(),
diff --git a/spec/frontend/integrations/edit/components/active_toggle_spec.js b/spec/frontend/integrations/edit/components/active_toggle_spec.js
index 7f13d707efd..4b968c9a70b 100644
--- a/spec/frontend/integrations/edit/components/active_toggle_spec.js
+++ b/spec/frontend/integrations/edit/components/active_toggle_spec.js
@@ -13,7 +13,7 @@ describe('ActiveToggle', () => {
const createComponent = props => {
wrapper = mount(ActiveToggle, {
- propsData: Object.assign({}, defaultProps, props),
+ propsData: { ...defaultProps, ...props },
});
};
diff --git a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js
index 16d9df96c16..e8aa920fdd4 100644
--- a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js
+++ b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js
@@ -13,7 +13,7 @@ describe('JiraTriggerFields', () => {
const createComponent = props => {
wrapper = mount(JiraTriggerFields, {
- propsData: Object.assign({}, defaultProps, props),
+ propsData: { ...defaultProps, ...props },
});
};
diff --git a/spec/frontend/issue_show/components/description_spec.js b/spec/frontend/issue_show/components/description_spec.js
index 9c448c498e2..0053475dd13 100644
--- a/spec/frontend/issue_show/components/description_spec.js
+++ b/spec/frontend/issue_show/components/description_spec.js
@@ -113,12 +113,7 @@ describe('Description component', () => {
beforeEach(() => {
vm.$destroy();
TaskList.mockClear();
- vm = mountComponent(
- DescriptionComponent,
- Object.assign({}, props, {
- issuableType: 'issuableType',
- }),
- );
+ vm = mountComponent(DescriptionComponent, { ...props, issuableType: 'issuableType' });
});
it('re-inits the TaskList when description changed', () => {
diff --git a/spec/frontend/jobs/components/log/line_header_spec.js b/spec/frontend/jobs/components/log/line_header_spec.js
index f2e202674ee..5ce69221dab 100644
--- a/spec/frontend/jobs/components/log/line_header_spec.js
+++ b/spec/frontend/jobs/components/log/line_header_spec.js
@@ -86,7 +86,7 @@ describe('Job Log Header Line', () => {
describe('with duration', () => {
beforeEach(() => {
- createComponent(Object.assign({}, data, { duration: '00:10' }));
+ createComponent({ ...data, duration: '00:10' });
});
it('renders the duration badge', () => {
diff --git a/spec/frontend/jobs/store/mutations_spec.js b/spec/frontend/jobs/store/mutations_spec.js
index d77690ffac0..3557d3b94b6 100644
--- a/spec/frontend/jobs/store/mutations_spec.js
+++ b/spec/frontend/jobs/store/mutations_spec.js
@@ -59,7 +59,7 @@ describe('Jobs Store Mutations', () => {
describe('when traceSize is bigger than the total size', () => {
it('sets isTraceSizeVisible to false', () => {
- const copy = Object.assign({}, stateCopy, { traceSize: 5118460, size: 2321312 });
+ const copy = { ...stateCopy, traceSize: 5118460, size: 2321312 };
mutations[types.RECEIVE_TRACE_SUCCESS](copy, { total: 511846 });
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index 1edfda30fec..c8dc90c9ace 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -503,7 +503,7 @@ describe('common_utils', () => {
beforeEach(() => {
window.gon = window.gon || {};
- beforeGon = Object.assign({}, window.gon);
+ beforeGon = { ...window.gon };
window.gon.sprite_icons = 'icons.svg';
});
diff --git a/spec/frontend/milestones/mock_data.js b/spec/frontend/milestones/mock_data.js
new file mode 100644
index 00000000000..c64eeeba663
--- /dev/null
+++ b/spec/frontend/milestones/mock_data.js
@@ -0,0 +1,82 @@
+export const milestones = [
+ {
+ id: 41,
+ iid: 6,
+ project_id: 8,
+ title: 'v0.1',
+ description: '',
+ state: 'active',
+ created_at: '2020-04-04T01:30:40.051Z',
+ updated_at: '2020-04-04T01:30:40.051Z',
+ due_date: null,
+ start_date: null,
+ web_url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/6',
+ },
+ {
+ id: 40,
+ iid: 5,
+ project_id: 8,
+ title: 'v4.0',
+ description: 'Laboriosam nisi sapiente dolores et magnam nobis ad earum.',
+ state: 'closed',
+ created_at: '2020-01-13T19:39:15.191Z',
+ updated_at: '2020-01-13T19:39:15.191Z',
+ due_date: null,
+ start_date: null,
+ web_url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/5',
+ },
+ {
+ id: 39,
+ iid: 4,
+ project_id: 8,
+ title: 'v3.0',
+ description: 'Necessitatibus illo alias et repellat dolorum assumenda ut.',
+ state: 'closed',
+ created_at: '2020-01-13T19:39:15.176Z',
+ updated_at: '2020-01-13T19:39:15.176Z',
+ due_date: null,
+ start_date: null,
+ web_url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/4',
+ },
+ {
+ id: 38,
+ iid: 3,
+ project_id: 8,
+ title: 'v2.0',
+ description: 'Doloribus qui repudiandae iste sit.',
+ state: 'closed',
+ created_at: '2020-01-13T19:39:15.161Z',
+ updated_at: '2020-01-13T19:39:15.161Z',
+ due_date: null,
+ start_date: null,
+ web_url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/3',
+ },
+ {
+ id: 37,
+ iid: 2,
+ project_id: 8,
+ title: 'v1.0',
+ description: 'Illo sint odio officia ea.',
+ state: 'closed',
+ created_at: '2020-01-13T19:39:15.146Z',
+ updated_at: '2020-01-13T19:39:15.146Z',
+ due_date: null,
+ start_date: null,
+ web_url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/2',
+ },
+ {
+ id: 36,
+ iid: 1,
+ project_id: 8,
+ title: 'v0.0',
+ description: 'Sed quae facilis deleniti at delectus assumenda nobis veritatis.',
+ state: 'active',
+ created_at: '2020-01-13T19:39:15.127Z',
+ updated_at: '2020-01-13T19:39:15.127Z',
+ due_date: null,
+ start_date: null,
+ web_url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/1',
+ },
+];
+
+export default milestones;
diff --git a/spec/frontend/milestones/project_milestone_combobox_spec.js b/spec/frontend/milestones/project_milestone_combobox_spec.js
new file mode 100644
index 00000000000..a7321d21559
--- /dev/null
+++ b/spec/frontend/milestones/project_milestone_combobox_spec.js
@@ -0,0 +1,150 @@
+import { milestones as projectMilestones } from './mock_data';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
+import { shallowMount } from '@vue/test-utils';
+import MilestoneCombobox from '~/milestones/project_milestone_combobox.vue';
+import { GlNewDropdown, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
+
+const TEST_SEARCH_ENDPOINT = '/api/v4/projects/8/search';
+
+const extraLinks = [
+ { text: 'Create new', url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/new' },
+ { text: 'Manage milestones', url: '/h5bp/html5-boilerplate/-/milestones' },
+];
+
+const preselectedMilestones = [];
+const projectId = '8';
+
+describe('Milestone selector', () => {
+ let wrapper;
+ let mock;
+
+ const findNoResultsMessage = () => wrapper.find({ ref: 'noResults' });
+
+ const factory = (options = {}) => {
+ wrapper = shallowMount(MilestoneCombobox, {
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ gon.api_version = 'v4';
+
+ mock.onGet('/api/v4/projects/8/milestones').reply(200, projectMilestones);
+
+ factory({
+ propsData: {
+ projectId,
+ preselectedMilestones,
+ extraLinks,
+ },
+ });
+ });
+
+ afterEach(() => {
+ mock.restore();
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders the dropdown', () => {
+ expect(wrapper.find(GlNewDropdown)).toExist();
+ });
+
+ it('renders additional links', () => {
+ const links = wrapper.findAll('[href]');
+ links.wrappers.forEach((item, idx) => {
+ expect(item.text()).toBe(extraLinks[idx].text);
+ expect(item.attributes('href')).toBe(extraLinks[idx].url);
+ });
+ });
+
+ describe('before results', () => {
+ it('should show a loading icon', () => {
+ const request = mock.onGet(TEST_SEARCH_ENDPOINT, {
+ params: { search: 'TEST_SEARCH', scope: 'milestones' },
+ });
+
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+
+ return wrapper.vm.$nextTick().then(() => {
+ request.reply(200, []);
+ });
+ });
+
+ it('should not show any dropdown items', () => {
+ expect(wrapper.findAll('[role="milestone option"]')).toHaveLength(0);
+ });
+
+ it('should have "No milestone" as the button text', () => {
+ expect(wrapper.find({ ref: 'buttonText' }).text()).toBe('No milestone');
+ });
+ });
+
+ describe('with empty results', () => {
+ beforeEach(() => {
+ mock
+ .onGet(TEST_SEARCH_ENDPOINT, { params: { search: 'TEST_SEARCH', scope: 'milestones' } })
+ .reply(200, []);
+ wrapper.find(GlSearchBoxByType).vm.$emit('input', 'TEST_SEARCH');
+ return axios.waitForAll();
+ });
+
+ it('should display that no matching items are found', () => {
+ expect(findNoResultsMessage().exists()).toBe(true);
+ });
+ });
+
+ describe('with results', () => {
+ let items;
+ beforeEach(() => {
+ mock
+ .onGet(TEST_SEARCH_ENDPOINT, { params: { search: 'v0.1', scope: 'milestones' } })
+ .reply(200, [
+ {
+ id: 41,
+ iid: 6,
+ project_id: 8,
+ title: 'v0.1',
+ description: '',
+ state: 'active',
+ created_at: '2020-04-04T01:30:40.051Z',
+ updated_at: '2020-04-04T01:30:40.051Z',
+ due_date: null,
+ start_date: null,
+ web_url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/-/milestones/6',
+ },
+ ]);
+ wrapper.find(GlSearchBoxByType).vm.$emit('input', 'v0.1');
+ return axios.waitForAll().then(() => {
+ items = wrapper.findAll('[role="milestone option"]');
+ });
+ });
+
+ it('should display one item per result', () => {
+ expect(items).toHaveLength(1);
+ });
+
+ it('should emit a change if an item is clicked', () => {
+ items.at(0).vm.$emit('click');
+ expect(wrapper.emitted().change.length).toBe(1);
+ expect(wrapper.emitted().change[0]).toEqual([[{ title: 'v0.1' }]]);
+ });
+
+ it('should not have a selecton icon on any item', () => {
+ items.wrappers.forEach(item => {
+ expect(item.find('.selected-item').exists()).toBe(false);
+ });
+ });
+
+ it('should have a selecton icon if an item is clicked', () => {
+ items.at(0).vm.$emit('click');
+ expect(wrapper.find('.selected-item').exists()).toBe(true);
+ });
+
+ it('should not display a message about no results', () => {
+ expect(findNoResultsMessage().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/notes/components/noteable_discussion_spec.js b/spec/frontend/notes/components/noteable_discussion_spec.js
index b91f599f158..b14ec2a65be 100644
--- a/spec/frontend/notes/components/noteable_discussion_spec.js
+++ b/spec/frontend/notes/components/noteable_discussion_spec.js
@@ -138,7 +138,7 @@ describe('noteable_discussion component', () => {
describe('signout widget', () => {
beforeEach(() => {
- originalGon = Object.assign({}, window.gon);
+ originalGon = { ...window.gon };
window.gon = window.gon || {};
});
diff --git a/spec/frontend/notes/old_notes_spec.js b/spec/frontend/notes/old_notes_spec.js
index 9f3ab4185c4..cb1d563ece7 100644
--- a/spec/frontend/notes/old_notes_spec.js
+++ b/spec/frontend/notes/old_notes_spec.js
@@ -193,7 +193,7 @@ describe.skip('Old Notes (~/notes.js)', () => {
$('.js-comment-button').click();
const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`);
- const updatedNote = Object.assign({}, noteEntity);
+ const updatedNote = { ...noteEntity };
updatedNote.note = 'bar';
notes.updateNote(updatedNote, $targetNote);
diff --git a/spec/frontend/notes/stores/collapse_utils_spec.js b/spec/frontend/notes/stores/collapse_utils_spec.js
index d3019f4b9a4..a74809eed79 100644
--- a/spec/frontend/notes/stores/collapse_utils_spec.js
+++ b/spec/frontend/notes/stores/collapse_utils_spec.js
@@ -18,9 +18,7 @@ describe('Collapse utils', () => {
});
it('returns false when a system note is not a description type', () => {
- expect(isDescriptionSystemNote(Object.assign({}, mockSystemNote, { note: 'foo' }))).toEqual(
- false,
- );
+ expect(isDescriptionSystemNote({ ...mockSystemNote, note: 'foo' })).toEqual(false);
});
it('gets the time difference between two notes', () => {
diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js
index 67757ad56c5..27e3490d64b 100644
--- a/spec/frontend/notes/stores/mutation_spec.js
+++ b/spec/frontend/notes/stores/mutation_spec.js
@@ -50,7 +50,7 @@ describe('Notes Store mutations', () => {
});
describe('ADD_NEW_REPLY_TO_DISCUSSION', () => {
- const newReply = Object.assign({}, note, { discussion_id: discussionMock.id });
+ const newReply = { ...note, discussion_id: discussionMock.id };
let state;
@@ -86,7 +86,7 @@ describe('Notes Store mutations', () => {
describe('EXPAND_DISCUSSION', () => {
it('should expand a collapsed discussion', () => {
- const discussion = Object.assign({}, discussionMock, { expanded: false });
+ const discussion = { ...discussionMock, expanded: false };
const state = {
discussions: [discussion],
@@ -100,7 +100,7 @@ describe('Notes Store mutations', () => {
describe('COLLAPSE_DISCUSSION', () => {
it('should collapse an expanded discussion', () => {
- const discussion = Object.assign({}, discussionMock, { expanded: true });
+ const discussion = { ...discussionMock, expanded: true };
const state = {
discussions: [discussion],
@@ -114,7 +114,7 @@ describe('Notes Store mutations', () => {
describe('REMOVE_PLACEHOLDER_NOTES', () => {
it('should remove all placeholder notes in indivudal notes and discussion', () => {
- const placeholderNote = Object.assign({}, individualNote, { isPlaceholderNote: true });
+ const placeholderNote = { ...individualNote, isPlaceholderNote: true };
const state = { discussions: [placeholderNote] };
mutations.REMOVE_PLACEHOLDER_NOTES(state);
@@ -298,7 +298,7 @@ describe('Notes Store mutations', () => {
describe('TOGGLE_DISCUSSION', () => {
it('should open a closed discussion', () => {
- const discussion = Object.assign({}, discussionMock, { expanded: false });
+ const discussion = { ...discussionMock, expanded: false };
const state = {
discussions: [discussion],
@@ -348,8 +348,8 @@ describe('Notes Store mutations', () => {
});
it('should open all closed discussions', () => {
- const discussion1 = Object.assign({}, discussionMock, { id: 0, expanded: false });
- const discussion2 = Object.assign({}, discussionMock, { id: 1, expanded: true });
+ const discussion1 = { ...discussionMock, id: 0, expanded: false };
+ const discussion2 = { ...discussionMock, id: 1, expanded: true };
const discussionIds = [discussion1.id, discussion2.id];
const state = { discussions: [discussion1, discussion2] };
@@ -362,8 +362,8 @@ describe('Notes Store mutations', () => {
});
it('should close all opened discussions', () => {
- const discussion1 = Object.assign({}, discussionMock, { id: 0, expanded: false });
- const discussion2 = Object.assign({}, discussionMock, { id: 1, expanded: true });
+ const discussion1 = { ...discussionMock, id: 0, expanded: false };
+ const discussion2 = { ...discussionMock, id: 1, expanded: true };
const discussionIds = [discussion1.id, discussion2.id];
const state = { discussions: [discussion1, discussion2] };
@@ -382,7 +382,7 @@ describe('Notes Store mutations', () => {
discussions: [individualNote],
};
- const updated = Object.assign({}, individualNote.notes[0], { note: 'Foo' });
+ const updated = { ...individualNote.notes[0], note: 'Foo' };
mutations.UPDATE_NOTE(state, updated);
diff --git a/spec/frontend/pipelines/graph/stage_column_component_spec.js b/spec/frontend/pipelines/graph/stage_column_component_spec.js
index 88e56eee1d6..d32534326c5 100644
--- a/spec/frontend/pipelines/graph/stage_column_component_spec.js
+++ b/spec/frontend/pipelines/graph/stage_column_component_spec.js
@@ -26,7 +26,7 @@ describe('stage column component', () => {
beforeEach(() => {
const mockGroups = [];
for (let i = 0; i < 3; i += 1) {
- const mockedJob = Object.assign({}, mockJob);
+ const mockedJob = { ...mockJob };
mockedJob.id += i;
mockGroups.push(mockedJob);
}
diff --git a/spec/javascripts/pipelines/linked_pipelines_mock.json b/spec/frontend/pipelines/linked_pipelines_mock.json
index 60e214ddc32..8ad19ef4865 100644
--- a/spec/javascripts/pipelines/linked_pipelines_mock.json
+++ b/spec/frontend/pipelines/linked_pipelines_mock.json
@@ -1766,7 +1766,8 @@
"name": "GitLab Docs",
"full_path": "/gitlab-com/gitlab-docs",
"full_name": "GitLab.com / GitLab Docs"
- }
+ },
+ "triggered": [{}]
},
{
"id": 34993052,
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
index 40c493e3aa5..e46b9c7c817 100644
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -601,7 +601,7 @@ describe('Pipelines', () => {
describe('updates results when a staged is clicked', () => {
beforeEach(() => {
- const copyPipeline = Object.assign({}, pipelineWithStages);
+ const copyPipeline = { ...pipelineWithStages };
copyPipeline.id += 1;
mock
.onGet('twitter/flight/pipelines.json')
diff --git a/spec/frontend/pipelines/pipelines_table_row_spec.js b/spec/frontend/pipelines/pipelines_table_row_spec.js
index c43210c5350..3d564c8758c 100644
--- a/spec/frontend/pipelines/pipelines_table_row_spec.js
+++ b/spec/frontend/pipelines/pipelines_table_row_spec.js
@@ -169,7 +169,7 @@ describe('Pipelines Table Row', () => {
};
beforeEach(() => {
- const withActions = Object.assign({}, pipeline);
+ const withActions = { ...pipeline };
withActions.details.scheduled_actions = [scheduledJobAction];
withActions.flags.cancelable = true;
withActions.flags.retryable = true;
diff --git a/spec/frontend/pipelines/stage_spec.js b/spec/frontend/pipelines/stage_spec.js
index b020aaedd06..6aa041bcb7f 100644
--- a/spec/frontend/pipelines/stage_spec.js
+++ b/spec/frontend/pipelines/stage_spec.js
@@ -96,7 +96,7 @@ describe('Pipelines stage component', () => {
describe('update endpoint correctly', () => {
beforeEach(() => {
- const copyStage = Object.assign({}, stageReply);
+ const copyStage = { ...stageReply };
copyStage.latest_statuses[0].name = 'this is the updated content';
mock.onGet('bar.json').reply(200, copyStage);
createComponent({
diff --git a/spec/javascripts/pipelines/stores/pipeline_store.js b/spec/frontend/pipelines/stores/pipeline_store_spec.js
index 4a0b3bf4c02..68d438109b3 100644
--- a/spec/javascripts/pipelines/stores/pipeline_store.js
+++ b/spec/frontend/pipelines/stores/pipeline_store_spec.js
@@ -7,14 +7,12 @@ describe('EE Pipeline store', () => {
beforeEach(() => {
store = new PipelineStore();
- data = Object.assign({}, LinkedPipelines);
+ data = { ...LinkedPipelines };
+
+ store.storePipeline(data);
});
describe('storePipeline', () => {
- beforeAll(() => {
- store.storePipeline(data);
- });
-
describe('triggered_by', () => {
it('sets triggered_by as an array', () => {
expect(store.state.pipeline.triggered_by.length).toEqual(1);
@@ -50,10 +48,6 @@ describe('EE Pipeline store', () => {
});
describe('resetTriggeredByPipeline', () => {
- beforeEach(() => {
- store.storePipeline(data);
- });
-
it('closes the pipeline & nested ones', () => {
store.state.pipeline.triggered_by[0].isExpanded = true;
store.state.pipeline.triggered_by[0].triggered_by[0].isExpanded = true;
@@ -66,10 +60,6 @@ describe('EE Pipeline store', () => {
});
describe('openTriggeredByPipeline', () => {
- beforeEach(() => {
- store.storePipeline(data);
- });
-
it('opens the given pipeline', () => {
store.openTriggeredByPipeline(store.state.pipeline, store.state.pipeline.triggered_by[0]);
@@ -78,10 +68,6 @@ describe('EE Pipeline store', () => {
});
describe('closeTriggeredByPipeline', () => {
- beforeEach(() => {
- store.storePipeline(data);
- });
-
it('closes the given pipeline', () => {
// open it first
store.openTriggeredByPipeline(store.state.pipeline, store.state.pipeline.triggered_by[0]);
@@ -93,15 +79,11 @@ describe('EE Pipeline store', () => {
});
describe('resetTriggeredPipelines', () => {
- beforeEach(() => {
- store.storePipeline(data);
- });
-
it('closes the pipeline & nested ones', () => {
store.state.pipeline.triggered[0].isExpanded = true;
store.state.pipeline.triggered[0].triggered[0].isExpanded = true;
- store.resetTriggeredPipeline(store.state.pipeline, store.state.pipeline.triggered[0]);
+ store.resetTriggeredPipelines(store.state.pipeline, store.state.pipeline.triggered[0]);
expect(store.state.pipeline.triggered[0].isExpanded).toEqual(false);
expect(store.state.pipeline.triggered[0].triggered[0].isExpanded).toEqual(false);
@@ -109,10 +91,6 @@ describe('EE Pipeline store', () => {
});
describe('openTriggeredPipeline', () => {
- beforeEach(() => {
- store.storePipeline(data);
- });
-
it('opens the given pipeline', () => {
store.openTriggeredPipeline(store.state.pipeline, store.state.pipeline.triggered[0]);
@@ -121,10 +99,6 @@ describe('EE Pipeline store', () => {
});
describe('closeTriggeredPipeline', () => {
- beforeEach(() => {
- store.storePipeline(data);
- });
-
it('closes the given pipeline', () => {
// open it first
store.openTriggeredPipeline(store.state.pipeline, store.state.pipeline.triggered[0]);
@@ -136,12 +110,8 @@ describe('EE Pipeline store', () => {
});
describe('toggleLoading', () => {
- beforeEach(() => {
- store.storePipeline(data);
- });
-
it('toggles the isLoading property for the given pipeline', () => {
- store.togglePipeline(store.state.pipeline.triggered[0]);
+ store.toggleLoading(store.state.pipeline.triggered[0]);
expect(store.state.pipeline.triggered[0].isLoading).toEqual(true);
});
diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
index 9eaa563025d..a0eb93c4e6b 100644
--- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
@@ -20,7 +20,7 @@ describe('Mutations TestReports Store', () => {
describe('set endpoint', () => {
it('should set endpoint', () => {
- const expectedState = Object.assign({}, mockState, { endpoint: 'foo' });
+ const expectedState = { ...mockState, endpoint: 'foo' };
mutations[types.SET_ENDPOINT](mockState, 'foo');
expect(mockState.endpoint).toEqual(expectedState.endpoint);
@@ -47,14 +47,14 @@ describe('Mutations TestReports Store', () => {
describe('toggle loading', () => {
it('should set to true', () => {
- const expectedState = Object.assign({}, mockState, { isLoading: true });
+ const expectedState = { ...mockState, isLoading: true };
mutations[types.TOGGLE_LOADING](mockState);
expect(mockState.isLoading).toEqual(expectedState.isLoading);
});
it('should toggle back to false', () => {
- const expectedState = Object.assign({}, mockState, { isLoading: false });
+ const expectedState = { ...mockState, isLoading: false };
mockState.isLoading = true;
mutations[types.TOGGLE_LOADING](mockState);
diff --git a/spec/frontend/pipelines/test_reports/test_summary_spec.js b/spec/frontend/pipelines/test_reports/test_summary_spec.js
index 160d93d2e6b..8f041e46472 100644
--- a/spec/frontend/pipelines/test_reports/test_summary_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_summary_spec.js
@@ -82,17 +82,19 @@ describe('Test reports summary', () => {
describe('success percentage calculation', () => {
it.each`
- name | successCount | totalCount | result
- ${'displays 0 when there are no tests'} | ${0} | ${0} | ${'0'}
- ${'displays whole number when possible'} | ${10} | ${50} | ${'20'}
- ${'rounds to 0.01'} | ${1} | ${16604} | ${'0.01'}
- ${'correctly rounds to 50'} | ${8302} | ${16604} | ${'50'}
- ${'rounds down for large close numbers'} | ${16603} | ${16604} | ${'99.99'}
- ${'correctly displays 100'} | ${16604} | ${16604} | ${'100'}
- `('$name', ({ successCount, totalCount, result }) => {
+ name | successCount | totalCount | skippedCount | result
+ ${'displays 0 when there are no tests'} | ${0} | ${0} | ${0} | ${'0'}
+ ${'displays whole number when possible'} | ${10} | ${50} | ${0} | ${'20'}
+ ${'excludes skipped tests from total'} | ${10} | ${50} | ${5} | ${'22.22'}
+ ${'rounds to 0.01'} | ${1} | ${16604} | ${0} | ${'0.01'}
+ ${'correctly rounds to 50'} | ${8302} | ${16604} | ${0} | ${'50'}
+ ${'rounds down for large close numbers'} | ${16603} | ${16604} | ${0} | ${'99.99'}
+ ${'correctly displays 100'} | ${16604} | ${16604} | ${0} | ${'100'}
+ `('$name', ({ successCount, totalCount, skippedCount, result }) => {
createComponent({
report: {
success_count: successCount,
+ skipped_count: skippedCount,
total_count: totalCount,
},
});
diff --git a/spec/frontend/releases/components/app_edit_spec.js b/spec/frontend/releases/components/app_edit_spec.js
index 09bafe4aa9b..4450b047acd 100644
--- a/spec/frontend/releases/components/app_edit_spec.js
+++ b/spec/frontend/releases/components/app_edit_spec.js
@@ -1,11 +1,13 @@
import Vuex from 'vuex';
import { mount } from '@vue/test-utils';
import ReleaseEditApp from '~/releases/components/app_edit.vue';
-import { release as originalRelease } from '../mock_data';
+import { release as originalRelease, milestones as originalMilestones } from '../mock_data';
import * as commonUtils from '~/lib/utils/common_utils';
import { BACK_URL_PARAM } from '~/releases/constants';
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
import { merge } from 'lodash';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
describe('Release edit component', () => {
let wrapper;
@@ -13,6 +15,7 @@ describe('Release edit component', () => {
let actions;
let getters;
let state;
+ let mock;
const factory = ({ featureFlags = {}, store: storeUpdates = {} } = {}) => {
state = {
@@ -20,6 +23,7 @@ describe('Release edit component', () => {
markdownDocsPath: 'path/to/markdown/docs',
updateReleaseApiDocsPath: 'path/to/update/release/api/docs',
releasesPagePath: 'path/to/releases/page',
+ projectId: '8',
};
actions = {
@@ -62,8 +66,11 @@ describe('Release edit component', () => {
};
beforeEach(() => {
+ mock = new MockAdapter(axios);
gon.api_version = 'v4';
+ mock.onGet('/api/v4/projects/8/milestones').reply(200, originalMilestones);
+
release = commonUtils.convertObjectPropsToCamelCase(originalRelease, { deep: true });
});
diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js
index 231d877a25d..854f06821be 100644
--- a/spec/frontend/releases/stores/modules/detail/actions_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js
@@ -130,6 +130,15 @@ describe('Release detail actions', () => {
});
});
+ describe('updateReleaseMilestones', () => {
+ it(`commits ${types.UPDATE_RELEASE_MILESTONES} with the updated release milestones`, () => {
+ const newReleaseMilestones = ['v0.0', 'v0.1'];
+ return testAction(actions.updateReleaseMilestones, newReleaseMilestones, state, [
+ { type: types.UPDATE_RELEASE_MILESTONES, payload: newReleaseMilestones },
+ ]);
+ });
+ });
+
describe('requestUpdateRelease', () => {
it(`commits ${types.REQUEST_UPDATE_RELEASE}`, () =>
testAction(actions.requestUpdateRelease, undefined, state, [
@@ -248,6 +257,7 @@ describe('Release detail actions', () => {
{
name: state.release.name,
description: state.release.description,
+ milestones: state.release.milestones.map(milestone => milestone.title),
},
],
]);
diff --git a/spec/frontend/snippets/components/snippet_blob_view_spec.js b/spec/frontend/snippets/components/snippet_blob_view_spec.js
index 1f6038bc7f0..612ca858f05 100644
--- a/spec/frontend/snippets/components/snippet_blob_view_spec.js
+++ b/spec/frontend/snippets/components/snippet_blob_view_spec.js
@@ -84,9 +84,7 @@ describe('Blob Embeddable', () => {
});
it('sets rich viewer correctly', () => {
- const data = Object.assign({}, dataMock, {
- activeViewerType: RichViewerMock.type,
- });
+ const data = { ...dataMock, activeViewerType: RichViewerMock.type };
createComponent({}, data);
expect(wrapper.find(RichViewer).exists()).toBe(true);
});
diff --git a/spec/frontend/snippets/components/snippet_title_spec.js b/spec/frontend/snippets/components/snippet_title_spec.js
index b9b60883eb3..88261a75f6c 100644
--- a/spec/frontend/snippets/components/snippet_title_spec.js
+++ b/spec/frontend/snippets/components/snippet_title_spec.js
@@ -17,7 +17,7 @@ describe('Snippet header component', () => {
};
function createComponent({ props = snippet } = {}) {
- const defaultProps = Object.assign({}, props);
+ const defaultProps = { ...props };
wrapper = shallowMount(SnippetTitle, {
propsData: {
diff --git a/spec/frontend/tracking_spec.js b/spec/frontend/tracking_spec.js
index 1f77b8c26b9..08a26d46618 100644
--- a/spec/frontend/tracking_spec.js
+++ b/spec/frontend/tracking_spec.js
@@ -46,10 +46,11 @@ describe('Tracking', () => {
expect(snowplowSpy).not.toHaveBeenCalledWith('enableFormTracking');
expect(snowplowSpy).not.toHaveBeenCalledWith('enableLinkClickTracking');
- window.snowplowOptions = Object.assign({}, window.snowplowOptions, {
+ window.snowplowOptions = {
+ ...window.snowplowOptions,
formTracking: true,
linkClickTracking: true,
- });
+ };
initUserTracking();
expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking');
diff --git a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
index a7ecb863cfb..8a604355625 100644
--- a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
@@ -61,7 +61,7 @@ describe('Merge Request Collapsible Extension', () => {
describe('while loading', () => {
beforeEach(() => {
- mountComponent(Object.assign({}, data, { isLoading: true }));
+ mountComponent({ ...data, isLoading: true });
});
it('renders the buttons disabled', () => {
@@ -86,7 +86,7 @@ describe('Merge Request Collapsible Extension', () => {
describe('with error', () => {
beforeEach(() => {
- mountComponent(Object.assign({}, data, { hasError: true }));
+ mountComponent({ ...data, hasError: true });
});
it('does not render the buttons', () => {
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
index cda5ca68d9b..5f3a8654990 100644
--- a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
@@ -13,7 +13,7 @@ describe('MrWidgetPipelineContainer', () => {
const factory = (props = {}) => {
wrapper = mount(MrWidgetPipelineContainer, {
propsData: {
- mr: Object.assign({}, mockStore),
+ mr: { ...mockStore },
...props,
},
});
diff --git a/spec/frontend/vue_shared/components/file_row_spec.js b/spec/frontend/vue_shared/components/file_row_spec.js
index 732491378fa..46df2d2aaf1 100644
--- a/spec/frontend/vue_shared/components/file_row_spec.js
+++ b/spec/frontend/vue_shared/components/file_row_spec.js
@@ -91,9 +91,7 @@ describe('File row component', () => {
jest.spyOn(wrapper.vm, 'scrollIntoView');
wrapper.setProps({
- file: Object.assign({}, wrapper.props('file'), {
- active: true,
- }),
+ file: { ...wrapper.props('file'), active: true },
});
return nextTick().then(() => {
@@ -125,9 +123,7 @@ describe('File row component', () => {
it('matches the current route against encoded file URL', () => {
const fileName = 'with space';
- const rowFile = Object.assign({}, file(fileName), {
- url: `/${fileName}`,
- });
+ const rowFile = { ...file(fileName), url: `/${fileName}` };
const routerPath = `/project/${escapeFileUrl(fileName)}`;
createComponent(
{
diff --git a/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js b/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js
index 4c654e01f74..90c3fe54901 100644
--- a/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js
+++ b/spec/frontend/vue_shared/components/issue/issue_milestone_spec.js
@@ -36,9 +36,7 @@ describe('IssueMilestoneComponent', () => {
describe('isMilestoneStarted', () => {
it('should return `false` when milestoneStart prop is not defined', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- start_date: '',
- }),
+ milestone: { ...mockMilestone, start_date: '' },
});
expect(wrapper.vm.isMilestoneStarted).toBe(false);
@@ -46,9 +44,7 @@ describe('IssueMilestoneComponent', () => {
it('should return `true` when milestone start date is past current date', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- start_date: '1990-07-22',
- }),
+ milestone: { ...mockMilestone, start_date: '1990-07-22' },
});
expect(wrapper.vm.isMilestoneStarted).toBe(true);
@@ -58,9 +54,7 @@ describe('IssueMilestoneComponent', () => {
describe('isMilestonePastDue', () => {
it('should return `false` when milestoneDue prop is not defined', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- due_date: '',
- }),
+ milestone: { ...mockMilestone, due_date: '' },
});
expect(wrapper.vm.isMilestonePastDue).toBe(false);
@@ -68,9 +62,7 @@ describe('IssueMilestoneComponent', () => {
it('should return `true` when milestone due is past current date', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- due_date: '1990-07-22',
- }),
+ milestone: { ...mockMilestone, due_date: '1990-07-22' },
});
expect(wrapper.vm.isMilestonePastDue).toBe(true);
@@ -84,9 +76,7 @@ describe('IssueMilestoneComponent', () => {
it('returns string containing absolute milestone start date when due date is not present', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- due_date: '',
- }),
+ milestone: { ...mockMilestone, due_date: '' },
});
expect(wrapper.vm.milestoneDatesAbsolute).toBe('(January 1, 2018)');
@@ -94,10 +84,7 @@ describe('IssueMilestoneComponent', () => {
it('returns empty string when both milestone start and due dates are not present', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- start_date: '',
- due_date: '',
- }),
+ milestone: { ...mockMilestone, start_date: '', due_date: '' },
});
expect(wrapper.vm.milestoneDatesAbsolute).toBe('');
@@ -107,9 +94,7 @@ describe('IssueMilestoneComponent', () => {
describe('milestoneDatesHuman', () => {
it('returns string containing milestone due date when date is yet to be due', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- due_date: `${new Date().getFullYear() + 10}-01-01`,
- }),
+ milestone: { ...mockMilestone, due_date: `${new Date().getFullYear() + 10}-01-01` },
});
expect(wrapper.vm.milestoneDatesHuman).toContain('years remaining');
@@ -117,10 +102,7 @@ describe('IssueMilestoneComponent', () => {
it('returns string containing milestone start date when date has already started and due date is not present', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- start_date: '1990-07-22',
- due_date: '',
- }),
+ milestone: { ...mockMilestone, start_date: '1990-07-22', due_date: '' },
});
expect(wrapper.vm.milestoneDatesHuman).toContain('Started');
@@ -128,10 +110,11 @@ describe('IssueMilestoneComponent', () => {
it('returns string containing milestone start date when date is yet to start and due date is not present', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
+ milestone: {
+ ...mockMilestone,
start_date: `${new Date().getFullYear() + 10}-01-01`,
due_date: '',
- }),
+ },
});
expect(wrapper.vm.milestoneDatesHuman).toContain('Starts');
@@ -139,10 +122,7 @@ describe('IssueMilestoneComponent', () => {
it('returns empty string when milestone start and due dates are not present', () => {
wrapper.setProps({
- milestone: Object.assign({}, mockMilestone, {
- start_date: '',
- due_date: '',
- }),
+ milestone: { ...mockMilestone, start_date: '', due_date: '' },
});
expect(wrapper.vm.milestoneDatesHuman).toBe('');
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js
index e2e11c94c0d..a4121448492 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js
@@ -8,11 +8,12 @@ import {
mockLabels,
} from '../../../../../javascripts/vue_shared/components/sidebar/labels_select/mock_data';
-const componentConfig = Object.assign({}, mockConfig, {
+const componentConfig = {
+ ...mockConfig,
fieldName: 'label_id[]',
labels: mockLabels,
showExtraOptions: false,
-});
+};
const createComponent = (config = componentConfig) => {
const Component = Vue.extend(dropdownButtonComponent);
@@ -34,7 +35,7 @@ describe('DropdownButtonComponent', () => {
describe('computed', () => {
describe('dropdownToggleText', () => {
it('returns text as `Label` when `labels` prop is empty array', () => {
- const mockEmptyLabels = Object.assign({}, componentConfig, { labels: [] });
+ const mockEmptyLabels = { ...componentConfig, labels: [] };
const vmEmptyLabels = createComponent(mockEmptyLabels);
expect(vmEmptyLabels.dropdownToggleText).toBe('Label');
@@ -42,9 +43,7 @@ describe('DropdownButtonComponent', () => {
});
it('returns first label name with remaining label count when `labels` prop has more than one item', () => {
- const mockMoreLabels = Object.assign({}, componentConfig, {
- labels: mockLabels.concat(mockLabels),
- });
+ const mockMoreLabels = { ...componentConfig, labels: mockLabels.concat(mockLabels) };
const vmMoreLabels = createComponent(mockMoreLabels);
expect(vmMoreLabels.dropdownToggleText).toBe(
@@ -54,9 +53,7 @@ describe('DropdownButtonComponent', () => {
});
it('returns first label name when `labels` prop has only one item present', () => {
- const singleLabel = Object.assign({}, componentConfig, {
- labels: [mockLabels[0]],
- });
+ const singleLabel = { ...componentConfig, labels: [mockLabels[0]] };
const vmSingleLabel = createComponent(singleLabel);
expect(vmSingleLabel.dropdownToggleText).toBe(mockLabels[0].title);
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js
index 6e2363ba96f..072d8fe2fe2 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/store/actions_spec.js
@@ -15,7 +15,7 @@ describe('LabelsSelect Actions', () => {
};
beforeEach(() => {
- state = Object.assign({}, defaultState());
+ state = { ...defaultState() };
});
describe('setInitialState', () => {
diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
index 5263de117cf..19fe9fc3900 100644
--- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
+++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
@@ -9,7 +9,7 @@ describe Mutations::AlertManagement::UpdateAlertStatus do
let(:new_status) { 'acknowledged' }
let(:args) { { status: new_status, project_path: project.full_path, iid: alert.iid } }
- specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alerts) }
+ specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alert) }
describe '#resolve' do
subject(:resolve) { mutation_for(project, current_user).resolve(args) }
diff --git a/spec/graphql/types/alert_management/alert_type_spec.rb b/spec/graphql/types/alert_management/alert_type_spec.rb
index aefd88f7645..65291f31422 100644
--- a/spec/graphql/types/alert_management/alert_type_spec.rb
+++ b/spec/graphql/types/alert_management/alert_type_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe GitlabSchema.types['AlertManagementAlert'] do
specify { expect(described_class.graphql_name).to eq('AlertManagementAlert') }
- specify { expect(described_class).to require_graphql_authorizations(:read_alert_management_alerts) }
+ specify { expect(described_class).to require_graphql_authorizations(:read_alert_management_alert) }
it 'exposes the expected fields' do
expected_fields = %i[
diff --git a/spec/helpers/releases_helper_spec.rb b/spec/helpers/releases_helper_spec.rb
index 282758679cb..de4086e48db 100644
--- a/spec/helpers/releases_helper_spec.rb
+++ b/spec/helpers/releases_helper_spec.rb
@@ -54,7 +54,9 @@ describe ReleasesHelper do
markdown_docs_path
releases_page_path
update_release_api_docs_path
- release_assets_docs_path)
+ release_assets_docs_path
+ manage_milestones_path
+ new_milestone_path)
expect(helper.data_for_edit_release_page.keys).to eq(keys)
end
end
diff --git a/spec/javascripts/emoji_spec.js b/spec/javascripts/emoji_spec.js
index 3db4d9800f1..25bc95e0dd6 100644
--- a/spec/javascripts/emoji_spec.js
+++ b/spec/javascripts/emoji_spec.js
@@ -69,7 +69,7 @@ const defaults = {
};
function testGlEmojiElement(element, name, unicodeVersion, unicodeMoji, options = {}) {
- const opts = Object.assign({}, defaults, options);
+ const opts = { ...defaults, ...options };
expect(element.tagName.toLowerCase()).toBe('gl-emoji');
expect(element.dataset.name).toBe(name);
expect(element.dataset.fallbackSrc.length).toBeGreaterThan(0);
@@ -383,9 +383,7 @@ describe('gl_emoji', () => {
it('bomb(6.0) with 6.0 support', () => {
const emojiKey = 'bomb';
- const unicodeSupportMap = Object.assign({}, emptySupportMap, {
- '6.0': true,
- });
+ const unicodeSupportMap = { ...emptySupportMap, '6.0': true };
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
@@ -409,9 +407,7 @@ describe('gl_emoji', () => {
it('bomb(6.0) without 6.0 but with 9.0 support', () => {
const emojiKey = 'bomb';
- const unicodeSupportMap = Object.assign({}, emptySupportMap, {
- '9.0': true,
- });
+ const unicodeSupportMap = { ...emptySupportMap, '9.0': true };
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
@@ -423,7 +419,8 @@ describe('gl_emoji', () => {
it('construction_worker_tone5(8.0) without skin tone modifier support', () => {
const emojiKey = 'construction_worker_tone5';
- const unicodeSupportMap = Object.assign({}, emptySupportMap, {
+ const unicodeSupportMap = {
+ ...emptySupportMap,
skinToneModifier: false,
'9.0': true,
'8.0': true,
@@ -437,7 +434,7 @@ describe('gl_emoji', () => {
3.2: true,
'3.0': true,
1.1: true,
- });
+ };
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
@@ -449,13 +446,14 @@ describe('gl_emoji', () => {
it('use native keycap on >=57 chrome', () => {
const emojiKey = 'five';
- const unicodeSupportMap = Object.assign({}, emptySupportMap, {
+ const unicodeSupportMap = {
+ ...emptySupportMap,
'3.0': true,
meta: {
isChrome: true,
chromeVersion: 57,
},
- });
+ };
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
@@ -467,13 +465,14 @@ describe('gl_emoji', () => {
it('fallback keycap on <57 chrome', () => {
const emojiKey = 'five';
- const unicodeSupportMap = Object.assign({}, emptySupportMap, {
+ const unicodeSupportMap = {
+ ...emptySupportMap,
'3.0': true,
meta: {
isChrome: true,
chromeVersion: 50,
},
- });
+ };
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index 00bc552bd7d..06f76c581f2 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -44,19 +44,17 @@ describe('glDropdown', function describeDropdown() {
};
function initDropDown(hasRemote, isFilterable, extraOpts = {}) {
- const options = Object.assign(
- {
- selectable: true,
- filterable: isFilterable,
- data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData,
- search: {
- fields: ['name'],
- },
- text: project => project.name_with_namespace || project.name,
- id: project => project.id,
+ const options = {
+ selectable: true,
+ filterable: isFilterable,
+ data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData,
+ search: {
+ fields: ['name'],
},
- extraOpts,
- );
+ text: project => project.name_with_namespace || project.name,
+ id: project => project.id,
+ ...extraOpts,
+ };
this.dropdownButtonElement = $(
'#js-project-dropdown',
this.dropdownContainerElement,
diff --git a/spec/javascripts/image_diff/image_badge_spec.js b/spec/javascripts/image_diff/image_badge_spec.js
index 2b23dce5d30..a1589d7b7a0 100644
--- a/spec/javascripts/image_diff/image_badge_spec.js
+++ b/spec/javascripts/image_diff/image_badge_spec.js
@@ -10,11 +10,7 @@ describe('ImageBadge', () => {
};
it('should save actual property', () => {
- const imageBadge = new ImageBadge(
- Object.assign({}, options, {
- actual: imageMeta,
- }),
- );
+ const imageBadge = new ImageBadge({ ...options, actual: imageMeta });
const { actual } = imageBadge;
@@ -25,11 +21,7 @@ describe('ImageBadge', () => {
});
it('should save browser property', () => {
- const imageBadge = new ImageBadge(
- Object.assign({}, options, {
- browser: imageMeta,
- }),
- );
+ const imageBadge = new ImageBadge({ ...options, browser: imageMeta });
const { browser } = imageBadge;
@@ -83,11 +75,7 @@ describe('ImageBadge', () => {
});
it('should generate browser property', () => {
- const imageBadge = new ImageBadge(
- Object.assign({}, options, {
- imageEl: document.createElement('img'),
- }),
- );
+ const imageBadge = new ImageBadge({ ...options, imageEl: document.createElement('img') });
expect(imageDiffHelper.resizeCoordinatesToImageElement).toHaveBeenCalled();
expect(imageBadge.browser).toEqual(true);
diff --git a/spec/javascripts/jobs/components/commit_block_spec.js b/spec/javascripts/jobs/components/commit_block_spec.js
index c02f564d01a..4e2d0053831 100644
--- a/spec/javascripts/jobs/components/commit_block_spec.js
+++ b/spec/javascripts/jobs/components/commit_block_spec.js
@@ -66,7 +66,7 @@ describe('Commit block', () => {
describe('without merge request', () => {
it('does not render merge request', () => {
- const copyProps = Object.assign({}, props);
+ const copyProps = { ...props };
delete copyProps.mergeRequest;
vm = mountComponent(Component, {
diff --git a/spec/javascripts/jobs/components/sidebar_spec.js b/spec/javascripts/jobs/components/sidebar_spec.js
index 740bc3d0491..0c8e2dc3aef 100644
--- a/spec/javascripts/jobs/components/sidebar_spec.js
+++ b/spec/javascripts/jobs/components/sidebar_spec.js
@@ -20,7 +20,7 @@ describe('Sidebar details block', () => {
describe('when there is no retry path retry', () => {
it('should not render a retry button', () => {
- const copy = Object.assign({}, job);
+ const copy = { ...job };
delete copy.retry_path;
store.dispatch('receiveJobSuccess', copy);
@@ -43,10 +43,7 @@ describe('Sidebar details block', () => {
describe('with terminal path', () => {
it('renders terminal link', () => {
- store.dispatch(
- 'receiveJobSuccess',
- Object.assign({}, job, { terminal_path: 'job/43123/terminal' }),
- );
+ store.dispatch('receiveJobSuccess', { ...job, terminal_path: 'job/43123/terminal' });
vm = mountComponentWithStore(SidebarComponent, {
store,
});
diff --git a/spec/javascripts/pipelines/mock_data.js b/spec/javascripts/pipelines/mock_data.js
deleted file mode 100644
index f876987cd88..00000000000
--- a/spec/javascripts/pipelines/mock_data.js
+++ /dev/null
@@ -1,423 +0,0 @@
-export const pipelineWithStages = {
- id: 20333396,
- user: {
- id: 128633,
- name: 'Rémy Coutable',
- username: 'rymai',
- state: 'active',
- avatar_url:
- 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon',
- web_url: 'https://gitlab.com/rymai',
- path: '/rymai',
- },
- active: true,
- coverage: '58.24',
- source: 'push',
- created_at: '2018-04-11T14:04:53.881Z',
- updated_at: '2018-04-11T14:05:00.792Z',
- path: '/gitlab-org/gitlab/pipelines/20333396',
- flags: {
- latest: true,
- stuck: false,
- auto_devops: false,
- yaml_errors: false,
- retryable: false,
- cancelable: true,
- failure_reason: false,
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab/pipelines/20333396',
- favicon:
- 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico',
- },
- duration: null,
- finished_at: null,
- stages: [
- {
- name: 'build',
- title: 'build: skipped',
- status: {
- icon: 'status_skipped',
- text: 'skipped',
- label: 'skipped',
- group: 'skipped',
- has_details: true,
- details_path: '/gitlab-org/gitlab/pipelines/20333396#build',
- favicon:
- 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_skipped-a2eee568a5bffdb494050c7b62dde241de9189280836288ac8923d369f16222d.ico',
- },
- path: '/gitlab-org/gitlab/pipelines/20333396#build',
- dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=build',
- },
- {
- name: 'prepare',
- title: 'prepare: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- has_details: true,
- details_path: '/gitlab-org/gitlab/pipelines/20333396#prepare',
- favicon:
- 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_success-26f59841becbef8c6fe414e9e74471d8bfd6a91b5855c19fe7f5923a40a7da47.ico',
- },
- path: '/gitlab-org/gitlab/pipelines/20333396#prepare',
- dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=prepare',
- },
- {
- name: 'test',
- title: 'test: running',
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab/pipelines/20333396#test',
- favicon:
- 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_running-2eb56be2871937954b2ba6d6f4ee9fdf7e5e1c146ac45f7be98119ccaca1aca9.ico',
- },
- path: '/gitlab-org/gitlab/pipelines/20333396#test',
- dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=test',
- },
- {
- name: 'post-test',
- title: 'post-test: created',
- status: {
- icon: 'status_created',
- text: 'created',
- label: 'created',
- group: 'created',
- has_details: true,
- details_path: '/gitlab-org/gitlab/pipelines/20333396#post-test',
- favicon:
- 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico',
- },
- path: '/gitlab-org/gitlab/pipelines/20333396#post-test',
- dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-test',
- },
- {
- name: 'pages',
- title: 'pages: created',
- status: {
- icon: 'status_created',
- text: 'created',
- label: 'created',
- group: 'created',
- has_details: true,
- details_path: '/gitlab-org/gitlab/pipelines/20333396#pages',
- favicon:
- 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico',
- },
- path: '/gitlab-org/gitlab/pipelines/20333396#pages',
- dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=pages',
- },
- {
- name: 'post-cleanup',
- title: 'post-cleanup: created',
- status: {
- icon: 'status_created',
- text: 'created',
- label: 'created',
- group: 'created',
- has_details: true,
- details_path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup',
- favicon:
- 'https://assets.gitlab-static.net/assets/ci_favicons/favicon_status_created-e997aa0b7db73165df8a9d6803932b18d7b7cc37d604d2d96e378fea2dba9c5f.ico',
- },
- path: '/gitlab-org/gitlab/pipelines/20333396#post-cleanup',
- dropdown_path: '/gitlab-org/gitlab/pipelines/20333396/stage.json?stage=post-cleanup',
- },
- ],
- artifacts: [
- {
- name: 'gitlab:assets:compile',
- expired: false,
- expire_at: '2018-05-12T14:22:54.730Z',
- path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411438/artifacts/browse',
- },
- {
- name: 'rspec-mysql 12 28',
- expired: false,
- expire_at: '2018-05-12T14:22:45.136Z',
- path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411397/artifacts/browse',
- },
- {
- name: 'rspec-mysql 6 28',
- expired: false,
- expire_at: '2018-05-12T14:22:41.523Z',
- path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411391/artifacts/browse',
- },
- {
- name: 'rspec-pg geo 0 1',
- expired: false,
- expire_at: '2018-05-12T14:22:13.287Z',
- path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411353/artifacts/browse',
- },
- {
- name: 'rspec-mysql 0 28',
- expired: false,
- expire_at: '2018-05-12T14:22:06.834Z',
- path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411385/artifacts/browse',
- },
- {
- name: 'spinach-mysql 0 2',
- expired: false,
- expire_at: '2018-05-12T14:21:51.409Z',
- path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411423/artifacts/browse',
- },
- {
- name: 'karma',
- expired: false,
- expire_at: '2018-05-12T14:21:20.934Z',
- path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411440/artifacts/browse',
- },
- {
- name: 'spinach-pg 0 2',
- expired: false,
- expire_at: '2018-05-12T14:20:01.028Z',
- path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411419/artifacts/browse',
- },
- {
- name: 'spinach-pg 1 2',
- expired: false,
- expire_at: '2018-05-12T14:19:04.336Z',
- path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411421/artifacts/browse',
- },
- {
- name: 'sast',
- expired: null,
- expire_at: null,
- path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/download',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411442/artifacts/browse',
- },
- {
- name: 'code_quality',
- expired: false,
- expire_at: '2018-04-18T14:16:24.484Z',
- path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411441/artifacts/browse',
- },
- {
- name: 'cache gems',
- expired: null,
- expire_at: null,
- path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/download',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411447/artifacts/browse',
- },
- {
- name: 'dependency_scanning',
- expired: null,
- expire_at: null,
- path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/download',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411443/artifacts/browse',
- },
- {
- name: 'compile-assets',
- expired: false,
- expire_at: '2018-04-18T14:12:07.638Z',
- path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411334/artifacts/browse',
- },
- {
- name: 'setup-test-env',
- expired: false,
- expire_at: '2018-04-18T14:10:27.024Z',
- path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411336/artifacts/browse',
- },
- {
- name: 'retrieve-tests-metadata',
- expired: false,
- expire_at: '2018-05-12T14:06:35.926Z',
- path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/download',
- keep_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/keep',
- browse_path: '/gitlab-org/gitlab/-/jobs/62411333/artifacts/browse',
- },
- ],
- manual_actions: [
- {
- name: 'package-and-qa',
- path: '/gitlab-org/gitlab/-/jobs/62411330/play',
- playable: true,
- },
- {
- name: 'review-docs-deploy',
- path: '/gitlab-org/gitlab/-/jobs/62411332/play',
- playable: true,
- },
- ],
- },
- ref: {
- name: 'master',
- path: '/gitlab-org/gitlab/commits/master',
- tag: false,
- branch: true,
- },
- commit: {
- id: 'e6a2885c503825792cb8a84a8731295e361bd059',
- short_id: 'e6a2885c',
- title: "Merge branch 'ce-to-ee-2018-04-11' into 'master'",
- created_at: '2018-04-11T14:04:39.000Z',
- parent_ids: [
- '5d9b5118f6055f72cff1a82b88133609912f2c1d',
- '6fdc6ee76a8062fe41b1a33f7c503334a6ebdc02',
- ],
- message:
- "Merge branch 'ce-to-ee-2018-04-11' into 'master'\n\nCE upstream - 2018-04-11 12:26 UTC\n\nSee merge request gitlab-org/gitlab-ee!5326",
- author_name: 'Rémy Coutable',
- author_email: 'remy@rymai.me',
- authored_date: '2018-04-11T14:04:39.000Z',
- committer_name: 'Rémy Coutable',
- committer_email: 'remy@rymai.me',
- committed_date: '2018-04-11T14:04:39.000Z',
- author: {
- id: 128633,
- name: 'Rémy Coutable',
- username: 'rymai',
- state: 'active',
- avatar_url:
- 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon',
- web_url: 'https://gitlab.com/rymai',
- path: '/rymai',
- },
- author_gravatar_url:
- 'https://secure.gravatar.com/avatar/263da227929cc0035cb0eba512bcf81a?s=80\u0026d=identicon',
- commit_url:
- 'https://gitlab.com/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059',
- commit_path: '/gitlab-org/gitlab/commit/e6a2885c503825792cb8a84a8731295e361bd059',
- },
- cancel_path: '/gitlab-org/gitlab/pipelines/20333396/cancel',
- triggered_by: null,
- triggered: [],
-};
-
-export const stageReply = {
- name: 'deploy',
- title: 'deploy: running',
- latest_statuses: [
- {
- id: 928,
- name: 'stop staging',
- started: false,
- build_path: '/twitter/flight/-/jobs/928',
- cancel_path: '/twitter/flight/-/jobs/928/cancel',
- playable: false,
- created_at: '2018-04-04T20:02:02.728Z',
- updated_at: '2018-04-04T20:02:02.766Z',
- status: {
- icon: 'status_pending',
- text: 'pending',
- label: 'pending',
- group: 'pending',
- tooltip: 'pending',
- has_details: true,
- details_path: '/twitter/flight/-/jobs/928',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_pending-db32e1faf94b9f89530ac519790920d1f18ea8f6af6cd2e0a26cd6840cacf101.ico',
- action: {
- icon: 'cancel',
- title: 'Cancel',
- path: '/twitter/flight/-/jobs/928/cancel',
- method: 'post',
- },
- },
- },
- {
- id: 926,
- name: 'production',
- started: false,
- build_path: '/twitter/flight/-/jobs/926',
- retry_path: '/twitter/flight/-/jobs/926/retry',
- play_path: '/twitter/flight/-/jobs/926/play',
- playable: true,
- created_at: '2018-04-04T20:00:57.202Z',
- updated_at: '2018-04-04T20:11:13.110Z',
- status: {
- icon: 'status_canceled',
- text: 'canceled',
- label: 'manual play action',
- group: 'canceled',
- tooltip: 'canceled',
- has_details: true,
- details_path: '/twitter/flight/-/jobs/926',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_canceled-5491840b9b6feafba0bc599cbd49ee9580321dc809683856cf1b0d51532b1af6.ico',
- action: {
- icon: 'play',
- title: 'Play',
- path: '/twitter/flight/-/jobs/926/play',
- method: 'post',
- },
- },
- },
- {
- id: 217,
- name: 'staging',
- started: '2018-03-07T08:41:46.234Z',
- build_path: '/twitter/flight/-/jobs/217',
- retry_path: '/twitter/flight/-/jobs/217/retry',
- playable: false,
- created_at: '2018-03-07T14:41:58.093Z',
- updated_at: '2018-03-07T14:41:58.093Z',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/twitter/flight/-/jobs/217',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/twitter/flight/-/jobs/217/retry',
- method: 'post',
- },
- },
- },
- ],
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- tooltip: 'running',
- has_details: true,
- details_path: '/twitter/flight/pipelines/13#deploy',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- path: '/twitter/flight/pipelines/13#deploy',
- dropdown_path: '/twitter/flight/pipelines/13/stage.json?stage=deploy',
-};
diff --git a/spec/javascripts/pipelines/stores/pipeline.json b/spec/javascripts/pipelines/stores/pipeline.json
deleted file mode 100644
index 7d5891d3d52..00000000000
--- a/spec/javascripts/pipelines/stores/pipeline.json
+++ /dev/null
@@ -1,167 +0,0 @@
-{
- "id": 37232567,
- "user": {
- "id": 113870,
- "name": "Phil Hughes",
- "username": "iamphill",
- "state": "active",
- "avatar_url": "https://secure.gravatar.com/avatar/533a51534470a11062df393543eab649?s=80\u0026d=identicon",
- "web_url": "https://gitlab.com/iamphill",
- "status_tooltip_html": null,
- "path": "/iamphill"
- },
- "active": false,
- "coverage": null,
- "source": "push",
- "created_at": "2018-11-20T10:22:52.617Z",
- "updated_at": "2018-11-20T10:24:09.511Z",
- "path": "/gitlab-org/gl-vue-cli/pipelines/37232567",
- "flags": {
- "latest": true,
- "stuck": false,
- "auto_devops": false,
- "yaml_errors": false,
- "retryable": false,
- "cancelable": false,
- "failure_reason": false
- },
- "details": {
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gl-vue-cli/pipelines/37232567",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "duration": 65,
- "finished_at": "2018-11-20T10:24:09.483Z",
- "stages": [
- {
- "name": "test",
- "title": "test: passed",
- "groups": [
- {
- "name": "eslint",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gl-vue-cli/-/jobs/122845352/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 122845352,
- "name": "eslint",
- "started": "2018-11-20T10:22:53.369Z",
- "archived": false,
- "build_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352",
- "retry_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-11-20T10:22:52.630Z",
- "updated_at": "2018-11-20T10:23:58.948Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gl-vue-cli/-/jobs/122845352",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gl-vue-cli/-/jobs/122845352/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gl-vue-cli/pipelines/37232567#test",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gl-vue-cli/pipelines/37232567#test",
- "dropdown_path": "/gitlab-org/gl-vue-cli/pipelines/37232567/stage.json?stage=test"
- }
- ],
- "artifacts": [],
- "manual_actions": [],
- "scheduled_actions": []
- },
- "ref": {
- "name": "master",
- "path": "/gitlab-org/gl-vue-cli/commits/master",
- "tag": false,
- "branch": true
- },
- "commit": {
- "id": "8f179601d481950bcb67032caeb33d1c24dde6bd",
- "short_id": "8f179601",
- "title": "Merge branch 'gl-cli-startt' into 'master'",
- "created_at": "2018-11-20T10:22:51.000Z",
- "parent_ids": [
- "781d78fcd3d6c17ccf208f0cf0ab47c3e5397118",
- "d227a0bb858c48eeee7393fcade1a33748f35183"
- ],
- "message": "Merge branch 'gl-cli-startt' into 'master'\n\nFirst iteration of the CLI\n\nCloses gitlab-foss#53657\n\nSee merge request gitlab-org/gl-vue-cli!2",
- "author_name": "Phil Hughes",
- "author_email": "me@iamphill.com",
- "authored_date": "2018-11-20T10:22:51.000Z",
- "committer_name": "Phil Hughes",
- "committer_email": "me@iamphill.com",
- "committed_date": "2018-11-20T10:22:51.000Z",
- "author": {
- "id": 113870,
- "name": "Phil Hughes",
- "username": "iamphill",
- "state": "active",
- "avatar_url": "https://secure.gravatar.com/avatar/533a51534470a11062df393543eab649?s=80\u0026d=identicon",
- "web_url": "https://gitlab.com/iamphill",
- "status_tooltip_html": null,
- "path": "/iamphill"
- },
- "author_gravatar_url": "https://secure.gravatar.com/avatar/533a51534470a11062df393543eab649?s=80\u0026d=identicon",
- "commit_url": "https://gitlab.com/gitlab-org/gl-vue-cli/commit/8f179601d481950bcb67032caeb33d1c24dde6bd",
- "commit_path": "/gitlab-org/gl-vue-cli/commit/8f179601d481950bcb67032caeb33d1c24dde6bd"
- },
- "triggered_by": null,
- "triggered": []
-}
diff --git a/spec/javascripts/pipelines/stores/pipeline_with_triggered.json b/spec/javascripts/pipelines/stores/pipeline_with_triggered.json
deleted file mode 100644
index 1fa15e45792..00000000000
--- a/spec/javascripts/pipelines/stores/pipeline_with_triggered.json
+++ /dev/null
@@ -1,381 +0,0 @@
-{
- "id": 23211253,
- "user": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": "\u003cspan class=\"user-status-emoji has-tooltip\" title=\"\" data-html=\"true\" data-placement=\"top\"\u003e\u003cgl-emoji title=\"trumpet\" data-name=\"trumpet\" data-unicode-version=\"6.0\"\u003e🎺\u003c/gl-emoji\u003e\u003c/span\u003e",
- "path": "/axil"
- },
- "active": false,
- "coverage": null,
- "source": "push",
- "created_at": "2018-06-05T11:31:30.452Z",
- "updated_at": "2018-10-31T16:35:31.305Z",
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "flags": {
- "latest": false,
- "stuck": false,
- "auto_devops": false,
- "yaml_errors": false,
- "retryable": false,
- "cancelable": false,
- "failure_reason": false
- },
- "details": {
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "duration": 53,
- "finished_at": "2018-10-31T16:35:31.299Z",
- "stages": [
- {
- "name": "prebuild",
- "title": "prebuild: passed",
- "groups": [
- {
- "name": "review-docs-deploy",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Trigger this manual action"
- }
- },
- "jobs": [
- {
- "id": 72469032,
- "name": "review-docs-deploy",
- "started": "2018-10-31T16:34:58.778Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.495Z",
- "updated_at": "2018-10-31T16:35:31.251Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Trigger this manual action"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild"
- },
- {
- "name": "test",
- "title": "test: passed",
- "groups": [
- {
- "name": "docs check links",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 72469033,
- "name": "docs check links",
- "started": "2018-06-05T11:31:33.240Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.627Z",
- "updated_at": "2018-06-05T11:31:54.363Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test"
- },
- {
- "name": "cleanup",
- "title": "cleanup: skipped",
- "groups": [
- {
- "name": "review-docs-cleanup",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- },
- "jobs": [
- {
- "id": 72469034,
- "name": "review-docs-cleanup",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.760Z",
- "updated_at": "2018-06-05T11:31:56.037Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup"
- }
- ],
- "artifacts": [],
- "manual_actions": [
- {
- "name": "review-docs-cleanup",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review-docs-deploy",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false
- }
- ],
- "scheduled_actions": []
- },
- "ref": {
- "name": "docs/add-development-guide-to-readme",
- "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme",
- "tag": false,
- "branch": true
- },
- "commit": {
- "id": "8083eb0a920572214d0dccedd7981f05d535ad46",
- "short_id": "8083eb0a",
- "title": "Add link to development guide in readme",
- "created_at": "2018-06-05T11:30:48.000Z",
- "parent_ids": ["1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"],
- "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n",
- "author_name": "Achilleas Pipinellis",
- "author_email": "axil@gitlab.com",
- "authored_date": "2018-06-05T11:30:48.000Z",
- "committer_name": "Achilleas Pipinellis",
- "committer_email": "axil@gitlab.com",
- "committed_date": "2018-06-05T11:30:48.000Z",
- "author": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": null,
- "path": "/axil"
- },
- "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80\u0026d=identicon",
- "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46",
- "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46"
- },
- "triggered_by": null,
- "triggered": [
- {
- "id": 34993051,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- }
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- }
- }
- ]
-}
diff --git a/spec/javascripts/pipelines/stores/pipeline_with_triggered_by.json b/spec/javascripts/pipelines/stores/pipeline_with_triggered_by.json
deleted file mode 100644
index 7aeea6f3ebb..00000000000
--- a/spec/javascripts/pipelines/stores/pipeline_with_triggered_by.json
+++ /dev/null
@@ -1,379 +0,0 @@
-{
- "id": 23211253,
- "user": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": "\u003cspan class=\"user-status-emoji has-tooltip\" title=\"\" data-html=\"true\" data-placement=\"top\"\u003e\u003cgl-emoji title=\"trumpet\" data-name=\"trumpet\" data-unicode-version=\"6.0\"\u003e🎺\u003c/gl-emoji\u003e\u003c/span\u003e",
- "path": "/axil"
- },
- "active": false,
- "coverage": null,
- "source": "push",
- "created_at": "2018-06-05T11:31:30.452Z",
- "updated_at": "2018-10-31T16:35:31.305Z",
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "flags": {
- "latest": false,
- "stuck": false,
- "auto_devops": false,
- "yaml_errors": false,
- "retryable": false,
- "cancelable": false,
- "failure_reason": false
- },
- "details": {
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "duration": 53,
- "finished_at": "2018-10-31T16:35:31.299Z",
- "stages": [
- {
- "name": "prebuild",
- "title": "prebuild: passed",
- "groups": [
- {
- "name": "review-docs-deploy",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Trigger this manual action"
- }
- },
- "jobs": [
- {
- "id": 72469032,
- "name": "review-docs-deploy",
- "started": "2018-10-31T16:34:58.778Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.495Z",
- "updated_at": "2018-10-31T16:35:31.251Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Trigger this manual action"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild"
- },
- {
- "name": "test",
- "title": "test: passed",
- "groups": [
- {
- "name": "docs check links",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 72469033,
- "name": "docs check links",
- "started": "2018-06-05T11:31:33.240Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.627Z",
- "updated_at": "2018-06-05T11:31:54.363Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test"
- },
- {
- "name": "cleanup",
- "title": "cleanup: skipped",
- "groups": [
- {
- "name": "review-docs-cleanup",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- },
- "jobs": [
- {
- "id": 72469034,
- "name": "review-docs-cleanup",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.760Z",
- "updated_at": "2018-06-05T11:31:56.037Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup"
- }
- ],
- "artifacts": [],
- "manual_actions": [
- {
- "name": "review-docs-cleanup",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review-docs-deploy",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false
- }
- ],
- "scheduled_actions": []
- },
- "ref": {
- "name": "docs/add-development-guide-to-readme",
- "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme",
- "tag": false,
- "branch": true
- },
- "commit": {
- "id": "8083eb0a920572214d0dccedd7981f05d535ad46",
- "short_id": "8083eb0a",
- "title": "Add link to development guide in readme",
- "created_at": "2018-06-05T11:30:48.000Z",
- "parent_ids": ["1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"],
- "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n",
- "author_name": "Achilleas Pipinellis",
- "author_email": "axil@gitlab.com",
- "authored_date": "2018-06-05T11:30:48.000Z",
- "committer_name": "Achilleas Pipinellis",
- "committer_email": "axil@gitlab.com",
- "committed_date": "2018-06-05T11:30:48.000Z",
- "author": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": null,
- "path": "/axil"
- },
- "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80\u0026d=identicon",
- "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46",
- "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46"
- },
- "triggered_by": {
- "id": 34993051,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- }
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- }
- },
- "triggered": []
-}
diff --git a/spec/javascripts/pipelines/stores/pipeline_with_triggered_triggered_by.json b/spec/javascripts/pipelines/stores/pipeline_with_triggered_triggered_by.json
deleted file mode 100644
index 2402cbae6c8..00000000000
--- a/spec/javascripts/pipelines/stores/pipeline_with_triggered_triggered_by.json
+++ /dev/null
@@ -1,452 +0,0 @@
-{
- "id": 23211253,
- "user": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": "\u003cspan class=\"user-status-emoji has-tooltip\" title=\"\" data-html=\"true\" data-placement=\"top\"\u003e\u003cgl-emoji title=\"trumpet\" data-name=\"trumpet\" data-unicode-version=\"6.0\"\u003e🎺\u003c/gl-emoji\u003e\u003c/span\u003e",
- "path": "/axil"
- },
- "active": false,
- "coverage": null,
- "source": "push",
- "created_at": "2018-06-05T11:31:30.452Z",
- "updated_at": "2018-10-31T16:35:31.305Z",
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "flags": {
- "latest": false,
- "stuck": false,
- "auto_devops": false,
- "yaml_errors": false,
- "retryable": false,
- "cancelable": false,
- "failure_reason": false
- },
- "details": {
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "duration": 53,
- "finished_at": "2018-10-31T16:35:31.299Z",
- "stages": [
- {
- "name": "prebuild",
- "title": "prebuild: passed",
- "groups": [
- {
- "name": "review-docs-deploy",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Trigger this manual action"
- }
- },
- "jobs": [
- {
- "id": 72469032,
- "name": "review-docs-deploy",
- "started": "2018-10-31T16:34:58.778Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.495Z",
- "updated_at": "2018-10-31T16:35:31.251Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Trigger this manual action"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild"
- },
- {
- "name": "test",
- "title": "test: passed",
- "groups": [
- {
- "name": "docs check links",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 72469033,
- "name": "docs check links",
- "started": "2018-06-05T11:31:33.240Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.627Z",
- "updated_at": "2018-06-05T11:31:54.363Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test"
- },
- {
- "name": "cleanup",
- "title": "cleanup: skipped",
- "groups": [
- {
- "name": "review-docs-cleanup",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- },
- "jobs": [
- {
- "id": 72469034,
- "name": "review-docs-cleanup",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.760Z",
- "updated_at": "2018-06-05T11:31:56.037Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup"
- }
- ],
- "artifacts": [],
- "manual_actions": [
- {
- "name": "review-docs-cleanup",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review-docs-deploy",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false
- }
- ],
- "scheduled_actions": []
- },
- "ref": {
- "name": "docs/add-development-guide-to-readme",
- "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme",
- "tag": false,
- "branch": true
- },
- "commit": {
- "id": "8083eb0a920572214d0dccedd7981f05d535ad46",
- "short_id": "8083eb0a",
- "title": "Add link to development guide in readme",
- "created_at": "2018-06-05T11:30:48.000Z",
- "parent_ids": ["1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"],
- "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n",
- "author_name": "Achilleas Pipinellis",
- "author_email": "axil@gitlab.com",
- "authored_date": "2018-06-05T11:30:48.000Z",
- "committer_name": "Achilleas Pipinellis",
- "committer_email": "axil@gitlab.com",
- "committed_date": "2018-06-05T11:30:48.000Z",
- "author": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": null,
- "path": "/axil"
- },
- "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80\u0026d=identicon",
- "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46",
- "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46"
- },
- "triggered_by": {
- "id": 34993051,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- }
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- }
- },
- "triggered": [
- {
- "id": 349233051,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/349233051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- }
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- }
- },
- {
- "id": 34993023,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993023",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- }
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- }
- }
- ]
-}
diff --git a/spec/javascripts/reports/components/test_issue_body_spec.js b/spec/javascripts/reports/components/test_issue_body_spec.js
index 9c1cec4c9bc..a55719a9d36 100644
--- a/spec/javascripts/reports/components/test_issue_body_spec.js
+++ b/spec/javascripts/reports/components/test_issue_body_spec.js
@@ -40,7 +40,7 @@ describe('Test Issue body', () => {
beforeEach(() => {
vm = mountComponentWithStore(Component, {
store,
- props: Object.assign({}, commonProps, { isNew: true }),
+ props: { ...commonProps, isNew: true },
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
index de1d351677c..3cbaa47c832 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
@@ -153,7 +153,7 @@ describe('MRWidgetHeader', () => {
beforeEach(() => {
vm = mountComponent(Component, {
- mr: Object.assign({}, mrDefaultOptions),
+ mr: { ...mrDefaultOptions },
});
});
@@ -176,7 +176,7 @@ describe('MRWidgetHeader', () => {
});
it('renders web ide button in disabled state with no href', () => {
- const mr = Object.assign({}, mrDefaultOptions, { canPushToSourceBranch: false });
+ const mr = { ...mrDefaultOptions, canPushToSourceBranch: false };
vm = mountComponent(Component, { mr });
const link = vm.$el.querySelector('.js-web-ide');
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
index 6a83790093a..a8acecdd3fc 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
@@ -30,11 +30,7 @@ describe('DiffViewer', () => {
relative_url_root: '',
};
- createComponent(
- Object.assign({}, requiredProps, {
- projectPath: '',
- }),
- );
+ createComponent({ ...requiredProps, projectPath: '' });
setTimeout(() => {
expect(vm.$el.querySelector('.deleted img').getAttribute('src')).toBe(
@@ -50,13 +46,12 @@ describe('DiffViewer', () => {
});
it('renders fallback download diff display', done => {
- createComponent(
- Object.assign({}, requiredProps, {
- diffViewerMode: 'added',
- newPath: 'test.abc',
- oldPath: 'testold.abc',
- }),
- );
+ createComponent({
+ ...requiredProps,
+ diffViewerMode: 'added',
+ newPath: 'test.abc',
+ oldPath: 'testold.abc',
+ });
setTimeout(() => {
expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain(
@@ -77,28 +72,26 @@ describe('DiffViewer', () => {
});
it('renders renamed component', () => {
- createComponent(
- Object.assign({}, requiredProps, {
- diffMode: 'renamed',
- diffViewerMode: 'renamed',
- newPath: 'test.abc',
- oldPath: 'testold.abc',
- }),
- );
+ createComponent({
+ ...requiredProps,
+ diffMode: 'renamed',
+ diffViewerMode: 'renamed',
+ newPath: 'test.abc',
+ oldPath: 'testold.abc',
+ });
expect(vm.$el.textContent).toContain('File moved');
});
it('renders mode changed component', () => {
- createComponent(
- Object.assign({}, requiredProps, {
- diffMode: 'mode_changed',
- newPath: 'test.abc',
- oldPath: 'testold.abc',
- aMode: '123',
- bMode: '321',
- }),
- );
+ createComponent({
+ ...requiredProps,
+ diffMode: 'mode_changed',
+ newPath: 'test.abc',
+ oldPath: 'testold.abc',
+ aMode: '123',
+ bMode: '321',
+ });
expect(vm.$el.textContent).toContain('File mode changed from 123 to 321');
});
diff --git a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
index 288eb40cc76..a87998aa72f 100644
--- a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js
@@ -25,12 +25,7 @@ describe('toolbar', () => {
describe('user cannot attach file', () => {
beforeEach(() => {
- vm = mountComponent(
- Toolbar,
- Object.assign({}, props, {
- canAttachFile: false,
- }),
- );
+ vm = mountComponent(Toolbar, { ...props, canAttachFile: false });
});
it('should not render uploading-container', () => {
diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
index c062ee13231..3e044f47a3f 100644
--- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
+++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
@@ -5,18 +5,15 @@ import stackedProgressBarComponent from '~/vue_shared/components/stacked_progres
const createComponent = config => {
const Component = Vue.extend(stackedProgressBarComponent);
- const defaultConfig = Object.assign(
- {},
- {
- successLabel: 'Synced',
- failureLabel: 'Failed',
- neutralLabel: 'Out of sync',
- successCount: 25,
- failureCount: 10,
- totalCount: 5000,
- },
- config,
- );
+ const defaultConfig = {
+ successLabel: 'Synced',
+ failureLabel: 'Failed',
+ neutralLabel: 'Out of sync',
+ successCount: 25,
+ failureCount: 10,
+ totalCount: 5000,
+ ...config,
+ };
return mountComponent(Component, defaultConfig);
};
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index d206d31eb96..c98fde543a1 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -45,22 +45,36 @@ describe Gitlab::ProjectSearchResults do
expect(results.formatted_count(scope)).to eq(expected)
end
end
+
+ context 'blobs' do
+ it "limits the search to #{described_class::COUNT_LIMIT} items" do
+ expect(results).to receive(:blobs).with(limit: described_class::COUNT_LIMIT).and_call_original
+ expect(results.formatted_count('blobs')).to eq('0')
+ end
+ end
+
+ context 'wiki_blobs' do
+ it "limits the search to #{described_class::COUNT_LIMIT} items" do
+ expect(results).to receive(:wiki_blobs).with(limit: described_class::COUNT_LIMIT).and_call_original
+ expect(results.formatted_count('wiki_blobs')).to eq('0')
+ end
+ end
end
- shared_examples 'general blob search' do |entity_type, blob_kind|
+ shared_examples 'general blob search' do |entity_type, blob_type|
let(:query) { 'files' }
subject(:results) { described_class.new(user, project, query).objects(blob_type) }
context "when #{entity_type} is disabled" do
let(:project) { disabled_project }
- it "hides #{blob_kind} from members" do
+ it "hides #{blob_type} from members" do
project.add_reporter(user)
is_expected.to be_empty
end
- it "hides #{blob_kind} from non-members" do
+ it "hides #{blob_type} from non-members" do
is_expected.to be_empty
end
end
@@ -68,13 +82,13 @@ describe Gitlab::ProjectSearchResults do
context "when #{entity_type} is internal" do
let(:project) { private_project }
- it "finds #{blob_kind} for members" do
+ it "finds #{blob_type} for members" do
project.add_reporter(user)
is_expected.not_to be_empty
end
- it "hides #{blob_kind} from non-members" do
+ it "hides #{blob_type} from non-members" do
is_expected.to be_empty
end
end
@@ -96,7 +110,7 @@ describe Gitlab::ProjectSearchResults do
end
end
- shared_examples 'blob search repository ref' do |entity_type|
+ shared_examples 'blob search repository ref' do |entity_type, blob_type|
let(:query) { 'files' }
let(:file_finder) { double }
let(:project_branch) { 'project_branch' }
@@ -139,9 +153,41 @@ describe Gitlab::ProjectSearchResults do
end
end
+ shared_examples 'blob search pagination' do |blob_type|
+ let(:per_page) { 20 }
+ let(:count_limit) { described_class::COUNT_LIMIT }
+ let(:file_finder) { instance_double('Gitlab::FileFinder') }
+ let(:results) { described_class.new(user, project, query) }
+ let(:repository_ref) { 'master' }
+
+ before do
+ allow(file_finder).to receive(:find).and_return([])
+ expect(Gitlab::FileFinder).to receive(:new).with(project, repository_ref).and_return(file_finder)
+ end
+
+ it 'limits search results based on the first page' do
+ expect(file_finder).to receive(:find).with(query, content_match_cutoff: count_limit)
+ results.objects(blob_type, page: 1, per_page: per_page)
+ end
+
+ it 'limits search results based on the second page' do
+ expect(file_finder).to receive(:find).with(query, content_match_cutoff: count_limit + per_page)
+ results.objects(blob_type, page: 2, per_page: per_page)
+ end
+
+ it 'limits search results based on the third page' do
+ expect(file_finder).to receive(:find).with(query, content_match_cutoff: count_limit + per_page * 2)
+ results.objects(blob_type, page: 3, per_page: per_page)
+ end
+
+ it 'uses the per_page value when passed' do
+ expect(file_finder).to receive(:find).with(query, content_match_cutoff: count_limit + 10 * 2)
+ results.objects(blob_type, page: 3, per_page: 10)
+ end
+ end
+
describe 'blob search' do
let(:project) { create(:project, :public, :repository) }
- let(:blob_type) { 'blobs' }
it_behaves_like 'general blob search', 'repository', 'blobs' do
let(:disabled_project) { create(:project, :public, :repository, :repository_disabled) }
@@ -150,37 +196,11 @@ describe Gitlab::ProjectSearchResults do
let(:expected_file_by_content) { 'CHANGELOG' }
end
- it_behaves_like 'blob search repository ref', 'project' do
+ it_behaves_like 'blob search repository ref', 'project', 'blobs' do
let(:entity) { project }
end
- context 'pagination' do
- let(:per_page) { 20 }
- let(:count_limit) { described_class::COUNT_LIMIT }
- let(:file_finder) { instance_double('Gitlab::FileFinder') }
- let(:results) { described_class.new(user, project, query, per_page: per_page) }
- let(:repository_ref) { 'master' }
-
- before do
- allow(file_finder).to receive(:find).and_return([])
- expect(Gitlab::FileFinder).to receive(:new).with(project, repository_ref).and_return(file_finder)
- end
-
- it 'limits search results based on the first page' do
- expect(file_finder).to receive(:find).with(query, content_match_cutoff: count_limit)
- results.objects(blob_type, 1)
- end
-
- it 'limits search results based on the second page' do
- expect(file_finder).to receive(:find).with(query, content_match_cutoff: count_limit + per_page)
- results.objects(blob_type, 2)
- end
-
- it 'limits search results based on the third page' do
- expect(file_finder).to receive(:find).with(query, content_match_cutoff: count_limit + per_page * 2)
- results.objects(blob_type, 3)
- end
- end
+ it_behaves_like 'blob search pagination', 'blobs'
end
describe 'wiki search' do
@@ -192,7 +212,7 @@ describe Gitlab::ProjectSearchResults do
wiki.create_page('CHANGELOG', 'Files example')
end
- it_behaves_like 'general blob search', 'wiki', 'wiki blobs' do
+ it_behaves_like 'general blob search', 'wiki', 'wiki_blobs' do
let(:blob_type) { 'wiki_blobs' }
let(:disabled_project) { create(:project, :public, :wiki_repo, :wiki_disabled) }
let(:private_project) { create(:project, :public, :wiki_repo, :wiki_private) }
@@ -200,10 +220,11 @@ describe Gitlab::ProjectSearchResults do
let(:expected_file_by_content) { 'CHANGELOG.md' }
end
- it_behaves_like 'blob search repository ref', 'wiki' do
- let(:blob_type) { 'wiki_blobs' }
+ it_behaves_like 'blob search repository ref', 'wiki', 'wiki_blobs' do
let(:entity) { project.wiki }
end
+
+ it_behaves_like 'blob search pagination', 'wiki_blobs'
end
it 'does not list issues on private projects' do
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index 86dde15cc8a..ab14602a468 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -28,7 +28,15 @@ describe Gitlab::SearchResults do
end
it 'returns with counts collection when requested' do
- expect(results.objects('projects', 1, false)).not_to be_kind_of(Kaminari::PaginatableWithoutCount)
+ expect(results.objects('projects', page: 1, per_page: 1, without_count: false)).not_to be_kind_of(Kaminari::PaginatableWithoutCount)
+ end
+
+ it 'uses page and per_page to paginate results' do
+ project2 = create(:project, name: 'foo')
+
+ expect(results.objects('projects', page: 1, per_page: 1).to_a).to eq([project])
+ expect(results.objects('projects', page: 2, per_page: 1).to_a).to eq([project2])
+ expect(results.objects('projects', page: 1, per_page: 2).count).to eq(2)
end
end
diff --git a/spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb b/spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb
index 8c55cc21f2c..283140d7fdf 100644
--- a/spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb
+++ b/spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb
@@ -34,7 +34,8 @@ describe Gitlab::SidekiqLogging::JSONFormatter do
'started_at' => timestamp_iso8601,
'retried_at' => timestamp_iso8601,
'failed_at' => timestamp_iso8601,
- 'completed_at' => timestamp_iso8601
+ 'completed_at' => timestamp_iso8601,
+ 'retry' => 0
}
)
@@ -57,6 +58,26 @@ describe Gitlab::SidekiqLogging::JSONFormatter do
expect(subject['args']).to eq(["1", "test", "2", %({"test"=>1})])
end
+
+ context 'when the job has a non-integer value for retry' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:retry_in_job, :retry_in_logs) do
+ 3 | 3
+ true | 25
+ false | 0
+ nil | 0
+ 'string' | -1
+ end
+
+ with_them do
+ it 'logs as the correct integer' do
+ hash_input['retry'] = retry_in_job
+
+ expect(subject['retry']).to eq(retry_in_logs)
+ end
+ end
+ end
end
describe 'with a String' do
diff --git a/spec/lib/gitlab/snippet_search_results_spec.rb b/spec/lib/gitlab/snippet_search_results_spec.rb
index 483283fd093..a41be0eaa95 100644
--- a/spec/lib/gitlab/snippet_search_results_spec.rb
+++ b/spec/lib/gitlab/snippet_search_results_spec.rb
@@ -20,4 +20,14 @@ describe Gitlab::SnippetSearchResults do
expect(results.formatted_count('snippet_titles')).to eq(max_limited_count)
end
end
+
+ describe '#objects' do
+ it 'uses page and per_page to paginate results' do
+ snippet2 = create(:snippet, :public, content: 'foo', file_name: 'foo')
+
+ expect(results.objects('snippet_titles', page: 1, per_page: 1).to_a).to eq([snippet2])
+ expect(results.objects('snippet_titles', page: 2, per_page: 1).to_a).to eq([snippet])
+ expect(results.objects('snippet_titles', page: 1, per_page: 2).count).to eq(2)
+ end
+ end
end
diff --git a/spec/policies/alert_management/alert_policy_spec.rb b/spec/policies/alert_management/alert_policy_spec.rb
index 523464d8ff1..0d7624a0142 100644
--- a/spec/policies/alert_management/alert_policy_spec.rb
+++ b/spec/policies/alert_management/alert_policy_spec.rb
@@ -10,16 +10,16 @@ describe AlertManagement::AlertPolicy, :models do
subject(:policy) { described_class.new(user, alert) }
describe 'rules' do
- it { is_expected.to be_disallowed :read_alert_management_alerts }
- it { is_expected.to be_disallowed :update_alert_management_alerts }
+ it { is_expected.to be_disallowed :read_alert_management_alert }
+ it { is_expected.to be_disallowed :update_alert_management_alert }
context 'when developer' do
before do
project.add_developer(user)
end
- it { is_expected.to be_allowed :read_alert_management_alerts }
- it { is_expected.to be_allowed :update_alert_management_alerts }
+ it { is_expected.to be_allowed :read_alert_management_alert }
+ it { is_expected.to be_allowed :update_alert_management_alert }
end
end
end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 0bdb9ea6bf9..90060c3f55a 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -15,10 +15,36 @@ describe API::Search do
it { expect(json_response.size).to eq(size) }
end
- describe 'GET /search' do
+ shared_examples 'pagination' do |scope:, search: ''|
+ it 'returns a different result for each page' do
+ get api(endpoint, user), params: { scope: scope, search: search, page: 1, per_page: 1 }
+ first = json_response.first
+
+ get api(endpoint, user), params: { scope: scope, search: search, page: 2, per_page: 1 }
+ second = Gitlab::Json.parse(response.body).first
+
+ expect(first).not_to eq(second)
+ end
+
+ it 'returns 1 result when per_page is 1' do
+ get api(endpoint, user), params: { scope: scope, search: search, per_page: 1 }
+
+ expect(json_response.count).to eq(1)
+ end
+
+ it 'returns 2 results when per_page is 2' do
+ get api(endpoint, user), params: { scope: scope, search: search, per_page: 2 }
+
+ expect(Gitlab::Json.parse(response.body).count).to eq(2)
+ end
+ end
+
+ describe 'GET /search' do
+ let(:endpoint) { '/search' }
+
context 'when user is not authenticated' do
it 'returns 401 error' do
- get api('/search'), params: { scope: 'projects', search: 'awesome' }
+ get api(endpoint), params: { scope: 'projects', search: 'awesome' }
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -26,7 +52,7 @@ describe API::Search do
context 'when scope is not supported' do
it 'returns 400 error' do
- get api('/search', user), params: { scope: 'unsupported', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'unsupported', search: 'awesome' }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -34,7 +60,7 @@ describe API::Search do
context 'when scope is missing' do
it 'returns 400 error' do
- get api('/search', user), params: { search: 'awesome' }
+ get api(endpoint, user), params: { search: 'awesome' }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -43,30 +69,48 @@ describe API::Search do
context 'with correct params' do
context 'for projects scope' do
before do
- get api('/search', user), params: { scope: 'projects', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'projects', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/projects'
+
+ it_behaves_like 'pagination', scope: :projects
end
context 'for issues scope' do
before do
create(:issue, project: project, title: 'awesome issue')
- get api('/search', user), params: { scope: 'issues', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'issues', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/issues'
+
+ describe 'pagination' do
+ before do
+ create(:issue, project: project, title: 'another issue')
+ end
+
+ include_examples 'pagination', scope: :issues
+ end
end
context 'for merge_requests scope' do
before do
create(:merge_request, source_project: repo_project, title: 'awesome mr')
- get api('/search', user), params: { scope: 'merge_requests', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'merge_requests', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/merge_requests'
+
+ describe 'pagination' do
+ before do
+ create(:merge_request, source_project: repo_project, title: 'another mr', target_branch: 'another_branch')
+ end
+
+ include_examples 'pagination', scope: :merge_requests
+ end
end
context 'for milestones scope' do
@@ -76,10 +120,18 @@ describe API::Search do
context 'when user can read project milestones' do
before do
- get api('/search', user), params: { scope: 'milestones', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'milestones', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
+
+ describe 'pagination' do
+ before do
+ create(:milestone, project: project, title: 'another milestone')
+ end
+
+ include_examples 'pagination', scope: :milestones
+ end
end
context 'when user cannot read project milestones' do
@@ -89,7 +141,7 @@ describe API::Search do
end
it 'returns empty array' do
- get api('/search', user), params: { scope: 'milestones', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'milestones', search: 'awesome' }
milestones = json_response
@@ -102,16 +154,18 @@ describe API::Search do
before do
create(:user, name: 'billy')
- get api('/search', user), params: { scope: 'users', search: 'billy' }
+ get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
+ it_behaves_like 'pagination', scope: :users
+
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
- get api('/search', user), params: { scope: 'users', search: 'billy' }
+ get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
it 'returns 400 error' do
@@ -124,18 +178,28 @@ describe API::Search do
before do
create(:snippet, :public, title: 'awesome snippet', content: 'snippet content')
- get api('/search', user), params: { scope: 'snippet_titles', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'snippet_titles', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/snippets'
+
+ describe 'pagination' do
+ before do
+ create(:snippet, :public, title: 'another snippet', content: 'snippet content')
+ end
+
+ include_examples 'pagination', scope: :snippet_titles
+ end
end
end
end
describe "GET /groups/:id/search" do
+ let(:endpoint) { "/groups/#{group.id}/-/search" }
+
context 'when user is not authenticated' do
it 'returns 401 error' do
- get api("/groups/#{group.id}/search"), params: { scope: 'projects', search: 'awesome' }
+ get api(endpoint), params: { scope: 'projects', search: 'awesome' }
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -143,7 +207,7 @@ describe API::Search do
context 'when scope is not supported' do
it 'returns 400 error' do
- get api("/groups/#{group.id}/search", user), params: { scope: 'unsupported', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'unsupported', search: 'awesome' }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -151,7 +215,7 @@ describe API::Search do
context 'when scope is missing' do
it 'returns 400 error' do
- get api("/groups/#{group.id}/search", user), params: { search: 'awesome' }
+ get api(endpoint, user), params: { search: 'awesome' }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -178,40 +242,66 @@ describe API::Search do
context 'with correct params' do
context 'for projects scope' do
before do
- get api("/groups/#{group.id}/search", user), params: { scope: 'projects', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'projects', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/projects'
+
+ it_behaves_like 'pagination', scope: :projects
end
context 'for issues scope' do
before do
create(:issue, project: project, title: 'awesome issue')
- get api("/groups/#{group.id}/search", user), params: { scope: 'issues', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'issues', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/issues'
+
+ describe 'pagination' do
+ before do
+ create(:issue, project: project, title: 'another issue')
+ end
+
+ include_examples 'pagination', scope: :issues
+ end
end
context 'for merge_requests scope' do
before do
create(:merge_request, source_project: repo_project, title: 'awesome mr')
- get api("/groups/#{group.id}/search", user), params: { scope: 'merge_requests', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'merge_requests', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/merge_requests'
+
+ describe 'pagination' do
+ before do
+ create(:merge_request, source_project: repo_project, title: 'another mr', target_branch: 'another_branch')
+ end
+
+ include_examples 'pagination', scope: :merge_requests
+ end
end
context 'for milestones scope' do
before do
create(:milestone, project: project, title: 'awesome milestone')
- get api("/groups/#{group.id}/search", user), params: { scope: 'milestones', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'milestones', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
+
+ describe 'pagination' do
+ before do
+ create(:milestone, project: project, title: 'another milestone')
+ end
+
+ include_examples 'pagination', scope: :milestones
+ end
end
context 'for milestones scope with group path as id' do
@@ -231,16 +321,24 @@ describe API::Search do
user = create(:user, name: 'billy')
create(:group_member, :developer, user: user, group: group)
- get api("/groups/#{group.id}/search", user), params: { scope: 'users', search: 'billy' }
+ get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
+ describe 'pagination' do
+ before do
+ create(:group_member, :developer, group: group)
+ end
+
+ include_examples 'pagination', scope: :users
+ end
+
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
- get api("/groups/#{group.id}/search", user), params: { scope: 'users', search: 'billy' }
+ get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
it 'returns 400 error' do
@@ -263,9 +361,11 @@ describe API::Search do
end
describe "GET /projects/:id/search" do
+ let(:endpoint) { "/projects/#{project.id}/search" }
+
context 'when user is not authenticated' do
it 'returns 401 error' do
- get api("/projects/#{project.id}/search"), params: { scope: 'issues', search: 'awesome' }
+ get api(endpoint), params: { scope: 'issues', search: 'awesome' }
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -273,7 +373,7 @@ describe API::Search do
context 'when scope is not supported' do
it 'returns 400 error' do
- get api("/projects/#{project.id}/search", user), params: { scope: 'unsupported', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'unsupported', search: 'awesome' }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -281,7 +381,7 @@ describe API::Search do
context 'when scope is missing' do
it 'returns 400 error' do
- get api("/projects/#{project.id}/search", user), params: { search: 'awesome' }
+ get api(endpoint, user), params: { search: 'awesome' }
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -299,7 +399,7 @@ describe API::Search do
it 'returns 404 error' do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- get api("/projects/#{project.id}/search", user), params: { scope: 'issues', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'issues', search: 'awesome' }
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -310,20 +410,38 @@ describe API::Search do
before do
create(:issue, project: project, title: 'awesome issue')
- get api("/projects/#{project.id}/search", user), params: { scope: 'issues', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'issues', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/issues'
+
+ describe 'pagination' do
+ before do
+ create(:issue, project: project, title: 'another issue')
+ end
+
+ include_examples 'pagination', scope: :issues
+ end
end
context 'for merge_requests scope' do
+ let(:endpoint) { "/projects/#{repo_project.id}/search" }
+
before do
create(:merge_request, source_project: repo_project, title: 'awesome mr')
- get api("/projects/#{repo_project.id}/search", user), params: { scope: 'merge_requests', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'merge_requests', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/merge_requests'
+
+ describe 'pagination' do
+ before do
+ create(:merge_request, source_project: repo_project, title: 'another mr', target_branch: 'another_branch')
+ end
+
+ include_examples 'pagination', scope: :merge_requests
+ end
end
context 'for milestones scope' do
@@ -333,10 +451,18 @@ describe API::Search do
context 'when user can read milestones' do
before do
- get api("/projects/#{project.id}/search", user), params: { scope: 'milestones', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'milestones', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
+
+ describe 'pagination' do
+ before do
+ create(:milestone, project: project, title: 'another milestone')
+ end
+
+ include_examples 'pagination', scope: :milestones
+ end
end
context 'when user cannot read project milestones' do
@@ -346,7 +472,7 @@ describe API::Search do
end
it 'returns empty array' do
- get api("/projects/#{project.id}/search", user), params: { scope: 'milestones', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'milestones', search: 'awesome' }
milestones = json_response
@@ -360,16 +486,24 @@ describe API::Search do
user1 = create(:user, name: 'billy')
create(:project_member, :developer, user: user1, project: project)
- get api("/projects/#{project.id}/search", user), params: { scope: 'users', search: 'billy' }
+ get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
+ describe 'pagination' do
+ before do
+ create(:project_member, :developer, project: project)
+ end
+
+ include_examples 'pagination', scope: :users
+ end
+
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
- get api("/projects/#{project.id}/search", user), params: { scope: 'users', search: 'billy' }
+ get api(endpoint, user), params: { scope: 'users', search: 'billy' }
end
it 'returns 400 error' do
@@ -382,29 +516,51 @@ describe API::Search do
before do
create(:note_on_merge_request, project: project, note: 'awesome note')
- get api("/projects/#{project.id}/search", user), params: { scope: 'notes', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'notes', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/notes'
+
+ describe 'pagination' do
+ before do
+ mr = create(:merge_request, source_project: project, target_branch: 'another_branch')
+ create(:note, project: project, noteable: mr, note: 'another note')
+ end
+
+ include_examples 'pagination', scope: :notes
+ end
end
context 'for wiki_blobs scope' do
+ let(:wiki) { create(:project_wiki, project: project) }
+
before do
- wiki = create(:project_wiki, project: project)
create(:wiki_page, wiki: wiki, title: 'home', content: "Awesome page")
- get api("/projects/#{project.id}/search", user), params: { scope: 'wiki_blobs', search: 'awesome' }
+ get api(endpoint, user), params: { scope: 'wiki_blobs', search: 'awesome' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/blobs'
+
+ describe 'pagination' do
+ before do
+ create(:wiki_page, wiki: wiki, title: 'home 2', content: 'Another page')
+ end
+
+ include_examples 'pagination', scope: :wiki_blobs, search: 'page'
+ end
end
context 'for commits scope' do
+ let(:endpoint) { "/projects/#{repo_project.id}/search" }
+
before do
- get api("/projects/#{repo_project.id}/search", user), params: { scope: 'commits', search: '498214de67004b1da3d820901307bed2a68a8ef6' }
+ get api(endpoint, user), params: { scope: 'commits', search: '498214de67004b1da3d820901307bed2a68a8ef6' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/commits_details'
+
+ it_behaves_like 'pagination', scope: :commits, search: 'merge'
end
context 'for commits scope with project path as id' do
@@ -416,15 +572,19 @@ describe API::Search do
end
context 'for blobs scope' do
+ let(:endpoint) { "/projects/#{repo_project.id}/search" }
+
before do
- get api("/projects/#{repo_project.id}/search", user), params: { scope: 'blobs', search: 'monitors' }
+ get api(endpoint, user), params: { scope: 'blobs', search: 'monitors' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/blobs', size: 2
+ it_behaves_like 'pagination', scope: :blobs, search: 'monitors'
+
context 'filters' do
it 'by filename' do
- get api("/projects/#{repo_project.id}/search", user), params: { scope: 'blobs', search: 'mon filename:PROCESS.md' }
+ get api(endpoint, user), params: { scope: 'blobs', search: 'mon filename:PROCESS.md' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(2)
@@ -433,21 +593,21 @@ describe API::Search do
end
it 'by path' do
- get api("/projects/#{repo_project.id}/search", user), params: { scope: 'blobs', search: 'mon path:markdown' }
+ get api(endpoint, user), params: { scope: 'blobs', search: 'mon path:markdown' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(8)
end
it 'by extension' do
- get api("/projects/#{repo_project.id}/search", user), params: { scope: 'blobs', search: 'mon extension:md' }
+ get api(endpoint, user), params: { scope: 'blobs', search: 'mon extension:md' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(11)
end
it 'by ref' do
- get api("/projects/#{repo_project.id}/search", user), params: { scope: 'blobs', search: 'This file is used in tests for ci_environments_status', ref: 'pages-deploy' }
+ get api(endpoint, user), params: { scope: 'blobs', search: 'This file is used in tests for ci_environments_status', ref: 'pages-deploy' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to eq(1)
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 77c160ccce2..0333eb85fb6 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -18,7 +18,9 @@ describe SearchService do
let(:group_project) { create(:project, group: accessible_group, name: 'group_project') }
let(:public_project) { create(:project, :public, name: 'public_project') }
- subject(:search_service) { described_class.new(user, search: search, scope: scope, page: 1) }
+ let(:per_page) { described_class::DEFAULT_PER_PAGE }
+
+ subject(:search_service) { described_class.new(user, search: search, scope: scope, page: 1, per_page: per_page) }
before do
accessible_project.add_maintainer(user)
@@ -240,6 +242,76 @@ describe SearchService do
end
describe '#search_objects' do
+ context 'handling per_page param' do
+ let(:search) { '' }
+ let(:scope) { nil }
+
+ context 'when nil' do
+ let(:per_page) { nil }
+
+ it "defaults to #{described_class::DEFAULT_PER_PAGE}" do
+ expect_any_instance_of(Gitlab::SearchResults)
+ .to receive(:objects)
+ .with(anything, hash_including(per_page: described_class::DEFAULT_PER_PAGE))
+ .and_call_original
+
+ subject.search_objects
+ end
+ end
+
+ context 'when empty string' do
+ let(:per_page) { '' }
+
+ it "defaults to #{described_class::DEFAULT_PER_PAGE}" do
+ expect_any_instance_of(Gitlab::SearchResults)
+ .to receive(:objects)
+ .with(anything, hash_including(per_page: described_class::DEFAULT_PER_PAGE))
+ .and_call_original
+
+ subject.search_objects
+ end
+ end
+
+ context 'when negative' do
+ let(:per_page) { '-1' }
+
+ it "defaults to #{described_class::DEFAULT_PER_PAGE}" do
+ expect_any_instance_of(Gitlab::SearchResults)
+ .to receive(:objects)
+ .with(anything, hash_including(per_page: described_class::DEFAULT_PER_PAGE))
+ .and_call_original
+
+ subject.search_objects
+ end
+ end
+
+ context 'when present' do
+ let(:per_page) { '50' }
+
+ it "converts to integer and passes to search results" do
+ expect_any_instance_of(Gitlab::SearchResults)
+ .to receive(:objects)
+ .with(anything, hash_including(per_page: 50))
+ .and_call_original
+
+ subject.search_objects
+ end
+ end
+
+ context "when greater than #{described_class::MAX_PER_PAGE}" do
+ let(:per_page) { described_class::MAX_PER_PAGE + 1 }
+
+ it "passes #{described_class::MAX_PER_PAGE}" do
+ expect_any_instance_of(Gitlab::SearchResults)
+ .to receive(:objects)
+ .with(anything, hash_including(per_page: described_class::MAX_PER_PAGE))
+ .and_call_original
+
+ subject.search_objects
+ end
+ end
+ end
+
context 'with accessible project_id' do
it 'returns objects in the project' do
search_objects = described_class.new(
diff --git a/yarn.lock b/yarn.lock
index 859fc145342..8d39687eab5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -782,15 +782,15 @@
eslint-plugin-vue "^6.2.1"
vue-eslint-parser "^7.0.0"
-"@gitlab/svgs@1.125.0":
- version "1.125.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.125.0.tgz#59c667dae8f7e4c80b482f5f6cc35367c016387b"
- integrity sha512-MKfFYa8f+9P2tJ/JN/E9oDBSSo/gRz2zuGui4XHQPoaw/DkIMn7EyAzeSpRgbgs1LgMcEqqKsIEx+spCga3jsQ==
-
-"@gitlab/ui@14.0.0":
- version "14.0.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-14.0.0.tgz#d478b1454659c0f54b72cdecce1c2014fc5f8564"
- integrity sha512-R+unP0mOBYQ+uRJLm/tI+2znsbsHY2rumSYtMqM3vGCXasteySQIMZ8huWGa5Cf4ZUdy1lNa0J/zxKj6TLdjCQ==
+"@gitlab/svgs@1.127.0":
+ version "1.127.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.127.0.tgz#1f7ffdffe44d6a82b372535f93d78f3a895d1960"
+ integrity sha512-Uv52DqkG2KwCB0VRlXUEHFZxJ/7Ql0t1YTdzICpXmDjltuUBrysFcdmWPVO6PgXQxk2ahryNsUjSOziMYTeSiw==
+
+"@gitlab/ui@14.2.1":
+ version "14.2.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-14.2.1.tgz#7972ce8e258357a41a354bda9213b144593ed912"
+ integrity sha512-jdwhtnjVW38wfeqibIgIqDqeYbKwq6kWfs0LRHhpoh+A+zgjgzUxjSkcz9Wg+Yipy08PmY/NSKEkuK33/fIqQA==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"