summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-02 18:07:23 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-02 18:07:23 +0000
commitaf833d9730dd367984b55ef02ccc3fe6eb83f0e4 (patch)
treef525dae9e43b8cb77d8eaad38f998ad06b8a769d
parent20ddcb963c756f5c7df26046adb01a8e325a26cd (diff)
downloadgitlab-ce-af833d9730dd367984b55ef02ccc3fe6eb83f0e4.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitignore1
-rw-r--r--.rubocop_todo/lint/constant_definition_in_block.yml4
-rw-r--r--.rubocop_todo/performance/bind_call.yml10
-rw-r--r--.rubocop_todo/rspec/hooks_before_examples.yml26
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue2
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue2
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue4
-rw-r--r--app/assets/javascripts/content_editor/components/suggestions_dropdown.vue4
-rw-r--r--app/assets/javascripts/editor/schema/ci.json41
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue4
-rw-r--r--app/assets/javascripts/merge_request.js2
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/actions_button.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js26
-rw-r--r--app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue110
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss1
-rw-r--r--app/assets/stylesheets/components/ref_selector.scss2
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss12
-rw-r--r--app/assets/stylesheets/framework/emojis.scss4
-rw-r--r--app/assets/stylesheets/framework/header.scss2
-rw-r--r--app/assets/stylesheets/framework/typography.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss5
-rw-r--r--app/assets/stylesheets/page_bundles/alert_management_details.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/clusters.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss2
-rw-r--r--app/assets/stylesheets/pages/monitor.scss5
-rw-r--r--app/assets/stylesheets/pages/projects.scss2
-rw-r--r--app/assets/stylesheets/pages/search.scss6
-rw-r--r--app/helpers/listbox_helper.rb4
-rw-r--r--app/models/concerns/has_user_type.rb16
-rw-r--r--app/services/clusters/applications/check_progress_service.rb50
-rw-r--r--app/services/metrics/dashboard/grafana_metric_embed_service.rb3
-rw-r--r--app/views/admin/projects/index.html.haml2
-rw-r--r--app/views/admin/projects/show.html.haml2
-rw-r--r--app/views/projects/buttons/_clone.html.haml12
-rw-r--r--app/views/projects/buttons/_download.html.haml2
-rw-r--r--app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml40
-rw-r--r--app/views/projects/merge_requests/_code_dropdown.html.haml34
-rw-r--r--app/views/shared/_new_project_item_select.html.haml2
-rw-r--r--app/views/shared/web_hooks/_test_button.html.haml6
-rw-r--r--app/workers/all_queues.yml20
-rw-r--r--app/workers/merge_requests/delete_branch_worker.rb27
-rw-r--r--app/workers/merge_requests/delete_source_branch_worker.rb9
-rw-r--r--app/workers/projects/delete_branch_worker.rb30
-rw-r--r--app/workers/run_pipeline_schedule_worker.rb2
-rw-r--r--config/feature_categories.yml1
-rw-r--r--config/feature_flags/development/disable_pagination_counts_on_jobs_api.yml8
-rw-r--r--config/feature_flags/development/markdown_image_attributes.yml8
-rw-r--r--config/feature_flags/development/track_and_raise_delete_source_errors.yml (renamed from config/feature_flags/development/actors_aware_gitaly_calls.yml)8
-rw-r--r--config/feature_flags/ops/suggested_reviewers_internal_api.yml8
-rw-r--r--config/gitlab.yml.example5
-rw-r--r--config/initializers/1_settings.rb6
-rw-r--r--config/initializers/gitlab_suggested_reviewers_secret.rb5
-rw-r--r--config/sidekiq_queues.yml4
-rw-r--r--doc/ci/yaml/index.md42
-rw-r--r--doc/operations/incident_management/alerts.md2
-rw-r--r--doc/operations/incident_management/linked_resources.md6
-rw-r--r--doc/operations/metrics/alerts.md4
-rw-r--r--doc/tutorials/index.md2
-rw-r--r--doc/user/markdown.md27
-rw-r--r--lib/api/ci/jobs.rb4
-rw-r--r--lib/api/internal/kubernetes.rb2
-rw-r--r--lib/banzai/filter/attributes_filter.rb57
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb1
-rw-r--r--lib/gitlab/gitaly_client/with_feature_flag_actors.rb11
-rw-r--r--lib/gitlab/pagination/offset_pagination.rb15
-rw-r--r--lib/gitlab/patch/prependable.rb2
-rw-r--r--locale/gitlab.pot15
-rw-r--r--package.json2
-rw-r--r--qa/qa/page/component/dropdown.rb10
-rw-r--r--spec/controllers/import/github_controller_spec.rb4
-rw-r--r--spec/features/admin/users/users_spec.rb4
-rw-r--r--spec/features/alert_management/alert_details_spec.rb2
-rw-r--r--spec/features/boards/board_filters_spec.rb2
-rw-r--r--spec/features/boards/sidebar_assignee_spec.rb2
-rw-r--r--spec/features/groups/board_spec.rb2
-rw-r--r--spec/features/incidents/incident_timeline_events_spec.rb8
-rw-r--r--spec/features/issuables/shortcuts_issuable_spec.rb2
-rw-r--r--spec/features/issues/form_spec.rb2
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb2
-rw-r--r--spec/features/markdown/markdown_spec.rb7
-rw-r--r--spec/features/projects/compare_spec.rb14
-rw-r--r--spec/features/projects/pages/user_configures_pages_pipeline_spec.rb8
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb4
-rw-r--r--spec/features/projects/pipelines/legacy_pipelines_spec.rb6
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb6
-rw-r--r--spec/features/projects/show/user_manages_notifications_spec.rb2
-rw-r--r--spec/fixtures/markdown.md.erb4
-rw-r--r--spec/frontend/boards/board_list_spec.js7
-rw-r--r--spec/frontend/boards/components/board_content_spec.js7
-rw-r--r--spec/frontend/boards/project_select_spec.js2
-rw-r--r--spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap8
-rw-r--r--spec/frontend/editor/schema/ci/ci_schema_spec.js4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml10
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml10
-rw-r--r--spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap20
-rw-r--r--spec/frontend/language_switcher/components/app_spec.js2
-rw-r--r--spec/frontend/milestones/components/milestone_combobox_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap4
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap2
-rw-r--r--spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js132
-rw-r--r--spec/graphql/resolvers/design_management/design_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/design_management/designs_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/project_pipeline_resolver_spec.rb8
-rw-r--r--spec/helpers/listbox_helper_spec.rb2
-rw-r--r--spec/lib/banzai/filter/attributes_filter_spec.rb85
-rw-r--r--spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/references/user_reference_filter_spec.rb4
-rw-r--r--spec/lib/feature/definition_spec.rb10
-rw-r--r--spec/lib/gitlab/auth/saml/user_spec.rb8
-rw-r--r--spec/lib/gitlab/email/handler/create_issue_handler_spec.rb4
-rw-r--r--spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb8
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb10
-rw-r--r--spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb8
-rw-r--r--spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb140
-rw-r--r--spec/lib/gitlab/pagination/offset_pagination_spec.rb22
-rw-r--r--spec/lib/gitlab/tracking/event_definition_spec.rb10
-rw-r--r--spec/lib/gitlab/usage/metric_definition_spec.rb10
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb8
-rw-r--r--spec/lib/mattermost/session_spec.rb8
-rw-r--r--spec/mailers/notify_spec.rb6
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb4
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb10
-rw-r--r--spec/models/clusters/applications/knative_spec.rb12
-rw-r--r--spec/models/concerns/has_user_type_spec.rb3
-rw-r--r--spec/models/integrations/chat_message/pipeline_message_spec.rb4
-rw-r--r--spec/models/repository_spec.rb4
-rw-r--r--spec/requests/api/ci/jobs_spec.rb4
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb16
-rw-r--r--spec/serializers/pipeline_details_entity_spec.rb8
-rw-r--r--spec/services/feature_flags/hook_service_spec.rb8
-rw-r--r--spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb8
-rw-r--r--spec/support/helpers/listbox_input_helper.rb18
-rw-r--r--spec/support/patches/rspec_mocks_prepended_methods.rb2
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb8
-rw-r--r--spec/workers/merge_requests/delete_branch_worker_spec.rb65
-rw-r--r--spec/workers/merge_requests/delete_source_branch_worker_spec.rb23
-rw-r--r--spec/workers/pipeline_schedule_worker_spec.rb26
-rw-r--r--spec/workers/projects/delete_branch_worker_spec.rb112
-rw-r--r--spec/workers/run_pipeline_schedule_worker_spec.rb10
-rw-r--r--yarn.lock8
151 files changed, 1232 insertions, 622 deletions
diff --git a/.gitignore b/.gitignore
index ad7595dc7f2..6441e798332 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,6 +73,7 @@ eslint-report.html
/.gitlab_workhorse_secret
/.gitlab_pages_secret
/.gitlab_kas_secret
+/.gitlab_suggested_reviewers_secret
/webpack-report/
/crystalball/
/test_results/
diff --git a/.rubocop_todo/lint/constant_definition_in_block.yml b/.rubocop_todo/lint/constant_definition_in_block.yml
index 813e0b22559..ff3f6b2afbc 100644
--- a/.rubocop_todo/lint/constant_definition_in_block.yml
+++ b/.rubocop_todo/lint/constant_definition_in_block.yml
@@ -1,8 +1,6 @@
---
Lint/ConstantDefinitionInBlock:
- # Offense count: 111
- # Temporarily disabled due to too many offenses
- Enabled: false
+ Details: grace period
Exclude:
- 'app/models/concerns/ignorable_columns.rb'
- 'app/models/concerns/partitioned_table.rb'
diff --git a/.rubocop_todo/performance/bind_call.yml b/.rubocop_todo/performance/bind_call.yml
deleted file mode 100644
index 732aecc3910..00000000000
--- a/.rubocop_todo/performance/bind_call.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-# Cop supports --autocorrect.
-Performance/BindCall:
- Exclude:
- - 'app/services/metrics/dashboard/grafana_metric_embed_service.rb'
- - 'ee/spec/features/issues/form_spec.rb'
- - 'lib/gitlab/patch/prependable.rb'
- - 'spec/features/issues/form_spec.rb'
- - 'spec/mailers/notify_spec.rb'
- - 'spec/support/patches/rspec_mocks_prepended_methods.rb'
diff --git a/.rubocop_todo/rspec/hooks_before_examples.yml b/.rubocop_todo/rspec/hooks_before_examples.yml
index 01756f4d8c3..333687e1cc4 100644
--- a/.rubocop_todo/rspec/hooks_before_examples.yml
+++ b/.rubocop_todo/rspec/hooks_before_examples.yml
@@ -44,29 +44,3 @@ RSpec/HooksBeforeExamples:
- 'qa/qa/specs/features/ee/browser_ui/1_manage/user/minimal_access_user_spec.rb'
- 'qa/spec/resource/reusable_collection_spec.rb'
- 'qa/spec/specs/runner_spec.rb'
- - 'spec/controllers/import/github_controller_spec.rb'
- - 'spec/features/projects/pages/user_configures_pages_pipeline_spec.rb'
- - 'spec/graphql/resolvers/design_management/design_resolver_spec.rb'
- - 'spec/graphql/resolvers/design_management/designs_resolver_spec.rb'
- - 'spec/graphql/resolvers/project_pipeline_resolver_spec.rb'
- - 'spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb'
- - 'spec/lib/banzai/filter/references/user_reference_filter_spec.rb'
- - 'spec/lib/feature/definition_spec.rb'
- - 'spec/lib/gitlab/auth/saml/user_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb'
- - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
- - 'spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb'
- - 'spec/lib/gitlab/tracking/event_definition_spec.rb'
- - 'spec/lib/gitlab/usage/metric_definition_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
- - 'spec/lib/mattermost/session_spec.rb'
- - 'spec/models/ci/build_trace_chunk_spec.rb'
- - 'spec/models/clusters/applications/ingress_spec.rb'
- - 'spec/models/clusters/applications/knative_spec.rb'
- - 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
- - 'spec/models/repository_spec.rb'
- - 'spec/serializers/pipeline_details_entity_spec.rb'
- - 'spec/services/feature_flags/hook_service_spec.rb'
- - 'spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb'
- - 'spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb'
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 5bc6ce105b9..ca86894ca40 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -116,6 +116,8 @@ export default {
group: 'boards-list',
tag: 'div',
value: this.boardListsToUse,
+ delay: 100,
+ delayOnTouchOnly: true,
};
return this.canDragColumns ? options : {};
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 6561958b9bf..215691c7ba2 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -133,6 +133,8 @@ export default {
'ghost-class': 'board-card-drag-active',
'data-list-id': this.list.id,
value: this.boardItems,
+ delay: 100,
+ delayOnTouchOnly: true,
};
return this.canMoveIssue ? options : {};
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index eaf3facb450..4f90d77c0be 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -237,7 +237,7 @@ export default {
:text="board.name"
@show="loadBoards"
>
- <p class="gl-new-dropdown-header-top" @mousedown.prevent>
+ <p class="gl-dropdown-header-top" @mousedown.prevent>
{{ s__('IssueBoards|Switch board') }}
</p>
<gl-search-box-by-type ref="searchBox" v-model="filterTerm" class="m-2" />
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
index a9668ebdb69..98b7203778f 100644
--- a/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
@@ -166,9 +166,7 @@ export default {
icon="arrow-left"
@click.prevent.stop="showCustomLanguageInput = false"
/>
- <p
- class="gl-text-center gl-new-dropdown-header-top gl-mb-0! gl-border-none! gl-pb-1!"
- >
+ <p class="gl-text-center gl-dropdown-header-top gl-mb-0! gl-border-none! gl-pb-1!">
{{ __('Create custom type') }}
</p>
</div>
diff --git a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
index 001b34a00fa..37e6ef61d50 100644
--- a/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/suggestions_dropdown.vue
@@ -210,10 +210,10 @@ export default {
<template>
<ul
:class="{ show: items.length > 0 }"
- class="gl-new-dropdown dropdown-menu gl-relative"
+ class="gl-dropdown dropdown-menu gl-relative"
data-testid="content-editor-suggestions-dropdown"
>
- <div class="gl-new-dropdown-inner gl-overflow-y-auto">
+ <div class="gl-dropdown-inner gl-overflow-y-auto">
<gl-dropdown-item
v-for="(item, index) in items"
ref="dropdownItems"
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index 5facd858f62..21c50acbf5d 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -41,6 +41,9 @@
"before_script": {
"$ref": "#/definitions/before_script"
},
+ "hooks": {
+ "$ref": "#/definitions/hooks"
+ },
"cache": {
"$ref": "#/definitions/cache"
},
@@ -1217,6 +1220,9 @@
"after_script": {
"$ref": "#/definitions/after_script"
},
+ "hooks": {
+ "$ref": "#/definitions/hooks"
+ },
"rules": {
"$ref": "#/definitions/rules"
},
@@ -1881,6 +1887,39 @@
}
]
}
+ },
+ "hooks": {
+ "type": "object",
+ "markdownDescription": "Specifies lists of commands to execute on the runner at certain stages of job execution. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#hooks).",
+ "properties": {
+ "pre_get_sources_script": {
+ "markdownDescription": "Specifies a list of commands to execute on the runner before updating the Git repository and any submodules. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#hookspre_get_sources_script).",
+ "oneOf": [
+ {
+ "type": "string",
+ "minLength": 1
+ },
+ {
+ "type": "array",
+ "items": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
+ },
+ "minItems": 1
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
}
}
-} \ No newline at end of file
+}
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
index de4b11699fc..122c7c005e9 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
@@ -357,7 +357,7 @@ export default {
>
<span class="d-flex">
<gl-icon
- class="gl-new-dropdown-item-check-icon"
+ class="gl-dropdown-item-check-icon"
:class="{ invisible: !isCurrentStatusFilter(status) }"
name="mobile-issue-close"
/>
@@ -374,7 +374,7 @@ export default {
>
<span class="d-flex">
<gl-icon
- class="gl-new-dropdown-item-check-icon"
+ class="gl-dropdown-item-check-icon"
:class="{ invisible: !isCurrentSortField(field) }"
name="mobile-issue-close"
/>
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index 57b5e9809d2..a77f339f5a1 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -173,7 +173,7 @@ MergeRequest.toggleDraftStatus = function (title, isReady) {
);
draftToggle.setAttribute('href', url);
- draftToggle.querySelector('.gl-new-dropdown-item-text-wrapper').textContent = isReady
+ draftToggle.querySelector('.gl-dropdown-item-text-wrapper').textContent = isReady
? __('Mark as draft')
: __('Mark as ready');
});
diff --git a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue
index 98f558c7df5..93110c8e3db 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue
@@ -162,7 +162,7 @@ export default {
/>
</li>
<template v-if="isMergeTrain">
- <li class="gl-new-dropdown-divider" role="presentation">
+ <li class="gl-dropdown-divider" role="presentation">
<hr role="separator" aria-orientation="horizontal" class="dropdown-divider" />
</li>
<li>
diff --git a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
index 1ccc31b6962..cdce6617591 100644
--- a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
@@ -111,9 +111,9 @@ export default {
</script>
<template>
- <li v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <li v-if="isMergeRequest" class="gl-dropdown-item">
<button type="button" class="dropdown-item" @click="toggleLocked">
- <span class="gl-new-dropdown-item-text-wrapper">
+ <span class="gl-dropdown-item-text-wrapper">
<template v-if="isLocked">
{{ __('Unlock merge request') }}
</template>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
index eb38de73237..0fba1cb5e4e 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
@@ -182,7 +182,7 @@ export default {
</script>
<template>
- <gl-dropdown-form v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <gl-dropdown-form v-if="isMergeRequest" class="gl-dropdown-item">
<div class="gl-px-5 gl-pb-2 gl-pt-1">
<gl-toggle
:value="subscribed"
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
index 672761af1cf..8d2ef20b381 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
@@ -106,7 +106,7 @@ export default {
@keydown.esc.native="$emit('hide-dropdown')"
@hide="$emit('hide-dropdown')"
>
- <p v-if="isSidebar" class="gl-new-dropdown-header-top" data-testid="dropdown-header">
+ <p v-if="isSidebar" class="gl-dropdown-header-top" data-testid="dropdown-header">
{{ s__('AlertManagement|Assign status') }}
</p>
<div class="dropdown-content dropdown-body">
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
index 72dcc16b57a..4ec301b946b 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
@@ -242,7 +242,7 @@ export default {
@keydown.esc.native="hideDropdown"
@hide="hideDropdown"
>
- <p class="gl-new-dropdown-header-top">
+ <p class="gl-dropdown-header-top">
{{ __('Assign To') }}
</p>
<gl-search-box-by-type v-model.trim="search" :placeholder="__('Search users')" />
diff --git a/app/assets/javascripts/vue_shared/components/actions_button.vue b/app/assets/javascripts/vue_shared/components/actions_button.vue
index c6c22f9c61f..e3b22bcaf98 100644
--- a/app/assets/javascripts/vue_shared/components/actions_button.vue
+++ b/app/assets/javascripts/vue_shared/components/actions_button.vue
@@ -70,7 +70,7 @@ export default {
@click="handleClick(selectedAction, $event)"
>
<template #button-content>
- <span class="gl-new-dropdown-button-text" v-bind="selectedAction.attrs">
+ <span class="gl-dropdown-button-text" v-bind="selectedAction.attrs">
{{ selectedAction.text }}
</span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js
new file mode 100644
index 00000000000..4106de371cb
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js
@@ -0,0 +1,26 @@
+import ListboxInput from './listbox_input.vue';
+
+export default {
+ component: ListboxInput,
+ title: 'vue_shared/listbox_input',
+};
+
+const Template = (args, { argTypes }) => ({
+ components: { ListboxInput },
+ data() {
+ return { selected: null };
+ },
+ props: Object.keys(argTypes),
+ template: '<listbox-input v-model="selected" v-bind="$props" />',
+});
+
+export const Default = Template.bind({});
+Default.args = {
+ name: 'input_name',
+ defaultToggleText: 'Select an option',
+ items: [
+ { text: 'Option 1', value: '1' },
+ { text: 'Option 2', value: '2' },
+ { text: 'Option 3', value: '3' },
+ ],
+};
diff --git a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue
new file mode 100644
index 00000000000..b1809e6a9f3
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue
@@ -0,0 +1,110 @@
+<script>
+import { GlListbox } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+const MIN_ITEMS_COUNT_FOR_SEARCHING = 20;
+
+export default {
+ i18n: {
+ noResultsText: __('No results found'),
+ },
+ components: {
+ GlListbox,
+ },
+ model: GlListbox.model,
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ defaultToggleText: {
+ type: String,
+ required: true,
+ },
+ selected: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ items: {
+ type: GlListbox.props.items.type,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ searchString: '',
+ };
+ },
+ computed: {
+ allOptions() {
+ const allOptions = [];
+
+ const getOptions = (options) => {
+ for (let i = 0; i < options.length; i += 1) {
+ const option = options[i];
+ if (option.options) {
+ getOptions(option.options);
+ } else {
+ allOptions.push(option);
+ }
+ }
+ };
+ getOptions(this.items);
+
+ return allOptions;
+ },
+ isGrouped() {
+ return this.items.some((item) => item.options !== undefined);
+ },
+ isSearchable() {
+ return this.allOptions.length > MIN_ITEMS_COUNT_FOR_SEARCHING;
+ },
+ filteredItems() {
+ const searchString = this.searchString.toLowerCase();
+
+ if (!searchString) {
+ return this.items;
+ }
+
+ if (this.isGrouped) {
+ return this.items
+ .map(({ text, options }) => {
+ return {
+ text,
+ options: options.filter((option) => option.text.toLowerCase().includes(searchString)),
+ };
+ })
+ .filter(({ options }) => options.length);
+ }
+
+ return this.items.filter((item) => item.text.toLowerCase().includes(searchString));
+ },
+ toggleText() {
+ return this.selected
+ ? this.allOptions.find((option) => option.value === this.selected).text
+ : this.defaultToggleText;
+ },
+ },
+ methods: {
+ search(searchString) {
+ this.searchString = searchString;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-listbox
+ :selected="selected"
+ :toggle-text="toggleText"
+ :items="filteredItems"
+ :searchable="isSearchable"
+ :no-results-text="$options.i18n.noResultsText"
+ @search="search"
+ @select="$emit($options.model.event, $event)"
+ />
+ <input ref="input" type="hidden" :name="name" :value="selected" />
+ </div>
+</template>
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index 6878e9a10d7..addb5f8b9c2 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -10,7 +10,6 @@
@import './pages/login';
@import './pages/ml_experiment_tracking';
@import './pages/merge_requests';
-@import './pages/monitor';
@import './pages/note_form';
@import './pages/notes';
@import './pages/pipelines';
diff --git a/app/assets/stylesheets/components/ref_selector.scss b/app/assets/stylesheets/components/ref_selector.scss
index ded911c2492..f7a9367499e 100644
--- a/app/assets/stylesheets/components/ref_selector.scss
+++ b/app/assets/stylesheets/components/ref_selector.scss
@@ -6,7 +6,7 @@
width: 20rem;
&,
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: $dropdown-max-height-lg;
}
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 3cd6db25d0e..0acda85f527 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -5,7 +5,7 @@
// for Snippets is introduced and Clone button is relocated, we won't
// need this style.
// Issue for the refactoring: https://gitlab.com/gitlab-org/gitlab/-/issues/213327
- &.gl-new-dropdown button.dropdown-toggle {
+ &.gl-dropdown button.dropdown-toggle {
@include gl-display-inline-flex;
}
@@ -41,7 +41,7 @@
max-height: $extended-max-height;
// See comment below for explanation
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: $extended-max-height - 2px;
}
}
@@ -54,12 +54,12 @@
width: 100%;
}
- // `GlDropdown` specifies the `max-height` of `.gl-new-dropdown-inner`
+ // `GlDropdown` specifies the `max-height` of `.gl-dropdown-inner`
// as `$dropdown-max-height`, but the `max-height` rule above forces
// the parent `.dropdown-menu` to be _slightly_ too small because of
// the 1px borders. The workaround below overrides the @gitlab/ui style
// to avoid a double scroll bar.
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: $dropdown-max-height - 2px;
}
}
@@ -1027,7 +1027,7 @@
// This class won't be needed once we can add a prop for this in the GitLab UI component
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/966
-.gl-new-dropdown {
+.gl-dropdown {
.gl-dropdown-menu-wide {
width: $gl-dropdown-width-wide;
}
@@ -1035,7 +1035,7 @@
// This class won't be needed once we can add a prop for this in the GitLab UI component
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/966
-.gl-new-dropdown.gl-dropdown-menu-full-width {
+.gl-dropdown.gl-dropdown-menu-full-width {
.dropdown-menu {
width: 100%;
}
diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index 68a3493670d..16ad6f62c64 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -36,7 +36,7 @@ gl-emoji {
}
}
-.emoji-picker .gl-new-dropdown .dropdown-menu {
+.emoji-picker .gl-dropdown .dropdown-menu {
width: 350px;
}
@@ -48,6 +48,6 @@ gl-emoji {
border-bottom-color: var(--gl-theme-accent, $theme-indigo-500);
}
-.emoji-picker .gl-new-dropdown-inner > :last-child {
+.emoji-picker .gl-dropdown-inner > :last-child {
padding-bottom: 0;
}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index ed41d10f3b2..9d518786eac 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -555,7 +555,7 @@
}
.top-nav-container-view {
- .gl-new-dropdown & .gl-search-box-by-type {
+ .gl-dropdown & .gl-search-box-by-type {
@include gl-m-0;
}
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 2c2d8a2b592..d8f0375358c 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -587,7 +587,7 @@
}
}
- .gl-new-dropdown-item {
+ .gl-dropdown-item {
margin: 0;
padding: 0;
line-height: 1rem;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 97ad342997b..6086405226e 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -789,11 +789,6 @@ $login-brand-holder-color: #888;
$project-option-descr-color: #54565b;
/*
- * Monitor Charts
- */
-$chart-tooltip-max-width: 512px;
-
-/*
Stat Graph
*/
$stat-graph-common-bg: #f3f3f3;
diff --git a/app/assets/stylesheets/page_bundles/alert_management_details.scss b/app/assets/stylesheets/page_bundles/alert_management_details.scss
index 2eaf4517710..d67dadafa9e 100644
--- a/app/assets/stylesheets/page_bundles/alert_management_details.scss
+++ b/app/assets/stylesheets/page_bundles/alert_management_details.scss
@@ -28,7 +28,7 @@
@include gl-pt-8;
}
- .gl-new-dropdown-item-text-wrapper {
+ .gl-dropdown-item-text-wrapper {
@include gl-py-0;
}
}
diff --git a/app/assets/stylesheets/page_bundles/clusters.scss b/app/assets/stylesheets/page_bundles/clusters.scss
index 4f29ff4b1ad..2f5c40de6e4 100644
--- a/app/assets/stylesheets/page_bundles/clusters.scss
+++ b/app/assets/stylesheets/page_bundles/clusters.scss
@@ -6,7 +6,7 @@
@include gl-w-full;
order: -1;
- .gl-new-dropdown,
+ .gl-dropdown,
.split-content-button {
@include gl-w-full;
}
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index c2e6294f0c2..eb1d86b02df 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -999,7 +999,7 @@ $tabs-holder-z-index: 250;
max-width: 650px;
max-height: calc(100vh - 50px);
- .gl-new-dropdown-inner {
+ .gl-dropdown-inner {
max-height: none !important;
}
@@ -1034,7 +1034,7 @@ $tabs-holder-z-index: 250;
}
}
- .gl-new-dropdown-contents {
+ .gl-dropdown-contents {
padding: $gl-spacing-scale-4 !important;
}
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index 91fd2d42657..b995724ec7c 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -9,7 +9,7 @@
@include gl-w-full;
}
- .gl-new-dropdown-item-text-primary {
+ .gl-dropdown-item-text-primary {
@include gl-overflow-hidden;
@include gl-text-overflow-ellipsis;
}
diff --git a/app/assets/stylesheets/pages/monitor.scss b/app/assets/stylesheets/pages/monitor.scss
deleted file mode 100644
index 25ff5abd774..00000000000
--- a/app/assets/stylesheets/pages/monitor.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.chart-tooltip > .popover {
- min-width: 0;
- width: max-content;
- max-width: $chart-tooltip-max-width;
-}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index bf20204cfd9..a119c815cf4 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -562,7 +562,7 @@
// Remove once gitlab/ui solution is implemented
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1158
// https://gitlab.com/gitlab-org/gitlab/-/issues/300405
- .gl-new-dropdown-button-text {
+ .gl-dropdown-button-text {
@include str-truncated;
}
}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 1bca04e5eb1..bec9efe2bf4 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -283,7 +283,7 @@ input[type='checkbox']:hover {
.btn-search,
.btn-success,
.dropdown-menu-toggle,
- .gl-new-dropdown {
+ .gl-dropdown {
width: 100%;
margin-top: 5px;
@@ -302,7 +302,7 @@ input[type='checkbox']:hover {
}
.dropdown-menu-toggle,
- .gl-new-dropdown {
+ .gl-dropdown {
@include media-breakpoint-up(sm) {
width: 180px;
margin-top: 0;
@@ -317,7 +317,7 @@ input[type='checkbox']:hover {
}
.dropdown-menu-toggle,
- .gl-new-dropdown {
+ .gl-dropdown {
@include media-breakpoint-up(lg) {
width: 240px;
}
diff --git a/app/helpers/listbox_helper.rb b/app/helpers/listbox_helper.rb
index 16caf862c7b..0aaeb39c82d 100644
--- a/app/helpers/listbox_helper.rb
+++ b/app/helpers/listbox_helper.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
module ListboxHelper
- DROPDOWN_CONTAINER_CLASSES = %w[dropdown b-dropdown gl-new-dropdown btn-group js-redirect-listbox].freeze
+ DROPDOWN_CONTAINER_CLASSES = %w[dropdown b-dropdown gl-dropdown btn-group js-redirect-listbox].freeze
DROPDOWN_BUTTON_CLASSES = %w[btn dropdown-toggle btn-default btn-md gl-button gl-dropdown-toggle].freeze
- DROPDOWN_INNER_CLASS = 'gl-new-dropdown-button-text'
+ DROPDOWN_INNER_CLASS = 'gl-dropdown-button-text'
DROPDOWN_ICON_CLASS = 'gl-button-icon dropdown-chevron gl-icon'
# Creates a listbox component with redirect behavior.
diff --git a/app/models/concerns/has_user_type.rb b/app/models/concerns/has_user_type.rb
index 8e371f2a9c1..158d7d1c9bb 100644
--- a/app/models/concerns/has_user_type.rb
+++ b/app/models/concerns/has_user_type.rb
@@ -14,10 +14,22 @@ module HasUserType
migration_bot: 7,
security_bot: 8,
automation_bot: 9,
- admin_bot: 11
+ admin_bot: 11,
+ suggested_reviewers_bot: 12
}.with_indifferent_access.freeze
- BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot security_bot automation_bot admin_bot].freeze
+ BOT_USER_TYPES = %w[
+ alert_bot
+ project_bot
+ support_bot
+ visual_review_bot
+ migration_bot
+ security_bot
+ automation_bot
+ admin_bot
+ suggested_reviewers_bot
+ ].freeze
+
NON_INTERNAL_USER_TYPES = %w[human project_bot service_user].freeze
INTERNAL_USER_TYPES = (USER_TYPES.keys - NON_INTERNAL_USER_TYPES).freeze
diff --git a/app/services/clusters/applications/check_progress_service.rb b/app/services/clusters/applications/check_progress_service.rb
deleted file mode 100644
index 4a07b955f8e..00000000000
--- a/app/services/clusters/applications/check_progress_service.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class CheckProgressService < BaseHelmService
- def execute
- return unless operation_in_progress?
-
- case pod_phase
- when Gitlab::Kubernetes::Pod::SUCCEEDED
- on_success
- when Gitlab::Kubernetes::Pod::FAILED
- on_failed
- else
- check_timeout
- end
- rescue Kubeclient::HttpError => e
- log_error(e)
-
- app.make_errored!(_('Kubernetes error: %{error_code}') % { error_code: e.error_code })
- end
-
- private
-
- def operation_in_progress?
- raise NotImplementedError
- end
-
- def on_success
- raise NotImplementedError
- end
-
- def pod_name
- raise NotImplementedError
- end
-
- def on_failed
- app.make_errored!(_('Operation failed. Check pod logs for %{pod_name} for more details.') % { pod_name: pod_name })
- end
-
- def timed_out?
- raise NotImplementedError
- end
-
- def pod_phase
- helm_api.status(pod_name)
- end
- end
- end
-end
diff --git a/app/services/metrics/dashboard/grafana_metric_embed_service.rb b/app/services/metrics/dashboard/grafana_metric_embed_service.rb
index e94c8d92c3a..26ccded45f8 100644
--- a/app/services/metrics/dashboard/grafana_metric_embed_service.rb
+++ b/app/services/metrics/dashboard/grafana_metric_embed_service.rb
@@ -51,8 +51,7 @@ module Metrics
# being passed to #get_dashboard (which accepts none)
::Metrics::Dashboard::BaseService
.instance_method(:get_dashboard)
- .bind(self)
- .call() # rubocop:disable Style/MethodCallWithoutArgsParentheses
+ .bind_call(self)
end
def cache_key(*args)
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index f23a688dd48..950642525db 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -20,7 +20,7 @@
- namespace = Namespace.find(params[:namespace_id])
- current_namespace = "#{namespace.kind}: #{namespace.full_path}"
%button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { show_any: 'true', field_name: 'namespace_id', placeholder: current_namespace, update_location: 'true' }, type: 'button' }
- %span.gl-new-dropdown-button-text
+ %span.gl-dropdown-button-text
= current_namespace
= render 'shared/projects/dropdown'
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index a60c3996cf2..652b526ef64 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -143,7 +143,7 @@
.col-sm-9
- placeholder = _('Search for Namespace')
%button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { field_name: 'new_namespace_id', placeholder: placeholder }, type: 'button' }
- %span.gl-new-dropdown-button-text
+ %span.gl-dropdown-button-text
= placeholder
.form-group.row
diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml
index 34aecd31c57..a755cb9f5b0 100644
--- a/app/views/projects/buttons/_clone.html.haml
+++ b/app/views/projects/buttons/_clone.html.haml
@@ -27,28 +27,28 @@
= render_if_exists 'projects/buttons/geo'
= render_if_exists 'projects/buttons/kerberos_clone_field'
%li.divider.mt-2
- %li.pt-2.gl-new-dropdown-item
+ %li.pt-2.gl-dropdown-item
%label.label-bold{ class: 'gl-px-4!' }
= _('Open in your IDE')
- if ssh_enabled?
- escaped_ssh_url_to_repo = CGI.escape(project.ssh_url_to_repo)
%a.dropdown-item.open-with-link{ href: 'vscode://vscode.git/clone?url=' + escaped_ssh_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Visual Studio Code (SSH)')
- if http_enabled?
- escaped_http_url_to_repo = CGI.escape(project.http_url_to_repo)
%a.dropdown-item.open-with-link{ href: 'vscode://vscode.git/clone?url=' + escaped_http_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Visual Studio Code (HTTPS)')
- if ssh_enabled?
%a.dropdown-item.open-with-link{ href: 'jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo=' + escaped_ssh_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('IntelliJ IDEA (SSH)')
- if http_enabled?
%a.dropdown-item.open-with-link{ href: 'jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo=' + escaped_http_url_to_repo }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('IntelliJ IDEA (HTTPS)')
- if show_xcode_link?(@project)
%a.dropdown-item.open-with-link{ href: xcode_uri_to_repo(@project) }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _("Xcode")
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 23dcb7f41e1..1fbc399c3ff 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -4,7 +4,7 @@
- if !project.empty_repo? && can?(current_user, :download_code, project)
- archive_prefix = "#{project.path}-#{ref.tr('/', '-')}"
- .project-action-button.dropdown.gl-new-dropdown.inline>
+ .project-action-button.dropdown.gl-dropdown.inline>
%button.gl-button.btn.btn-default.dropdown-toggle.gl-dropdown-toggle.dropdown-icon-only.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static', data: { qa_selector: 'download_source_code_button' } }
= sprite_icon('download', css_class: 'gl-icon dropdown-icon')
%span.sr-only= _('Select Archive Format')
diff --git a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
index 78fce3f7087..9b7d63797dd 100644
--- a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
+++ b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
@@ -1,53 +1,53 @@
- display_issuable_type = issuable_display_type(@merge_request)
-.btn-group.gl-md-ml-3.gl-display-flex.dropdown.gl-new-dropdown.gl-md-w-auto.gl-w-full
+.btn-group.gl-md-ml-3.gl-display-flex.dropdown.gl-dropdown.gl-md-w-auto.gl-w-full
= button_tag type: 'button', class: "btn dropdown-toggle btn-default btn-md gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret has-tooltip gl-display-none! gl-md-display-inline-flex!", data: { toggle: 'dropdown', title: _('Merge request actions'), testid: 'merge-request-actions', 'aria-label': _('Merge request actions') } do
= sprite_icon "ellipsis_v", size: 16, css_class: "dropdown-icon gl-icon"
= button_tag type: 'button', class: "btn dropdown-toggle btn-default btn-md btn-block gl-button gl-dropdown-toggle gl-md-display-none!", data: { 'toggle' => 'dropdown' } do
- %span.gl-new-dropdown-button-text= _('Merge request actions')
+ %span.gl-dropdown-button-text= _('Merge request actions')
= sprite_icon "chevron-down", size: 16, css_class: "dropdown-icon gl-icon"
.dropdown-menu.dropdown-menu-right
- .gl-new-dropdown-inner
- .gl-new-dropdown-contents
+ .gl-dropdown-inner
+ .gl-dropdown-contents
%ul
- if current_user && moved_mr_sidebar_enabled?
- %li.gl-new-dropdown-item.js-sidebar-subscriptions-widget-root
- %li.gl-new-dropdown-divider
+ %li.gl-dropdown-item.js-sidebar-subscriptions-widget-root
+ %li.gl-dropdown-divider
%hr.dropdown-divider
- if can?(current_user, :update_merge_request, @merge_request)
- %li.gl-new-dropdown-item{ class: "gl-md-display-none!" }
+ %li.gl-dropdown-item{ class: "gl-md-display-none!" }
= link_to edit_project_merge_request_path(@project, @merge_request), class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Edit')
- if @merge_request.open?
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to toggle_draft_merge_request_path(@merge_request), method: :put, class: 'dropdown-item js-draft-toggle-button' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= @merge_request.draft? ? _('Mark as ready') : _('Mark as draft')
- %li.gl-new-dropdown-item.js-close-item
+ %li.gl-dropdown-item.js-close-item
= link_to close_issuable_path(@merge_request), method: :put, class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Close')
= display_issuable_type
- elsif !@merge_request.source_project_missing? && @merge_request.closed?
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to reopen_issuable_path(@merge_request), method: :put, class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Reopen')
= display_issuable_type
- if moved_mr_sidebar_enabled?
- %li.gl-new-dropdown-item.js-sidebar-lock-root
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item.js-sidebar-lock-root
+ %li.gl-dropdown-item
%button.dropdown-item.js-copy-reference{ type: "button", data: { 'clipboard-text': @merge_request.to_reference(full: true) } }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Copy reference')
- unless current_controller?('conflicts')
- unless issuable_author_is_current_user(@merge_request)
- if moved_mr_sidebar_enabled?
- %li.gl-new-dropdown-divider
+ %li.gl-dropdown-divider
%hr.dropdown-divider
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Report abuse')
diff --git a/app/views/projects/merge_requests/_code_dropdown.html.haml b/app/views/projects/merge_requests/_code_dropdown.html.haml
index 5c7fe56095c..2ef89a7bf04 100644
--- a/app/views/projects/merge_requests/_code_dropdown.html.haml
+++ b/app/views/projects/merge_requests/_code_dropdown.html.haml
@@ -1,39 +1,39 @@
-.gl-md-ml-3.dropdown.gl-new-dropdown{ class: "gl-display-none! gl-md-display-flex!" }
+.gl-md-ml-3.dropdown.gl-dropdown{ class: "gl-display-none! gl-md-display-flex!" }
#js-check-out-modal{ data: how_merge_modal_data(@merge_request) }
= button_tag type: 'button', class: "btn dropdown-toggle btn-confirm gl-button gl-dropdown-toggle", data: { toggle: 'dropdown', qa_selector: 'mr_code_dropdown' } do
- %span.gl-new-dropdown-button-text= _('Code')
+ %span.gl-dropdown-button-text= _('Code')
= sprite_icon "chevron-down", size: 16, css_class: "dropdown-icon gl-icon gl-ml-2 gl-mr-0!"
.dropdown-menu.dropdown-menu-right
- .gl-new-dropdown-inner
- .gl-new-dropdown-contents
+ .gl-dropdown-inner
+ .gl-dropdown-contents
%ul
- %li.gl-new-dropdown-section-header
+ %li.gl-dropdown-section-header
%header.dropdown-header
= _('Review changes')
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
%button.dropdown-item.js-check-out-modal-trigger{ type: 'button' }
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Check out branch')
- if current_user
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to ide_merge_request_path(@merge_request), class: 'dropdown-item', target: '_blank', data: { qa_selector: 'open_in_web_ide_button' } do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Open in Web IDE')
- if Gitlab::CurrentSettings.gitpod_enabled && current_user&.gitpod_enabled
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to "#{Gitlab::CurrentSettings.gitpod_url}##{merge_request_url(@merge_request)}", target: '_blank', class: 'dropdown-item' do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Open in Gitpod')
- %li.gl-new-dropdown-divider
+ %li.gl-dropdown-divider
%hr.dropdown-divider
- %li.gl-new-dropdown-section-header
+ %li.gl-dropdown-section-header
%header.dropdown-header
= _('Download')
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to merge_request_path(@merge_request, format: :patch), class: 'dropdown-item', download: '', data: { qa_selector: 'download_email_patches_menu_item' } do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Email patches')
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to merge_request_path(@merge_request, format: :diff), class: 'dropdown-item', download: '', data: { qa_selector: 'download_plain_diff_menu_item' } do
- .gl-new-dropdown-item-text-wrapper
+ .gl-dropdown-item-text-wrapper
= _('Plain diff')
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index 0bd5d1795d0..d080d8be8fe 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,5 +1,5 @@
- if any_projects?(@projects)
- .dropdown.b-dropdown.gl-new-dropdown.btn-group.project-item-select-holder{ class: 'gl-display-inline-flex!' }
+ .dropdown.b-dropdown.gl-dropdown.btn-group.project-item-select-holder{ class: 'gl-display-inline-flex!' }
%a.btn.gl-button.btn-confirm.split-content-button.js-new-project-item-link.block-truncated{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } }
= gl_loading_icon(inline: true, color: 'light')
= project_select_tag :project_path, class: "project-item-select gl-absolute! gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled]
diff --git a/app/views/shared/web_hooks/_test_button.html.haml b/app/views/shared/web_hooks/_test_button.html.haml
index 3ffa45f01be..7a78a32fe87 100644
--- a/app/views/shared/web_hooks/_test_button.html.haml
+++ b/app/views/shared/web_hooks/_test_button.html.haml
@@ -2,12 +2,12 @@
- hook = local_assigns.fetch(:hook)
- triggers = hook.class.triggers
-.hook-test-button.dropdown.gl-new-dropdown.inline>
+.hook-test-button.dropdown.gl-dropdown.inline>
%button.btn.gl-button{ 'data-toggle' => 'dropdown', class: button_class }
= _('Test')
= sprite_icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
- .gl-new-dropdown-inner
+ .gl-dropdown-inner
- triggers.each_value do |event|
- %li.gl-new-dropdown-item
+ %li.gl-dropdown-item
= link_to_test_hook(hook, event)
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 8cce80e2771..40c74e9429f 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1801,7 +1801,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 4
- :idempotent: false
+ :idempotent: true
:tags: []
- :name: pipeline_default:ci_create_cross_project_pipeline
:worker_name: Ci::CreateCrossProjectPipelineWorker
@@ -2721,15 +2721,6 @@
:weight: 1
:idempotent: true
:tags: []
-- :name: merge_requests_delete_branch
- :worker_name: MergeRequests::DeleteBranchWorker
- :feature_category: :source_code_management
- :has_external_dependencies: false
- :urgency: :high
- :resource_boundary: :unknown
- :weight: 1
- :idempotent: true
- :tags: []
- :name: merge_requests_delete_source_branch
:worker_name: MergeRequests::DeleteSourceBranchWorker
:feature_category: :source_code_management
@@ -3000,6 +2991,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: projects_delete_branch
+ :worker_name: Projects::DeleteBranchWorker
+ :feature_category: :source_code_management
+ :has_external_dependencies: false
+ :urgency: :high
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: projects_git_garbage_collect
:worker_name: Projects::GitGarbageCollectWorker
:feature_category: :gitaly
diff --git a/app/workers/merge_requests/delete_branch_worker.rb b/app/workers/merge_requests/delete_branch_worker.rb
deleted file mode 100644
index 6816f9a4b77..00000000000
--- a/app/workers/merge_requests/delete_branch_worker.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-module MergeRequests
- class DeleteBranchWorker
- include ApplicationWorker
-
- data_consistency :always
-
- feature_category :source_code_management
- urgency :high
- idempotent!
-
- def perform(merge_request_id, user_id, branch_name, retarget_branch)
- merge_request = MergeRequest.find_by_id(merge_request_id)
- user = User.find_by_id(user_id)
-
- return unless merge_request.present? && user.present?
-
- ::Branches::DeleteService.new(merge_request.source_project, user).execute(branch_name)
-
- return unless retarget_branch
-
- ::MergeRequests::RetargetChainService.new(project: merge_request.source_project, current_user: user)
- .execute(merge_request)
- end
- end
-end
diff --git a/app/workers/merge_requests/delete_source_branch_worker.rb b/app/workers/merge_requests/delete_source_branch_worker.rb
index 96dde413d5b..da1eca067a9 100644
--- a/app/workers/merge_requests/delete_source_branch_worker.rb
+++ b/app/workers/merge_requests/delete_source_branch_worker.rb
@@ -19,13 +19,14 @@ class MergeRequests::DeleteSourceBranchWorker
return if merge_request.source_branch_sha != source_branch_sha
if Feature.enabled?(:add_delete_branch_worker, merge_request.source_project)
- ::MergeRequests::DeleteBranchWorker.perform_async(merge_request_id, user_id, merge_request.source_branch, true)
+ ::Projects::DeleteBranchWorker.new.perform(merge_request.source_project.id, user_id,
+ merge_request.source_branch)
else
::Branches::DeleteService.new(merge_request.source_project, user).execute(merge_request.source_branch)
-
- ::MergeRequests::RetargetChainService.new(project: merge_request.source_project, current_user: user)
- .execute(merge_request)
end
+
+ ::MergeRequests::RetargetChainService.new(project: merge_request.source_project, current_user: user)
+ .execute(merge_request)
rescue ActiveRecord::RecordNotFound
end
end
diff --git a/app/workers/projects/delete_branch_worker.rb b/app/workers/projects/delete_branch_worker.rb
new file mode 100644
index 00000000000..1949fb67e83
--- /dev/null
+++ b/app/workers/projects/delete_branch_worker.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Projects
+ class DeleteBranchWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ feature_category :source_code_management
+ urgency :high
+ idempotent!
+
+ def perform(project_id, user_id, branch_name)
+ project = Project.find_by_id(project_id)
+ user = User.find_by_id(user_id)
+
+ return unless project.present? && user.present?
+ return unless project.repository.branch_exists?(branch_name)
+
+ delete_service_result = ::Branches::DeleteService.new(project, user)
+ .execute(branch_name)
+
+ return unless Feature.enabled?(:track_and_raise_delete_source_errors, project)
+ # Only want to raise on 400 to avoid permission and non existant branch error
+ return unless delete_service_result[:http_status] == 400
+
+ delete_service_result.track_and_raise_exception
+ end
+ end
+end
diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb
index 8974ddce47b..f31f006eec1 100644
--- a/app/workers/run_pipeline_schedule_worker.rb
+++ b/app/workers/run_pipeline_schedule_worker.rb
@@ -10,6 +10,8 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
queue_namespace :pipeline_creation
feature_category :continuous_integration
+ deduplicate :until_executed
+ idempotent!
def perform(schedule_id, user_id)
schedule = Ci::PipelineSchedule.find_by_id(schedule_id)
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index 0aa77bdb66b..e3b208cd495 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -25,6 +25,7 @@
- cluster_cost_management
- code_quality
- code_review
+- code_suggestions
- code_testing
- commerce_integrations
- compliance_management
diff --git a/config/feature_flags/development/disable_pagination_counts_on_jobs_api.yml b/config/feature_flags/development/disable_pagination_counts_on_jobs_api.yml
new file mode 100644
index 00000000000..b4774fdf2c7
--- /dev/null
+++ b/config/feature_flags/development/disable_pagination_counts_on_jobs_api.yml
@@ -0,0 +1,8 @@
+---
+name: disable_pagination_counts_on_jobs_api
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105814
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/384399
+milestone: '15.7'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/markdown_image_attributes.yml b/config/feature_flags/development/markdown_image_attributes.yml
new file mode 100644
index 00000000000..ddc2ca6ac63
--- /dev/null
+++ b/config/feature_flags/development/markdown_image_attributes.yml
@@ -0,0 +1,8 @@
+---
+name: markdown_image_attributes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104904
+rollout_issue_url:
+milestone: '15.7'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/config/feature_flags/development/actors_aware_gitaly_calls.yml b/config/feature_flags/development/track_and_raise_delete_source_errors.yml
index 0ae6140c579..5f34ab47f19 100644
--- a/config/feature_flags/development/actors_aware_gitaly_calls.yml
+++ b/config/feature_flags/development/track_and_raise_delete_source_errors.yml
@@ -1,8 +1,8 @@
---
-name: actors_aware_gitaly_calls
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101218
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381516
+name: track_and_raise_delete_source_errors
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103842
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/382440
milestone: '15.6'
type: development
-group: group::gitaly
+group: group::code review
default_enabled: false
diff --git a/config/feature_flags/ops/suggested_reviewers_internal_api.yml b/config/feature_flags/ops/suggested_reviewers_internal_api.yml
new file mode 100644
index 00000000000..ed3045765d5
--- /dev/null
+++ b/config/feature_flags/ops/suggested_reviewers_internal_api.yml
@@ -0,0 +1,8 @@
+---
+name: suggested_reviewers_internal_api
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103881
+rollout_issue_url:
+milestone: '15.7'
+type: ops
+group: group::applied ml
+default_enabled: true
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 732d46d284b..0435e66b720 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -1258,6 +1258,11 @@ production: &base
# The URL to the Kubernetes API proxy (used by GitLab users)
# external_k8s_proxy_url: https://localhost:8154 # default: nil
+ gitlab_suggested_reviewers:
+ # File that contains the secret key for verifying access for gitlab-suggested-reviewers.
+ # Default is '.gitlab_suggested_reviewers_secret' relative to Rails.root (i.e. root of the GitLab app).
+ # secret_file: /home/git/gitlab/.gitlab_suggested_reviewers_secret
+
## GitLab Elasticsearch settings
elasticsearch:
indexer_path: /home/git/gitlab-elasticsearch-indexer/
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index d0a67c13622..dd5770447c3 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -853,6 +853,12 @@ Settings.gitlab_kas['internal_url'] ||= 'grpc://localhost:8153'
# Settings.gitlab_kas['external_k8s_proxy_url'] ||= 'grpc://localhost:8154' # NOTE: Do not set a default until all distributions have been updated with a correct value
#
+# GitLab Suggested Reviewers
+#
+Settings['gitlab_suggested_reviewers'] ||= Settingslogic.new({})
+Settings.gitlab_suggested_reviewers['secret_file'] ||= Rails.root.join('.gitlab_suggested_reviewers_secret')
+
+#
# Repositories
#
Settings['repositories'] ||= Settingslogic.new({})
diff --git a/config/initializers/gitlab_suggested_reviewers_secret.rb b/config/initializers/gitlab_suggested_reviewers_secret.rb
new file mode 100644
index 00000000000..01c7299c414
--- /dev/null
+++ b/config/initializers/gitlab_suggested_reviewers_secret.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+return unless Gitlab.com? && Gitlab.ee?
+
+Gitlab::AppliedMl::SuggestedReviewers.ensure_secret!
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 46305512a7a..5b1b78b3046 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -303,8 +303,6 @@
- 1
- - merge_requests_create_approval_note
- 1
-- - merge_requests_delete_branch
- - 1
- - merge_requests_delete_source_branch
- 1
- - merge_requests_execute_approval_hooks
@@ -403,6 +401,8 @@
- 1
- - projects_after_import
- 1
+- - projects_delete_branch
+ - 1
- - projects_git_garbage_collect
- 1
- - projects_import_export_parallel_project_export
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 3f5e11129f9..52422cba111 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -86,6 +86,7 @@ of the listed keywords use the value defined in the `default` section.
- [`artifacts`](#artifacts)
- [`before_script`](#before_script)
- [`cache`](#cache)
+- [`hooks`](#hooks)
- [`image`](#image)
- [`interruptible`](#interruptible)
- [`retry`](#retry)
@@ -1862,6 +1863,47 @@ rspec:
- [Reuse configuration sections by using `extends`](yaml_optimization.md#use-extends-to-reuse-configuration-sections).
- Use `extends` to reuse configuration from [included configuration files](yaml_optimization.md#use-extends-and-include-together).
+### `hooks`
+
+> Introduced in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_hooks_pre_get_sources_script`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `ci_hooks_pre_get_sources_script`.
+The feature is not ready for production use.
+
+Use `hooks` to specify lists of commands to execute on the runner
+at certain stages of job execution, like before retrieving the Git repository.
+
+**Keyword type**: Job keyword. You can use it only as part of a job or in the
+[`default` section](#default).
+
+**Possible inputs**:
+
+- A hash of hooks and their commands. Available hooks: `pre_get_sources_script`.
+
+#### `hooks:pre_get_sources_script`
+
+> Introduced in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `ci_hooks_pre_get_sources_script`. Disabled by default.
+
+Use `hooks:pre_get_sources_script` to specify a list of commands to execute on the runner
+before retrieving the Git repository and any submodules. You can use it
+to adjust the Git client configuration first, for example.
+
+**Related topics**:
+
+- [GitLab Runner configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section)
+
+**Example of `hooks:pre_get_sources_script`**:
+
+```yaml
+job1:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello job1 pre_get_sources_script'
+ script: echo 'hello job1 script'
+```
+
### `image`
Use `image` to specify a Docker image that the job runs in.
diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md
index 44e619ed166..69b74127891 100644
--- a/doc/operations/incident_management/alerts.md
+++ b/doc/operations/incident_management/alerts.md
@@ -149,7 +149,7 @@ To change an alert's status:
1. On the right sidebar, select **Edit**.
1. Select a status.
-To stop email notifications for alert reoccurrences in projects with [email notifications enabled](paging.md#email-notifications-for-alerts),
+To stop email notifications for alert recurrences in projects with [email notifications enabled](paging.md#email-notifications-for-alerts),
change the alert's status away from **Triggered**.
#### Resolve an alert by closing the linked incident
diff --git a/doc/operations/incident_management/linked_resources.md b/doc/operations/incident_management/linked_resources.md
index 40b2bbdc757..eb289076424 100644
--- a/doc/operations/incident_management/linked_resources.md
+++ b/doc/operations/incident_management/linked_resources.md
@@ -15,9 +15,9 @@ you can add linked resources to an incident issue.
Resources you might want link to:
-- Zoom meetings
-- Slack channels or threads
-- Google Docs
+- The incident Slack channel
+- Zoom meeting
+- Resources for resolving the incidents
## View linked resources of an incident
diff --git a/doc/operations/metrics/alerts.md b/doc/operations/metrics/alerts.md
index ffd304c8897..aa46384688b 100644
--- a/doc/operations/metrics/alerts.md
+++ b/doc/operations/metrics/alerts.md
@@ -6,8 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Set up alerts for Prometheus metrics **(FREE)**
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/42640) to GitLab Free in 12.10.
-
After [configuring metrics for your CI/CD environment](index.md), you can set up
alerting for Prometheus metrics, and
[trigger actions from alerts](#trigger-actions-from-alerts) to notify
@@ -15,7 +13,7 @@ your team when environment performance falls outside of the boundaries you set.
## Prometheus cluster integrations
-Alerts are not currently supported for [Prometheus cluster integrations](../../user/clusters/integrations.md).
+Alerts are not supported for [Prometheus cluster integrations](../../user/clusters/integrations.md).
## Trigger actions from alerts **(ULTIMATE)**
diff --git a/doc/tutorials/index.md b/doc/tutorials/index.md
index 118c2acdfa9..8e03029e501 100644
--- a/doc/tutorials/index.md
+++ b/doc/tutorials/index.md
@@ -31,6 +31,7 @@ the most out of GitLab.
|-------|-------------|--------------------|
| [Make your first Git commit](make_your_first_git_commit.md) | Create a project, edit a file, and commit changes to a Git repository from the command line. | **{star}** |
| [Start using Git on the command line](../gitlab-basics/start-using-git.md) | Learn how to set up Git, clone repositories, and work with branches. | **{star}** |
+| [Take advantage of Git rebase](https://about.gitlab.com/blog/2022/10/06/take-advantage-of-git-rebase/)| Learn how to use the `rebase` command in your workflow. | |
| [Git cheat sheet](https://about.gitlab.com/images/press/git-cheat-sheet.pdf) | Download a PDF of common Git commands. | |
## Plan your work in projects
@@ -84,6 +85,7 @@ GitLab can check your application for security vulnerabilities.
| Topic | Description | Good for beginners |
|-------|-------------|--------------------|
| [Set up dependency scanning](https://about.gitlab.com/blog/2021/01/14/try-dependency-scanning/) | Try out dependency scanning, which checks for known vulnerabilities in dependencies. | **{star}** |
+| [Get started with GitLab application security](../user/application_security/get-started-security.md) | Follow recommended steps to set up security tools. | |
## Work with a self-managed instance
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index b6f3ba1cfdd..f0dbfb854bb 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -1017,8 +1017,31 @@ Do not change to a reference style link.
![alt text](img/markdown_logo.png "Title Text")
-In the rare case where you must set a specific height or width for an image,
-you can use the `img` HTML tag instead of Markdown and set its `height` and
+#### Change the image dimensions
+
+> Introduced in GitLab 15.7 [with a flag](../administration/feature_flags.md) named `markdown_image_attributes`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `markdown_image_attributes`.
+On GitLab.com, this feature is available.
+The feature is not ready for production use.
+
+You can control the width and height of an image by following the image with
+an attribute list.
+The value must an integer with a unit of either `px` (default) or `%`.
+
+For example
+
+```markdown
+![alt text](img/markdown_logo.png "Title Text"){width=100 height=100px}
+
+![alt text](img/markdown_logo.png "Title Text"){width=75%}
+```
+
+![alt text](img/markdown_logo.png "Title Text"){width=100 height=100px}
+
+You can also use the `img` HTML tag instead of Markdown and set its `height` and
`width` parameters.
#### Videos
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index 9e41e1c0d8f..e988ac4bf3b 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -55,7 +55,9 @@ module API
builds = filter_builds(builds, params[:scope])
builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, :tags, pipeline: :project)
- present paginate(builds), with: Entities::Ci::Job
+ without_count = Feature.enabled?(:disable_pagination_counts_on_jobs_api, user_project)
+
+ present paginate(builds, without_count: without_count), with: Entities::Ci::Job
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index c42b6fd8951..777d5019a29 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -146,7 +146,7 @@ module API
end
optional :unique_counters, type: Hash do
- optional :agent_users_using_ci_tunnel, type: Set[Integer], desc: 'A set of user ids that have interacted a CI Tunnel to'
+ optional :agent_users_using_ci_tunnel, type: Array[Integer], desc: 'An array of user ids that have interacted with CI Tunnel'
end
end
post '/', feature_category: :kubernetes_management do
diff --git a/lib/banzai/filter/attributes_filter.rb b/lib/banzai/filter/attributes_filter.rb
new file mode 100644
index 00000000000..1cf7155b80e
--- /dev/null
+++ b/lib/banzai/filter/attributes_filter.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ # Looks for attributes that are specified for an element. Follows the basic syntax laid out
+ # in https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/attributes.md
+ # For example,
+ # ![](http://example.com/image.jpg){width=50%}
+ #
+ # However we currently have the following limitations:
+ # - only support images
+ # - only support the `width` and `height` attributes
+ # - attributes can not span multiple lines
+ # - unsupported attributes are thrown away
+ class AttributesFilter < HTML::Pipeline::Filter
+ CSS = 'img'
+ XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
+
+ ATTRIBUTES_PATTERN = %r{\A(?<matched>\{(?<attributes>.{1,100})\})}.freeze
+ WIDTH_HEIGHT_REGEX = %r{\A(?<name>height|width)="?(?<size>[\w%]{1,10})"?\z}.freeze
+ VALID_SIZE_REGEX = %r{\A\d{1,4}(%|px)?\z}.freeze
+
+ def call
+ return doc unless Feature.enabled?(:markdown_image_attributes, group)
+
+ doc.xpath(XPATH).each do |img|
+ sibling = img.next
+ next unless sibling && sibling.text? && sibling.content.first == '{'
+
+ match = sibling.content.match(ATTRIBUTES_PATTERN)
+ next unless match && match[:attributes]
+
+ match[:attributes].split(' ').each do |attribute|
+ next unless attribute.match?(WIDTH_HEIGHT_REGEX)
+
+ attribute_match = attribute.match(WIDTH_HEIGHT_REGEX)
+ img[attribute_match[:name].to_sym] = attribute_match[:size] if valid_size?(attribute_match[:size])
+ end
+
+ sibling.content = sibling.content.sub(match[:matched], '')
+ end
+
+ doc
+ end
+
+ private
+
+ def valid_size?(size)
+ size.match?(VALID_SIZE_REGEX)
+ end
+
+ def group
+ context[:group] || context[:project]&.group
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 0727652d88c..5e34ff8a33c 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -22,6 +22,7 @@ module Banzai
Filter::MermaidFilter,
Filter::VideoLinkFilter,
Filter::AudioLinkFilter,
+ Filter::AttributesFilter,
Filter::ImageLazyLoadFilter,
Filter::ImageLinkFilter,
*metrics_filters,
diff --git a/lib/gitlab/gitaly_client/with_feature_flag_actors.rb b/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
index 92fc524b724..3d81292da16 100644
--- a/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
+++ b/lib/gitlab/gitaly_client/with_feature_flag_actors.rb
@@ -16,8 +16,6 @@ module Gitlab
# gitaly_client_call performs Gitaly calls including collected feature flag actors. The actors are retrieved
# from repository actor and memoized. The service must set `self.repository_actor = a_repository` beforehand.
def gitaly_client_call(*args, **kargs)
- return GitalyClient.call(*args, **kargs) unless actors_aware_gitaly_calls?
-
unless repository_actor
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
Feature::InvalidFeatureFlagError.new("gitaly_client_call called without setting repository_actor")
@@ -34,11 +32,8 @@ module Gitlab
end
end
- # gitaly_feature_flag_actors returns a hash of actors implied from input repository. If actors_aware_gitaly_calls
- # flag is not on, this method returns an empty hash.
+ # gitaly_feature_flag_actors returns a hash of actors implied from input repository.
def gitaly_feature_flag_actors(repository)
- return {} unless actors_aware_gitaly_calls?
-
container = find_repository_container(repository)
{
repository: repository,
@@ -92,10 +87,6 @@ module Gitlab
repository.container
end
end
-
- def actors_aware_gitaly_calls?
- Feature.enabled?(:actors_aware_gitaly_calls)
- end
end
end
end
diff --git a/lib/gitlab/pagination/offset_pagination.rb b/lib/gitlab/pagination/offset_pagination.rb
index 00304f48dc5..3d10bcef019 100644
--- a/lib/gitlab/pagination/offset_pagination.rb
+++ b/lib/gitlab/pagination/offset_pagination.rb
@@ -11,15 +11,15 @@ module Gitlab
@request_context = request_context
end
- def paginate(relation, exclude_total_headers: false, skip_default_order: false)
- paginate_with_limit_optimization(add_default_order(relation, skip_default_order: skip_default_order)).tap do |data|
+ def paginate(relation, exclude_total_headers: false, skip_default_order: false, without_count: false)
+ paginate_with_limit_optimization(add_default_order(relation, skip_default_order: skip_default_order), without_count: without_count).tap do |data|
add_pagination_headers(data, exclude_total_headers)
end
end
private
- def paginate_with_limit_optimization(relation)
+ def paginate_with_limit_optimization(relation, without_count:)
pagination_data = if needs_pagination?(relation)
relation.page(params[:page]).per(params[:per_page])
else
@@ -28,8 +28,7 @@ module Gitlab
return pagination_data unless pagination_data.is_a?(ActiveRecord::Relation)
- limited_total_count = pagination_data.total_count_with_limit
- if limited_total_count > Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT
+ if without_count || exceeeds_count?(pagination_data)
# The call to `total_count_with_limit` memoizes `@arel` because of a call to `references_eager_loaded_tables?`
# We need to call `reset` because `without_count` relies on `@arel` being unmemoized
pagination_data.reset.without_count
@@ -78,6 +77,12 @@ module Gitlab
# Ensure there is in total at least 1 page
[paginated_data.total_pages, 1].max
end
+
+ def exceeeds_count?(paginated_data)
+ limited_total_count = paginated_data.total_count_with_limit
+
+ limited_total_count > Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT
+ end
end
end
end
diff --git a/lib/gitlab/patch/prependable.rb b/lib/gitlab/patch/prependable.rb
index 1ed341e1c26..b974c0b2c7f 100644
--- a/lib/gitlab/patch/prependable.rb
+++ b/lib/gitlab/patch/prependable.rb
@@ -26,7 +26,7 @@ module Gitlab
# https://github.com/rails/rails/pull/42067
#
# Let's keep our own implementation, until the issue is fixed
- Module.instance_method(:prepend_features).bind(self).call(base)
+ Module.instance_method(:prepend_features).bind_call(self, base)
if const_defined?(:ClassMethods)
klass_methods = const_get(:ClassMethods, false)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 425384ca40d..709d15193db 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12278,6 +12278,9 @@ msgstr ""
msgid "DastConfig|Not enabled"
msgstr ""
+msgid "DastProfiles|/graphql"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -12425,6 +12428,9 @@ msgstr ""
msgid "DastProfiles|Monitors all HTTP requests sent to the target to find potential vulnerabilities."
msgstr ""
+msgid "DastProfiles|Must allow introspection queries to request the API schema. %{linkStart}How do I enable introspection%{linkEnd}?"
+msgstr ""
+
msgid "DastProfiles|New scanner profile"
msgstr ""
@@ -19037,6 +19043,12 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "GraphQL"
+msgstr ""
+
+msgid "GraphQL endpoint path"
+msgstr ""
+
msgid "GraphViewType|Job dependencies"
msgstr ""
@@ -28787,9 +28799,6 @@ msgstr ""
msgid "Opens new window"
msgstr ""
-msgid "Operation failed. Check pod logs for %{pod_name} for more details."
-msgstr ""
-
msgid "Operation not allowed"
msgstr ""
diff --git a/package.json b/package.json
index 3377ee82c43..2e44fb8be2f 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,7 @@
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "3.13.0",
- "@gitlab/ui": "50.1.2",
+ "@gitlab/ui": "51.1.1",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20221114183058",
"@rails/actioncable": "6.1.4-7",
diff --git a/qa/qa/page/component/dropdown.rb b/qa/qa/page/component/dropdown.rb
index 2d05bea50a9..58a6fee5e2b 100644
--- a/qa/qa/page/component/dropdown.rb
+++ b/qa/qa/page/component/dropdown.rb
@@ -9,20 +9,20 @@ module QA
def select_item(item_text)
return super if use_select2?
- find('li.gl-new-dropdown-item', text: item_text, match: :prefer_exact).click
+ find('li.gl-dropdown-item', text: item_text, match: :prefer_exact).click
end
def has_item?(item_text)
return super if use_select2?
- has_css?('li.gl-new-dropdown-item', text: item_text, match: :prefer_exact)
+ has_css?('li.gl-dropdown-item', text: item_text, match: :prefer_exact)
end
def current_selection
return super if use_select2?
expand_select_list unless dropdown_open?
- find('span.gl-new-dropdown-button-text').text
+ find('span.gl-dropdown-button-text').text
end
def clear_current_selection_if_present
@@ -69,7 +69,7 @@ module QA
raise QA::Page::Base::ElementNotFound, %(Couldn't find option named "#{item_text}")
end
- find('li.gl-new-dropdown-item span:nth-child(2)', text: item_text, exact_text: true).click
+ find('li.gl-dropdown-item span:nth-child(2)', text: item_text, exact_text: true).click
end
def expand_select_list
@@ -90,7 +90,7 @@ module QA
def dropdown_open?
return super if use_select2?
- has_css?('ul.gl-new-dropdown-contents', wait: 1)
+ has_css?('ul.gl-dropdown-contents', wait: 1)
end
private
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index f3632e7370c..9c2c90b147c 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -331,12 +331,12 @@ RSpec.describe Import::GithubController do
describe "GET realtime_changes" do
let(:user) { create(:user) }
- it_behaves_like 'a GitHub-ish import controller: GET realtime_changes'
-
before do
assign_session_token(provider)
end
+ it_behaves_like 'a GitHub-ish import controller: GET realtime_changes'
+
it 'includes stats in response' do
create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo')
diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb
index 53d3aec9cff..4b49e8f4bc6 100644
--- a/spec/features/admin/users/users_spec.rb
+++ b/spec/features/admin/users/users_spec.rb
@@ -604,8 +604,8 @@ RSpec.describe 'Admin::Users', feature_category: :user_management do
def sort_by(option)
page.within('.filtered-search-block') do
- find('.gl-new-dropdown').click
- find('.gl-new-dropdown-item', text: option).click
+ find('.gl-dropdown').click
+ find('.gl-dropdown-item', text: option).click
end
end
end
diff --git a/spec/features/alert_management/alert_details_spec.rb b/spec/features/alert_management/alert_details_spec.rb
index 26233fbda64..abb97d3be4d 100644
--- a/spec/features/alert_management/alert_details_spec.rb
+++ b/spec/features/alert_management/alert_details_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe 'Alert details', :js, feature_category: :incident_management do
expect(alert_status).to have_content('Triggered')
find('.gl-button').click
- find('.gl-new-dropdown-item', text: 'Acknowledged').click
+ find('.gl-dropdown-item', text: 'Acknowledged').click
wait_for_requests
diff --git a/spec/features/boards/board_filters_spec.rb b/spec/features/boards/board_filters_spec.rb
index 4ed1a7a58f4..dee63be8119 100644
--- a/spec/features/boards/board_filters_spec.rb
+++ b/spec/features/boards/board_filters_spec.rb
@@ -191,7 +191,7 @@ RSpec.describe 'Issue board filters', :js, feature_category: :team_planning do
end
def expect_filtered_search_dropdown_results(filter_dropdown, count)
- expect(filter_dropdown).to have_selector('.gl-new-dropdown-item', count: count)
+ expect(filter_dropdown).to have_selector('.gl-dropdown-item', count: count)
end
def visit_project_board
diff --git a/spec/features/boards/sidebar_assignee_spec.rb b/spec/features/boards/sidebar_assignee_spec.rb
index 44ae0c5a3b4..e3de594f856 100644
--- a/spec/features/boards/sidebar_assignee_spec.rb
+++ b/spec/features/boards/sidebar_assignee_spec.rb
@@ -113,7 +113,7 @@ RSpec.describe 'Project issue boards sidebar assignee', :js, quarantine: 'https:
page.within(assignees_widget) do
click_button('Edit')
- expect(find('.dropdown-menu')).to have_selector('.gl-new-dropdown-item-check-icon')
+ expect(find('.dropdown-menu')).to have_selector('.gl-dropdown-item-check-icon')
end
end
end
diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb
index ffd1d83b619..11ec38f637b 100644
--- a/spec/features/groups/board_spec.rb
+++ b/spec/features/groups/board_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe 'Group Boards', feature_category: :team_planning do
page.within("[data-testid='project-select-dropdown']") do
find('button.gl-dropdown-toggle').click
- find('.gl-new-dropdown-item button').click
+ find('.gl-dropdown-item button').click
end
click_button 'Create issue'
diff --git a/spec/features/incidents/incident_timeline_events_spec.rb b/spec/features/incidents/incident_timeline_events_spec.rb
index 2aed289884b..3a73ea50247 100644
--- a/spec/features/incidents/incident_timeline_events_spec.rb
+++ b/spec/features/incidents/incident_timeline_events_spec.rb
@@ -50,9 +50,9 @@ RSpec.describe 'Incident timeline events', :js, feature_category: :incident_mana
it 'shows the confirmation modal and edits the event' do
click_button _('More actions')
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content(_('Edit'))
- page.find('.gl-new-dropdown-item-text-primary', text: _('Edit')).click
+ page.find('.gl-dropdown-item-text-primary', text: _('Edit')).click
end
expect(page).to have_selector('.common-note-form')
@@ -82,9 +82,9 @@ RSpec.describe 'Incident timeline events', :js, feature_category: :incident_mana
it 'shows the confirmation modal and deletes the event' do
click_button _('More actions')
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content(_('Delete'))
- page.find('.gl-new-dropdown-item-text-primary', text: 'Delete').click
+ page.find('.gl-dropdown-item-text-primary', text: 'Delete').click
end
page.within '.modal' do
diff --git a/spec/features/issuables/shortcuts_issuable_spec.rb b/spec/features/issuables/shortcuts_issuable_spec.rb
index dea0137f654..0190111b2f0 100644
--- a/spec/features/issuables/shortcuts_issuable_spec.rb
+++ b/spec/features/issuables/shortcuts_issuable_spec.rb
@@ -87,7 +87,7 @@ RSpec.describe 'Blob shortcuts', :js, feature_category: :team_planning do
it "opens milestones dropdown for editing" do
find('body').native.send_key('m')
- expect(find('[data-testid="milestone-edit"]')).to have_selector('.gl-new-dropdown-inner')
+ expect(find('[data-testid="milestone-edit"]')).to have_selector('.gl-dropdown-inner')
end
end
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index d434fa9441f..2898c97c2e9 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe 'New/edit issue', :js, feature_category: :team_planning do
# To work around this, we have to hold on to and call to the original implementation manually.
original_issue_dropdown_options = FormHelper.instance_method(:assignees_dropdown_options)
allow_any_instance_of(FormHelper).to receive(:assignees_dropdown_options).and_wrap_original do |original, *args|
- options = original_issue_dropdown_options.bind(original.receiver).call(*args)
+ options = original_issue_dropdown_options.bind_call(original.receiver, *args)
options[:data][:per_page] = 2
options
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 97739908053..223832a6ede 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -416,7 +416,7 @@ RSpec.describe "Issues > User edits issue", :js, feature_category: :team_plannin
find('.gl-form-input', visible: true).send_keys "\"#{milestones[0].title}\""
wait_for_requests
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content milestones[0].title
end
end
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index 08f9b8eda13..73670772402 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -290,6 +290,13 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures 'KrokiFilter' do
expect(doc).to parse_kroki
end
+
+ aggregate_failures 'AttributeFilter' do
+ img = doc.at_css('img[alt="Sized Image"]')
+
+ expect(img.attr('width')).to eq('75%')
+ expect(img.attr('height')).to eq('100')
+ end
end
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 22b0f344606..0c269f55212 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -17,10 +17,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: 'master', to: 'master')
select_using_dropdown 'from', 'feature'
- expect(find('.js-compare-from-dropdown .gl-new-dropdown-button-text')).to have_content('feature')
+ expect(find('.js-compare-from-dropdown .gl-dropdown-button-text')).to have_content('feature')
select_using_dropdown 'to', 'binary-encoding'
- expect(find('.js-compare-to-dropdown .gl-new-dropdown-button-text')).to have_content('binary-encoding')
+ expect(find('.js-compare-to-dropdown .gl-dropdown-button-text')).to have_content('binary-encoding')
click_button 'Compare'
@@ -32,8 +32,8 @@ RSpec.describe "Compare", :js do
it "pre-populates fields" do
visit project_compare_index_path(project, from: "master", to: "master")
- expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("master")
- expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("master")
+ expect(find(".js-compare-from-dropdown .gl-dropdown-button-text")).to have_content("master")
+ expect(find(".js-compare-to-dropdown .gl-dropdown-button-text")).to have_content("master")
end
it_behaves_like 'compares branches'
@@ -99,7 +99,7 @@ RSpec.describe "Compare", :js do
find(".js-compare-from-dropdown .compare-dropdown-toggle").click
- expect(find(".js-compare-from-dropdown .gl-new-dropdown-contents")).to have_selector('li.gl-new-dropdown-item', count: 1)
+ expect(find(".js-compare-from-dropdown .gl-dropdown-contents")).to have_selector('li.gl-dropdown-item', count: 1)
end
context 'when commit has overflow', :js do
@@ -153,10 +153,10 @@ RSpec.describe "Compare", :js do
visit project_compare_index_path(project, from: "master", to: "master")
select_using_dropdown "from", "v1.0.0"
- expect(find(".js-compare-from-dropdown .gl-new-dropdown-button-text")).to have_content("v1.0.0")
+ expect(find(".js-compare-from-dropdown .gl-dropdown-button-text")).to have_content("v1.0.0")
select_using_dropdown "to", "v1.1.0"
- expect(find(".js-compare-to-dropdown .gl-new-dropdown-button-text")).to have_content("v1.1.0")
+ expect(find(".js-compare-to-dropdown .gl-dropdown-button-text")).to have_content("v1.1.0")
click_button "Compare"
expect(page).to have_content "Commits"
diff --git a/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb b/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb
index 029479d6b95..83e9e3d6617 100644
--- a/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb
+++ b/spec/features/projects/pages/user_configures_pages_pipeline_spec.rb
@@ -46,14 +46,14 @@ RSpec.describe 'Pages edits pages settings', :js do
Feature.disable(:use_pipeline_wizard_for_pages)
end
+ after do
+ Feature.enable(:use_pipeline_wizard_for_pages)
+ end
+
it 'shows configure pages instructions' do
visit project_pages_path(project)
expect(page).to have_content('Configure pages')
end
-
- after do
- Feature.enable(:use_pipeline_wizard_for_pages)
- end
end
end
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index e569fef76f8..3f1f834bfb1 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -64,7 +64,7 @@ RSpec.describe 'Pipeline Schedules', :js do
it 'shows the pipeline schedule with default ref' do
page.within('[data-testid="schedule-target-ref"]') do
- expect(first('.gl-new-dropdown-button-text').text).to eq('master')
+ expect(first('.gl-dropdown-button-text').text).to eq('master')
end
end
end
@@ -77,7 +77,7 @@ RSpec.describe 'Pipeline Schedules', :js do
it 'shows the pipeline schedule with default ref' do
page.within('[data-testid="schedule-target-ref"]') do
- expect(first('.gl-new-dropdown-button-text').text).to eq('master')
+ expect(first('.gl-dropdown-button-text').text).to eq('master')
end
end
end
diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
index 9d3ac71a875..a99a5b7ac4a 100644
--- a/spec/features/projects/pipelines/legacy_pipelines_spec.rb
+++ b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
@@ -598,8 +598,8 @@ RSpec.describe 'Pipelines', :js do
it 'changes the Pipeline ID column for Pipeline IID' do
page.find('[data-testid="pipeline-key-dropdown"]').click
- within '.gl-new-dropdown-contents' do
- dropdown_options = page.find_all '.gl-new-dropdown-item'
+ within '.gl-dropdown-contents' do
+ dropdown_options = page.find_all '.gl-dropdown-item'
dropdown_options[1].click
end
@@ -800,7 +800,7 @@ RSpec.describe 'Pipelines', :js do
page.within '[data-testid="ref-select"]' do
find('[data-testid="search-refs"]').native.send_keys('fix')
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content('fix')
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 8260786adac..4378b709d9e 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -596,8 +596,8 @@ RSpec.describe 'Pipelines', :js do
it 'changes the Pipeline ID column for Pipeline IID' do
page.find('[data-testid="pipeline-key-dropdown"]').click
- within '.gl-new-dropdown-contents' do
- dropdown_options = page.find_all '.gl-new-dropdown-item'
+ within '.gl-dropdown-contents' do
+ dropdown_options = page.find_all '.gl-dropdown-item'
dropdown_options[1].click
end
@@ -825,7 +825,7 @@ RSpec.describe 'Pipelines', :js do
page.within '[data-testid="ref-select"]' do
find('[data-testid="search-refs"]').native.send_keys('fix')
- page.within '.gl-new-dropdown-contents' do
+ page.within '.gl-dropdown-contents' do
expect(page).to have_content('fix')
end
end
diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb
index 1df37eef6a9..4a3bc6b82e6 100644
--- a/spec/features/projects/show/user_manages_notifications_spec.rb
+++ b/spec/features/projects/show/user_manages_notifications_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do
click_notifications_button
page.within first('[data-testid="notification-dropdown"]') do
- expect(page.find('.gl-new-dropdown-item.is-active')).to have_content('On mention')
+ expect(page.find('.gl-dropdown-item.is-active')).to have_content('On mention')
expect(page).to have_css('[data-testid="notifications-icon"]')
end
end
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 14885813d93..38b2a8381bb 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -399,3 +399,7 @@ Bob -> Sara : Hello
[beard]-:>[foul mouth]
]
```
+
+### Image Attributes
+
+![Sized Image](app/assets/images/touch-icon-ipad.png){width=75% height=100}
diff --git a/spec/frontend/boards/board_list_spec.js b/spec/frontend/boards/board_list_spec.js
index eddc916ed14..34c0504143c 100644
--- a/spec/frontend/boards/board_list_spec.js
+++ b/spec/frontend/boards/board_list_spec.js
@@ -198,6 +198,13 @@ describe('Board list component', () => {
expect(findDraggable().exists()).toBe(true);
});
+ it('sets delay and delayOnTouchOnly attributes on board list', () => {
+ const listEl = wrapper.findComponent({ ref: 'list' });
+
+ expect(listEl.attributes('delay')).toBe('100');
+ expect(listEl.attributes('delayontouchonly')).toBe('true');
+ });
+
describe('handleDragOnStart', () => {
it('adds a class `is-dragging` to document body', () => {
expect(document.body.classList.contains('is-dragging')).toBe(false);
diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js
index 39b2fe2ebbf..82e7ab48e7d 100644
--- a/spec/frontend/boards/components/board_content_spec.js
+++ b/spec/frontend/boards/components/board_content_spec.js
@@ -150,6 +150,13 @@ describe('BoardContent', () => {
expect(style).toBe('height: 900px;');
});
+
+ it('sets delay and delayOnTouchOnly attributes on board list', () => {
+ const listEl = wrapper.findComponent({ ref: 'list' });
+
+ expect(listEl.attributes('delay')).toBe('100');
+ expect(listEl.attributes('delayontouchonly')).toBe('true');
+ });
});
describe('when issuableType is not issue', () => {
diff --git a/spec/frontend/boards/project_select_spec.js b/spec/frontend/boards/project_select_spec.js
index 7ff34ffdf9e..4324e7068e0 100644
--- a/spec/frontend/boards/project_select_spec.js
+++ b/spec/frontend/boards/project_select_spec.js
@@ -156,7 +156,7 @@ describe('ProjectSelect component', () => {
});
it('renders the name of the selected project', () => {
- expect(findGlDropdown().find('.gl-new-dropdown-button-text').text()).toBe(
+ expect(findGlDropdown().find('.gl-dropdown-button-text').text()).toBe(
mockProjectsList1[0].name,
);
});
diff --git a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
index 6ad8a9de8d3..331a0a474a3 100644
--- a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
+++ b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap
@@ -14,15 +14,15 @@ exports[`content_editor/components/toolbar_link_button renders dropdown componen
</div>
</form>
</li>
- <li role=\\"presentation\\" class=\\"gl-new-dropdown-divider\\">
+ <li role=\\"presentation\\" class=\\"gl-dropdown-divider\\">
<hr role=\\"separator\\" aria-orientation=\\"horizontal\\" class=\\"dropdown-divider\\">
</li>
- <li role=\\"presentation\\" class=\\"gl-new-dropdown-item\\"><button role=\\"menuitem\\" type=\\"button\\" class=\\"dropdown-item\\">
+ <li role=\\"presentation\\" class=\\"gl-dropdown-item\\"><button role=\\"menuitem\\" type=\\"button\\" class=\\"dropdown-item\\">
<!---->
<!---->
<!---->
- <div class=\\"gl-new-dropdown-item-text-wrapper\\">
- <p class=\\"gl-new-dropdown-item-text-primary\\">
+ <div class=\\"gl-dropdown-item-text-wrapper\\">
+ <p class=\\"gl-dropdown-item-text-primary\\">
Upload file
</p>
<!---->
diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js
index 9c622d49db9..a06f81e4d1c 100644
--- a/spec/frontend/editor/schema/ci/ci_schema_spec.js
+++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js
@@ -31,6 +31,7 @@ import ProjectPathYaml from './yaml_tests/positive_tests/project_path.yml';
import VariablesYaml from './yaml_tests/positive_tests/variables.yml';
import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
import IdTokensYaml from './yaml_tests/positive_tests/id_tokens.yml';
+import HooksYaml from './yaml_tests/positive_tests/hooks.yml';
// YAML NEGATIVE TEST
import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml';
@@ -47,6 +48,7 @@ import TriggerNegative from './yaml_tests/negative_tests/trigger.yml';
import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
import IdTokensNegativeYaml from './yaml_tests/negative_tests/id_tokens.yml';
+import HooksNegative from './yaml_tests/negative_tests/hooks.yml';
const ajv = new Ajv({
strictTypes: false,
@@ -79,6 +81,7 @@ describe('positive tests', () => {
FilterYaml,
IncludeYaml,
JobWhenYaml,
+ HooksYaml,
RulesYaml,
VariablesYaml,
ProjectPathYaml,
@@ -118,6 +121,7 @@ describe('negative tests', () => {
ProjectPathIncludeNoSlashYaml,
ProjectPathIncludeTailSlashYaml,
TriggerNegative,
+ HooksNegative,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml
new file mode 100644
index 00000000000..e3366b0b6d3
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/hooks.yml
@@ -0,0 +1,10 @@
+job1:
+ hooks:
+ invalid_script:
+ - echo 'hello job1 invalid_script'
+ script: echo 'hello job1 script'
+
+job2:
+ hooks:
+ pre_get_sources_script: true
+ script: echo 'hello job1 script'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml
new file mode 100644
index 00000000000..4d45c5528ea
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/hooks.yml
@@ -0,0 +1,10 @@
+default:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello default pre_get_sources_script'
+
+job1:
+ hooks:
+ pre_get_sources_script:
+ - echo 'hello job1 pre_get_sources_script'
+ script: echo 'hello job1 script'
diff --git a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
index a72528ae36b..748e151f31b 100644
--- a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
+++ b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap
@@ -87,7 +87,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
>
<div
aria-label="The GitLab user to which the Jira user Jane Doe will be mapped"
- class="dropdown b-dropdown gl-new-dropdown w-100 btn-group"
+ class="dropdown b-dropdown gl-dropdown w-100 btn-group"
>
<!---->
<button
@@ -101,7 +101,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
<!---->
<span
- class="gl-new-dropdown-button-text"
+ class="gl-dropdown-button-text"
>
janedoe
</span>
@@ -123,14 +123,14 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
tabindex="-1"
>
<div
- class="gl-new-dropdown-inner"
+ class="gl-dropdown-inner"
>
<!---->
<!---->
<div
- class="gl-new-dropdown-contents"
+ class="gl-dropdown-contents"
>
<!---->
@@ -165,7 +165,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
</div>
<li
- class="gl-new-dropdown-text text-secondary"
+ class="gl-dropdown-text text-secondary"
role="presentation"
>
<p
@@ -218,7 +218,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
>
<div
aria-label="The GitLab user to which the Jira user Fred Chopin will be mapped"
- class="dropdown b-dropdown gl-new-dropdown w-100 btn-group"
+ class="dropdown b-dropdown gl-dropdown w-100 btn-group"
>
<!---->
<button
@@ -232,7 +232,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
<!---->
<span
- class="gl-new-dropdown-button-text"
+ class="gl-dropdown-button-text"
>
mrgitlab
</span>
@@ -254,14 +254,14 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
tabindex="-1"
>
<div
- class="gl-new-dropdown-inner"
+ class="gl-dropdown-inner"
>
<!---->
<!---->
<div
- class="gl-new-dropdown-contents"
+ class="gl-dropdown-contents"
>
<!---->
@@ -296,7 +296,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
</div>
<li
- class="gl-new-dropdown-text text-secondary"
+ class="gl-dropdown-text text-secondary"
role="presentation"
>
<p
diff --git a/spec/frontend/language_switcher/components/app_spec.js b/spec/frontend/language_switcher/components/app_spec.js
index d44187897b8..6a1b94cd813 100644
--- a/spec/frontend/language_switcher/components/app_spec.js
+++ b/spec/frontend/language_switcher/components/app_spec.js
@@ -27,7 +27,7 @@ describe('<LanguageSwitcher />', () => {
wrapper.destroy();
});
- const getPreferredLanguage = () => wrapper.find('.gl-new-dropdown-button-text').text();
+ const getPreferredLanguage = () => wrapper.find('.gl-dropdown-button-text').text();
const findLanguageDropdownItem = (code) => wrapper.findByTestId(`language_switcher_lang_${code}`);
it('preferred language', () => {
diff --git a/spec/frontend/milestones/components/milestone_combobox_spec.js b/spec/frontend/milestones/components/milestone_combobox_spec.js
index ce5b2a1000b..c20c51db75e 100644
--- a/spec/frontend/milestones/components/milestone_combobox_spec.js
+++ b/spec/frontend/milestones/components/milestone_combobox_spec.js
@@ -346,7 +346,7 @@ describe('Milestone combobox component', () => {
expect(
findFirstProjectMilestonesDropdownItem()
.find('svg')
- .classes('gl-new-dropdown-item-check-icon'),
+ .classes('gl-dropdown-item-check-icon'),
).toBe(true);
selectFirstProjectMilestone();
@@ -473,7 +473,7 @@ describe('Milestone combobox component', () => {
expect(
findFirstGroupMilestonesDropdownItem()
.find('svg')
- .classes('gl-new-dropdown-item-check-icon'),
+ .classes('gl-dropdown-item-check-icon'),
).toBe(true);
selectFirstGroupMilestone();
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
index 92c2cd90568..c4020eeb75f 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/pypi_installation_spec.js.snap
@@ -13,7 +13,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
<div>
<div
- class="dropdown b-dropdown gl-new-dropdown btn-group"
+ class="dropdown b-dropdown gl-dropdown btn-group"
id="__BVID__27"
lazy=""
>
@@ -30,7 +30,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
<!---->
<span
- class="gl-new-dropdown-button-text"
+ class="gl-dropdown-button-text"
>
Show PyPi commands
</span>
diff --git a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
index b473f7c343c..4077564486c 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
+++ b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
@@ -61,7 +61,7 @@ exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = `
class="gl-display-flex gl-align-items-flex-start"
>
<div
- class="dropdown b-dropdown gl-new-dropdown gl-display-block gl-md-display-none! btn-group"
+ class="dropdown b-dropdown gl-dropdown gl-display-block gl-md-display-none! btn-group"
lazy=""
no-caret=""
title="Options"
@@ -87,7 +87,7 @@ exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = `
</svg>
<span
- class="gl-new-dropdown-button-text gl-sr-only"
+ class="gl-dropdown-button-text gl-sr-only"
>
</span>
diff --git a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
index bdf5ea23812..06be40fc941 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
@@ -225,7 +225,7 @@ exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
>
<div
boundary="scrollParent"
- class="dropdown b-dropdown gl-new-dropdown btn-group"
+ class="dropdown b-dropdown gl-dropdown btn-group"
id="__BVID__13"
lazy=""
menu-class="dropdown-extended-height"
diff --git a/spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js b/spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js
new file mode 100644
index 00000000000..cb7262b15e3
--- /dev/null
+++ b/spec/frontend/vue_shared/components/listbox_input/listbox_input_spec.js
@@ -0,0 +1,132 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlListbox } from '@gitlab/ui';
+import ListboxInput from '~/vue_shared/components/listbox_input/listbox_input.vue';
+
+describe('ListboxInput', () => {
+ let wrapper;
+
+ // Props
+ const name = 'name';
+ const defaultToggleText = 'defaultToggleText';
+ const items = [
+ {
+ text: 'Group 1',
+ options: [
+ { text: 'Item 1', value: '1' },
+ { text: 'Item 2', value: '2' },
+ ],
+ },
+ {
+ text: 'Group 2',
+ options: [{ text: 'Item 3', value: '3' }],
+ },
+ ];
+
+ // Finders
+ const findGlListbox = () => wrapper.findComponent(GlListbox);
+ const findInput = () => wrapper.find('input');
+
+ const createComponent = (propsData) => {
+ wrapper = shallowMount(ListboxInput, {
+ propsData: {
+ name,
+ defaultToggleText,
+ items,
+ ...propsData,
+ },
+ });
+ };
+
+ describe('input attributes', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('sets the input name', () => {
+ expect(findInput().attributes('name')).toBe(name);
+ });
+ });
+
+ describe('toggle text', () => {
+ it('uses the default toggle text while no value is selected', () => {
+ createComponent();
+
+ expect(findGlListbox().props('toggleText')).toBe(defaultToggleText);
+ });
+
+ it("uses the selected option's text as the toggle text", () => {
+ const selectedOption = items[0].options[0];
+ createComponent({ selected: selectedOption.value });
+
+ expect(findGlListbox().props('toggleText')).toBe(selectedOption.text);
+ });
+ });
+
+ describe('input value', () => {
+ const selectedOption = items[0].options[0];
+
+ beforeEach(() => {
+ createComponent({ selected: selectedOption.value });
+ jest.spyOn(findInput().element, 'dispatchEvent');
+ });
+
+ it("sets the listbox's and input's values", () => {
+ const { value } = selectedOption;
+
+ expect(findGlListbox().props('selected')).toBe(value);
+ expect(findInput().attributes('value')).toBe(value);
+ });
+
+ describe("when the listbox's value changes", () => {
+ const newSelectedOption = items[1].options[0];
+
+ beforeEach(() => {
+ findGlListbox().vm.$emit('select', newSelectedOption.value);
+ });
+
+ it('emits the `select` event', () => {
+ expect(wrapper.emitted('select')).toEqual([[newSelectedOption.value]]);
+ });
+ });
+ });
+
+ describe('search', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('passes all items to GlListbox by default', () => {
+ createComponent();
+ expect(findGlListbox().props('items')).toStrictEqual(items);
+ });
+
+ describe('with groups', () => {
+ beforeEach(() => {
+ createComponent();
+ findGlListbox().vm.$emit('search', '1');
+ });
+
+ it('passes only the items that match the search string', async () => {
+ expect(findGlListbox().props('items')).toStrictEqual([
+ {
+ text: 'Group 1',
+ options: [{ text: 'Item 1', value: '1' }],
+ },
+ ]);
+ });
+ });
+
+ describe('with flat items', () => {
+ beforeEach(() => {
+ createComponent({
+ items: items[0].options,
+ });
+ findGlListbox().vm.$emit('search', '1');
+ });
+
+ it('passes only the items that match the search string', async () => {
+ expect(findGlListbox().props('items')).toStrictEqual([{ text: 'Item 1', value: '1' }]);
+ });
+ });
+ });
+});
diff --git a/spec/graphql/resolvers/design_management/design_resolver_spec.rb b/spec/graphql/resolvers/design_management/design_resolver_spec.rb
index 0915dddf438..3a62f24993a 100644
--- a/spec/graphql/resolvers/design_management/design_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/design_resolver_spec.rb
@@ -6,14 +6,14 @@ RSpec.describe Resolvers::DesignManagement::DesignResolver do
include GraphqlHelpers
include DesignManagementTestHelpers
- specify do
- expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType)
- end
-
before do
enable_design_management
end
+ specify do
+ expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType)
+ end
+
describe '#resolve' do
let_it_be(:issue) { create(:issue) }
let_it_be(:project) { issue.project }
diff --git a/spec/graphql/resolvers/design_management/designs_resolver_spec.rb b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb
index 64eae14d888..7024e62c670 100644
--- a/spec/graphql/resolvers/design_management/designs_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb
@@ -6,14 +6,14 @@ RSpec.describe Resolvers::DesignManagement::DesignsResolver do
include GraphqlHelpers
include DesignManagementTestHelpers
- specify do
- expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType.connection_type)
- end
-
before do
enable_design_management
end
+ specify do
+ expect(described_class).to have_nullable_graphql_type(::Types::DesignManagement::DesignType.connection_type)
+ end
+
describe '#resolve' do
let_it_be(:issue) { create(:issue) }
let_it_be(:project) { issue.project }
diff --git a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
index 398f8f52269..9250485e4c8 100644
--- a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb
@@ -12,6 +12,10 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
let(:current_user) { create(:user) }
+ before do
+ project.add_developer(current_user)
+ end
+
specify do
expect(described_class).to have_nullable_graphql_type(::Types::Ci::PipelineType)
end
@@ -20,10 +24,6 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
resolve(described_class, obj: project, args: args, ctx: { current_user: current_user })
end
- before do
- project.add_developer(current_user)
- end
-
it 'resolves pipeline for the passed iid' do
expect(Ci::PipelinesFinder)
.to receive(:new)
diff --git a/spec/helpers/listbox_helper_spec.rb b/spec/helpers/listbox_helper_spec.rb
index cba00b43ae5..bae9c40aa02 100644
--- a/spec/helpers/listbox_helper_spec.rb
+++ b/spec/helpers/listbox_helper_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe ListboxHelper do
*%w[
dropdown
b-dropdown
- gl-new-dropdown
+ gl-dropdown
btn-group
js-redirect-listbox
])
diff --git a/spec/lib/banzai/filter/attributes_filter_spec.rb b/spec/lib/banzai/filter/attributes_filter_spec.rb
new file mode 100644
index 00000000000..86af3364fef
--- /dev/null
+++ b/spec/lib/banzai/filter/attributes_filter_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::AttributesFilter, feature_category: :team_planning do
+ using RSpec::Parameterized::TableSyntax
+ include FilterSpecHelper
+
+ def image
+ %(<img src="example.jpg">)
+ end
+
+ it 'does not recognize new syntax when feature flag is off' do
+ stub_feature_flags(markdown_image_attributes: false)
+ doc = filter("#{image}{width=100}")
+
+ expect(doc.to_s).to eq "#{image}{width=100}"
+ end
+
+ describe 'attribute syntax' do
+ context 'when attribute syntax is valid' do
+ where(:text, :result) do
+ "#{image}{width=100}" | '<img src="example.jpg" width="100">'
+ "#{image}{ width=100 }" | '<img src="example.jpg" width="100">'
+ "#{image}{width=\"100\"}" | '<img src="example.jpg" width="100">'
+ "#{image}{width=100 width=200}" | '<img src="example.jpg" width="200">'
+
+ "#{image}{.test_class width=100 style=\"width:400\"}" | '<img src="example.jpg" width="100">'
+ "<img src=\"example.jpg\" class=\"lazy\" />{width=100}" | '<img src="example.jpg" class="lazy" width="100">'
+ end
+
+ with_them do
+ it 'adds them to the img' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+
+ context 'when attribute syntax is invalid' do
+ where(:text, :result) do
+ "#{image} {width=100}" | '<img src="example.jpg"> {width=100}'
+ "#{image}{width=100\nheight=100}" | "<img src=\"example.jpg\">{width=100\nheight=100}"
+ "{width=100 height=100}\n#{image}" | "{width=100 height=100}\n<img src=\"example.jpg\">"
+ '<h1>header</h1>{width=100}' | '<h1>header</h1>{width=100}'
+ end
+
+ with_them do
+ it 'does not recognize as attributes' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+ end
+
+ describe 'height and width' do
+ context 'when size attributes are valid' do
+ where(:text, :result) do
+ "#{image}{width=100 height=200px}" | '<img src="example.jpg" width="100" height="200px">'
+ "#{image}{width=100}" | '<img src="example.jpg" width="100">'
+ "#{image}{width=100px}" | '<img src="example.jpg" width="100px">'
+ "#{image}{height=100%}" | '<img src="example.jpg" height="100%">'
+ "#{image}{width=\"100%\"}" | '<img src="example.jpg" width="100%">'
+ end
+
+ with_them do
+ it 'adds them to the img' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+
+ context 'when size attributes are invalid' do
+ where(:text, :result) do
+ "#{image}{width=100cs}" | '<img src="example.jpg">'
+ "#{image}{width=auto height=200}" | '<img src="example.jpg" height="200">'
+ end
+
+ with_them do
+ it 'ignores them' do
+ expect(filter(text).to_html).to eq result
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
index d29af311ee5..db0c10a802b 100644
--- a/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
@@ -29,12 +29,12 @@ RSpec.describe Banzai::Filter::InlineGrafanaMetricsFilter do
)
end
- it_behaves_like 'a metrics embed filter'
-
around do |example|
travel_to(Time.utc(2019, 3, 17, 13, 10)) { example.run }
end
+ it_behaves_like 'a metrics embed filter'
+
context 'when grafana is not configured' do
before do
allow(project).to receive(:grafana_integration).and_return(nil)
diff --git a/spec/lib/banzai/filter/references/user_reference_filter_spec.rb b/spec/lib/banzai/filter/references/user_reference_filter_spec.rb
index b153efd9655..d61b71c711d 100644
--- a/spec/lib/banzai/filter/references/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/references/user_reference_filter_spec.rb
@@ -42,12 +42,12 @@ RSpec.describe Banzai::Filter::References::UserReferenceFilter do
context 'mentioning @all' do
let(:reference) { User.reference_prefix + 'all' }
- it_behaves_like 'a reference containing an element node'
-
before do
project.add_developer(project.creator)
end
+ it_behaves_like 'a reference containing an element node'
+
it 'supports a special @all mention' do
project.add_developer(user)
doc = reference_filter("Hey #{reference}", author: user)
diff --git a/spec/lib/feature/definition_spec.rb b/spec/lib/feature/definition_spec.rb
index 3d11ad4c0d8..10334b3d0ea 100644
--- a/spec/lib/feature/definition_spec.rb
+++ b/spec/lib/feature/definition_spec.rb
@@ -111,6 +111,11 @@ RSpec.describe Feature::Definition do
subject { described_class.send(:load_all!) }
+ after do
+ FileUtils.rm_rf(store1)
+ FileUtils.rm_rf(store2)
+ end
+
it "when there's no feature flags a list of definitions is empty" do
is_expected.to be_empty
end
@@ -136,11 +141,6 @@ RSpec.describe Feature::Definition do
.to raise_error(/Feature flag is missing name/)
end
- after do
- FileUtils.rm_rf(store1)
- FileUtils.rm_rf(store2)
- end
-
def write_feature_flag(store, path, content)
path = File.join(store, path)
dir = File.dirname(path)
diff --git a/spec/lib/gitlab/auth/saml/user_spec.rb b/spec/lib/gitlab/auth/saml/user_spec.rb
index 796512bc52b..6c9598f920e 100644
--- a/spec/lib/gitlab/auth/saml/user_spec.rb
+++ b/spec/lib/gitlab/auth/saml/user_spec.rb
@@ -46,6 +46,10 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
context 'external groups' do
+ before do
+ stub_saml_group_config(%w(Interns))
+ end
+
context 'are defined' do
it 'marks the user as external' do
stub_saml_group_config(%w(Freelancers))
@@ -55,10 +59,6 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
end
- before do
- stub_saml_group_config(%w(Interns))
- end
-
context 'are defined but the user does not belong there' do
it 'does not mark the user as external' do
saml_user.save # rubocop:disable Rails/SaveBang
diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
index ad183fd422b..8ff8de2379a 100644
--- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb
@@ -16,13 +16,13 @@ RSpec.describe Gitlab::Email::Handler::CreateIssueHandler do
let(:namespace) { create(:namespace, path: 'gitlabhq') }
let(:email_raw) { email_fixture('emails/valid_new_issue.eml') }
- it_behaves_like 'reply processing shared examples'
-
before do
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo")
stub_config_setting(host: 'localhost')
end
+ it_behaves_like 'reply processing shared examples'
+
context "when email key" do
let(:mail) { Mail::Message.new(email_raw) }
diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
index 60bb94e841f..f5b44d30c50 100644
--- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
@@ -16,16 +16,16 @@ RSpec.describe Gitlab::Email::Handler::CreateMergeRequestHandler do
let(:namespace) { create(:namespace, path: 'gitlabhq') }
let(:email_raw) { email_fixture('emails/valid_new_merge_request.eml') }
- it_behaves_like 'reply processing shared examples'
+ after do
+ TestEnv.clean_test_path
+ end
before do
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo")
stub_config_setting(host: 'localhost')
end
- after do
- TestEnv.clean_test_path
- end
+ it_behaves_like 'reply processing shared examples'
context "when email key" do
let(:mail) { Mail::Message.new(email_raw) }
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index 222fdb2088b..f70645a8272 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -15,6 +15,11 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
SentNotification.record_note(note, user.id, mail_key)
end
+ before do
+ stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
+ stub_config_setting(host: 'localhost')
+ end
+
it_behaves_like 'reply processing shared examples'
it_behaves_like 'note handler shared examples' do
@@ -26,11 +31,6 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
let(:with_quick_actions) { fixture_file('emails/valid_reply_with_quick_actions.eml') }
end
- before do
- stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo")
- stub_config_setting(host: 'localhost')
- end
-
context 'when the recipient address does not include a mail key' do
let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, '') }
diff --git a/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
index 47f6015c6f8..b22c55208f0 100644
--- a/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
@@ -7,15 +7,15 @@ RSpec.describe Gitlab::Email::Hook::DisableEmailInterceptor do
Mail.register_interceptor(described_class)
end
+ after do
+ Mail.unregister_interceptor(described_class)
+ end
+
it 'does not send emails' do
allow(Gitlab.config.gitlab).to receive(:email_enabled).and_return(false)
expect { deliver_mail }.not_to change(ActionMailer::Base.deliveries, :count)
end
- after do
- Mail.unregister_interceptor(described_class)
- end
-
def deliver_mail
key = create :personal_key
Notify.new_ssh_key_email(key.id)
diff --git a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
index 41dce5d76dd..61945cc06b8 100644
--- a/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/with_feature_flag_actors_spec.rb
@@ -157,70 +157,47 @@ RSpec.describe Gitlab::GitalyClient::WithFeatureFlagActors do
let(:call_arg_2) { double }
let(:call_arg_3) { double }
let(:call_result) { double }
+ let(:repository_actor) { instance_double(::Repository) }
+ let(:user_actor) { instance_double(::User) }
+ let(:project_actor) { instance_double(Project) }
+ let(:group_actor) { instance_double(Group) }
before do
+ allow(service).to receive(:user_actor).and_return(user_actor)
+ allow(service).to receive(:repository_actor).and_return(repository_actor)
+ allow(service).to receive(:project_actor).and_return(project_actor)
+ allow(service).to receive(:group_actor).and_return(group_actor)
+ allow(Gitlab::GitalyClient).to receive(:with_feature_flag_actors).and_call_original
allow(Gitlab::GitalyClient).to receive(:call).and_return(call_result)
end
- context 'when actors_aware_gitaly_calls flag is enabled' do
- let(:repository_actor) { instance_double(::Repository) }
- let(:user_actor) { instance_double(::User) }
- let(:project_actor) { instance_double(Project) }
- let(:group_actor) { instance_double(Group) }
-
- before do
- stub_feature_flags(actors_aware_gitaly_calls: true)
-
- allow(service).to receive(:user_actor).and_return(user_actor)
- allow(service).to receive(:repository_actor).and_return(repository_actor)
- allow(service).to receive(:project_actor).and_return(project_actor)
- allow(service).to receive(:group_actor).and_return(group_actor)
- allow(Gitlab::GitalyClient).to receive(:with_feature_flag_actors).and_call_original
- end
-
- it 'triggers client call with feature flag actors' do
- result = service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
-
- expect(Gitlab::GitalyClient).to have_received(:call).with(call_arg_1, call_arg_2, karg: call_arg_3)
- expect(Gitlab::GitalyClient).to have_received(:with_feature_flag_actors).with(
- repository: repository_actor,
- user: user_actor,
- project: project_actor,
- group: group_actor
- )
- expect(result).to be(call_result)
- end
-
- context 'when call without repository_actor' do
- before do
- allow(service).to receive(:repository_actor).and_return(nil)
- allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).and_call_original
- end
-
- it 'calls error tracking track_and_raise_for_dev_exception' do
- expect do
- service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
- end.to raise_error /gitaly_client_call called without setting repository_actor/
-
- expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(
- be_a(Feature::InvalidFeatureFlagError)
- )
- end
- end
+ it 'triggers client call with feature flag actors' do
+ result = service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
+
+ expect(Gitlab::GitalyClient).to have_received(:call).with(call_arg_1, call_arg_2, karg: call_arg_3)
+ expect(Gitlab::GitalyClient).to have_received(:with_feature_flag_actors).with(
+ repository: repository_actor,
+ user: user_actor,
+ project: project_actor,
+ group: group_actor
+ )
+ expect(result).to be(call_result)
end
- context 'when actors_aware_gitaly_calls not enabled' do
+ context 'when call without repository_actor' do
before do
- stub_feature_flags(actors_aware_gitaly_calls: false)
+ allow(service).to receive(:repository_actor).and_return(nil)
+ allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).and_call_original
end
- it 'triggers client call without feature flag actors' do
- expect(Gitlab::GitalyClient).not_to receive(:with_feature_flag_actors)
+ it 'calls error tracking track_and_raise_for_dev_exception' do
+ expect do
+ service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
+ end.to raise_error /gitaly_client_call called without setting repository_actor/
- result = service.gitaly_client_call(call_arg_1, call_arg_2, karg: call_arg_3)
-
- expect(Gitlab::GitalyClient).to have_received(:call).with(call_arg_1, call_arg_2, karg: call_arg_3)
- expect(result).to be(call_result)
+ expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(
+ be_a(Feature::InvalidFeatureFlagError)
+ )
end
end
@@ -228,47 +205,28 @@ RSpec.describe Gitlab::GitalyClient::WithFeatureFlagActors do
let_it_be(:project) { create(:project) }
let(:repository_actor) { project.repository }
- context 'when actors_aware_gitaly_calls flag is enabled' do
- let(:user_actor) { instance_double(::User) }
- let(:project_actor) { instance_double(Project) }
- let(:group_actor) { instance_double(Group) }
-
- before do
- stub_feature_flags(actors_aware_gitaly_calls: true)
-
- allow(Feature::Gitaly).to receive(:user_actor).and_return(user_actor)
- allow(Feature::Gitaly).to receive(:project_actor).with(project).and_return(project_actor)
- allow(Feature::Gitaly).to receive(:group_actor).with(project).and_return(group_actor)
- end
-
- it 'returns a hash with collected feature flag actors' do
- result = service.gitaly_feature_flag_actors(repository_actor)
- expect(result).to eql(
- repository: repository_actor,
- user: user_actor,
- project: project_actor,
- group: group_actor
- )
-
- expect(Feature::Gitaly).to have_received(:user_actor).with(no_args)
- expect(Feature::Gitaly).to have_received(:project_actor).with(project)
- expect(Feature::Gitaly).to have_received(:group_actor).with(project)
- end
- end
+ let(:user_actor) { instance_double(::User) }
+ let(:project_actor) { instance_double(Project) }
+ let(:group_actor) { instance_double(Group) }
- context 'when actors_aware_gitaly_calls not enabled' do
- before do
- stub_feature_flags(actors_aware_gitaly_calls: false)
- end
+ before do
+ allow(Feature::Gitaly).to receive(:user_actor).and_return(user_actor)
+ allow(Feature::Gitaly).to receive(:project_actor).with(project).and_return(project_actor)
+ allow(Feature::Gitaly).to receive(:group_actor).with(project).and_return(group_actor)
+ end
- it 'returns an empty hash' do
- expect(Feature::Gitaly).not_to receive(:user_actor)
- expect(Feature::Gitaly).not_to receive(:project_actor)
- expect(Feature::Gitaly).not_to receive(:group_actor)
+ it 'returns a hash with collected feature flag actors' do
+ result = service.gitaly_feature_flag_actors(repository_actor)
+ expect(result).to eql(
+ repository: repository_actor,
+ user: user_actor,
+ project: project_actor,
+ group: group_actor
+ )
- result = service.gitaly_feature_flag_actors(repository_actor)
- expect(result).to eql({})
- end
+ expect(Feature::Gitaly).to have_received(:user_actor).with(no_args)
+ expect(Feature::Gitaly).to have_received(:project_actor).with(project)
+ expect(Feature::Gitaly).to have_received(:group_actor).with(project)
end
end
end
diff --git a/spec/lib/gitlab/pagination/offset_pagination_spec.rb b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
index ebbd207cc11..b1c4ffd6c29 100644
--- a/spec/lib/gitlab/pagination/offset_pagination_spec.rb
+++ b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
@@ -101,6 +101,28 @@ RSpec.describe Gitlab::Pagination::OffsetPagination do
end
end
+ context 'when without_count is true' do
+ it_behaves_like 'paginated response'
+
+ it 'does not return the X-Total and X-Total-Pages headers' do
+ expect_no_header('X-Total')
+ expect_no_header('X-Total-Pages')
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Page', '1')
+ expect_header('X-Next-Page', '2')
+ expect_header('X-Prev-Page', '')
+
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
+ expect(val).not_to include('rel="last"')
+ expect(val).not_to include('rel="prev"')
+ end
+
+ expect { subject.paginate(resource, without_count: true) }.to make_queries_matching(/SELECT COUNT/, 0)
+ end
+ end
+
it 'does not return the total headers when excluding them' do
expect_no_header('X-Total')
expect_no_header('X-Total-Pages')
diff --git a/spec/lib/gitlab/tracking/event_definition_spec.rb b/spec/lib/gitlab/tracking/event_definition_spec.rb
index 623009e9a30..c8e616b092b 100644
--- a/spec/lib/gitlab/tracking/event_definition_spec.rb
+++ b/spec/lib/gitlab/tracking/event_definition_spec.rb
@@ -83,6 +83,11 @@ RSpec.describe Gitlab::Tracking::EventDefinition do
subject { described_class.definitions }
+ after do
+ FileUtils.rm_rf(metric1)
+ FileUtils.rm_rf(metric2)
+ end
+
it 'has empty list when there are no definition files' do
is_expected.to be_empty
end
@@ -92,10 +97,5 @@ RSpec.describe Gitlab::Tracking::EventDefinition do
is_expected.to be_one
end
-
- after do
- FileUtils.rm_rf(metric1)
- FileUtils.rm_rf(metric2)
- end
end
end
diff --git a/spec/lib/gitlab/usage/metric_definition_spec.rb b/spec/lib/gitlab/usage/metric_definition_spec.rb
index 931340947a2..4b835d11975 100644
--- a/spec/lib/gitlab/usage/metric_definition_spec.rb
+++ b/spec/lib/gitlab/usage/metric_definition_spec.rb
@@ -233,6 +233,11 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
subject { described_class.send(:load_all!) }
+ after do
+ FileUtils.rm_rf(metric1)
+ FileUtils.rm_rf(metric2)
+ end
+
it 'has empty list when there are no definition files' do
is_expected.to be_empty
end
@@ -251,11 +256,6 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
subject
end
-
- after do
- FileUtils.rm_rf(metric1)
- FileUtils.rm_rf(metric2)
- end
end
describe 'dump_metrics_yaml' do
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index e4cb1e74d32..7b5cfb57707 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -138,14 +138,14 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
File.open(ce_temp_file.path, "w+b") { |f| f.write [ce_event].to_yaml }
end
- it 'returns ce events' do
- expect(described_class.known_events).to include(ce_event)
- end
-
after do
ce_temp_file.unlink
FileUtils.remove_entry(ce_temp_dir) if Dir.exist?(ce_temp_dir)
end
+
+ it 'returns ce events' do
+ expect(described_class.known_events).to include(ce_event)
+ end
end
describe 'known_events' do
diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb
index d208ef93224..87e2e341777 100644
--- a/spec/lib/mattermost/session_spec.rb
+++ b/spec/lib/mattermost/session_spec.rb
@@ -14,15 +14,15 @@ RSpec.describe Mattermost::Session, type: :request do
subject { described_class.new(user) }
# Needed for doorkeeper to function
+ before do
+ subject.base_uri = mattermost_url
+ end
+
it { is_expected.to respond_to(:current_resource_owner) }
it { is_expected.to respond_to(:request) }
it { is_expected.to respond_to(:authorization) }
it { is_expected.to respond_to(:strategy) }
- before do
- subject.base_uri = mattermost_url
- end
-
describe '#with session' do
let(:location) { 'http://location.tld' }
let(:cookie_header) { 'MMOAUTH=taskik8az7rq8k6rkpuas7htia; Path=/;' }
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 5733e892d2a..bae92b78a24 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -419,15 +419,15 @@ RSpec.describe Notify do
end
it 'includes the reason in the footer' do
- text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(reason: NotificationReason::ASSIGNED, format: :html)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind_call(self, reason: NotificationReason::ASSIGNED, format: :html)
is_expected.to have_body_text(text)
new_subject = described_class.reassigned_merge_request_email(recipient.id, merge_request.id, [previous_assignee.id], current_user.id, NotificationReason::MENTIONED)
- text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(reason: NotificationReason::MENTIONED, format: :html)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind_call(self, reason: NotificationReason::MENTIONED, format: :html)
expect(new_subject).to have_body_text(text)
new_subject = described_class.reassigned_merge_request_email(recipient.id, merge_request.id, [previous_assignee.id], current_user.id, nil)
- text = EmailsHelper.instance_method(:notification_reason_text).bind(self).call(format: :html)
+ text = EmailsHelper.instance_method(:notification_reason_text).bind_call(self, format: :html)
expect(new_subject).to have_body_text(text)
end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 7fc916f0939..ac0a18a176d 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -15,13 +15,13 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state, :clean_git
described_class.new(build: build, chunk_index: chunk_index, data_store: data_store, raw_data: raw_data)
end
- it_behaves_like 'having unique enum values'
-
before do
stub_feature_flags(ci_enable_live_trace: true)
stub_artifacts_object_storage
end
+ it_behaves_like 'having unique enum values'
+
def redis_instance
{
redis: Gitlab::Redis::SharedState,
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index e5caa11452e..2be59e5f515 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -5,6 +5,11 @@ require 'spec_helper'
RSpec.describe Clusters::Applications::Ingress do
let(:ingress) { create(:clusters_applications_ingress) }
+ before do
+ allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
+ allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
+ end
+
it_behaves_like 'having unique enum values'
include_examples 'cluster application core specs', :clusters_applications_ingress
@@ -13,11 +18,6 @@ RSpec.describe Clusters::Applications::Ingress do
include_examples 'cluster application helm specs', :clusters_applications_ingress
include_examples 'cluster application initial status specs'
- before do
- allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
- allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
- end
-
describe 'default values' do
it { expect(subject.ingress_type).to eq("nginx") }
it { expect(subject.version).to eq(described_class::VERSION) }
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 3914450339a..f6b13f4a93f 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -5,18 +5,18 @@ require 'spec_helper'
RSpec.describe Clusters::Applications::Knative do
let(:knative) { create(:clusters_applications_knative) }
- include_examples 'cluster application core specs', :clusters_applications_knative
- include_examples 'cluster application status specs', :clusters_applications_knative
- include_examples 'cluster application helm specs', :clusters_applications_knative
- include_examples 'cluster application version specs', :clusters_applications_knative
- include_examples 'cluster application initial status specs'
-
before do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
allow(ClusterConfigureIstioWorker).to receive(:perform_async)
end
+ include_examples 'cluster application core specs', :clusters_applications_knative
+ include_examples 'cluster application status specs', :clusters_applications_knative
+ include_examples 'cluster application helm specs', :clusters_applications_knative
+ include_examples 'cluster application version specs', :clusters_applications_knative
+ include_examples 'cluster application initial status specs'
+
describe 'associations' do
it { is_expected.to have_one(:serverless_domain_cluster).class_name('::Serverless::DomainCluster').with_foreign_key('clusters_applications_knative_id').inverse_of(:knative) }
end
diff --git a/spec/models/concerns/has_user_type_spec.rb b/spec/models/concerns/has_user_type_spec.rb
index b6e711e8325..f8a7763aeed 100644
--- a/spec/models/concerns/has_user_type_spec.rb
+++ b/spec/models/concerns/has_user_type_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe User do
specify 'types consistency checks', :aggregate_failures do
expect(described_class::USER_TYPES.keys)
- .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot automation_bot admin_bot])
+ .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot
+ migration_bot automation_bot admin_bot suggested_reviewers_bot])
expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES)
expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES)
diff --git a/spec/models/integrations/chat_message/pipeline_message_spec.rb b/spec/models/integrations/chat_message/pipeline_message_spec.rb
index f3388853b37..413cb097327 100644
--- a/spec/models/integrations/chat_message/pipeline_message_spec.rb
+++ b/spec/models/integrations/chat_message/pipeline_message_spec.rb
@@ -40,8 +40,6 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do
let(:has_yaml_errors) { false }
- it_behaves_like Integrations::ChatMessage
-
before do
test_commit = double("A test commit", committer: args[:user], title: "A test commit message")
test_project = build(:project, name: args[:project][:name])
@@ -62,6 +60,8 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do
allow(Gitlab::UrlBuilder).to receive(:build).with(args[:user]).and_return("http://example.gitlab.com/hacker")
end
+ it_behaves_like Integrations::ChatMessage
+
it 'returns an empty pretext' do
expect(subject.pretext).to be_empty
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index c17e180f282..01e5c3d04db 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -164,11 +164,11 @@ RSpec.describe Repository do
repository.add_tag(user, annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', 'test tag message\n')
end
- it { is_expected.to eq(['v1.0.0', 'v1.1.0', annotated_tag_name]) }
-
after do
repository.rm_tag(user, annotated_tag_name)
end
+
+ it { is_expected.to eq(['v1.0.0', 'v1.1.0', annotated_tag_name]) }
end
end
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index c1b7461f444..fcf3ab201fb 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Ci::Jobs do
+RSpec.describe API::Ci::Jobs, feature_category: :continuous_integration do
include HttpBasicAuthHelpers
include DependencyProxyHelpers
@@ -325,7 +325,7 @@ RSpec.describe API::Ci::Jobs do
context 'authorized user' do
it 'returns project jobs' do
expect(response).to have_gitlab_http_status(:ok)
- expect(response).to include_pagination_headers
+ expect(response).to include_limited_pagination_headers
expect(json_response).to be_an Array
end
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index 3c6604cf409..f8fd81953b2 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -103,15 +103,19 @@ RSpec.describe API::Internal::Kubernetes do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'tracks events' do
+ it 'tracks events and unique events', :aggregate_failures do
+ request_count = 2
counters = { gitops_sync: 10, k8s_api_proxy_request: 5 }
- unique_counters = { agent_users_using_ci_tunnel: [10] }
+ unique_counters = { agent_users_using_ci_tunnel: [10, 999, 777, 10] }
expected_counters = {
- kubernetes_agent_gitops_sync: counters[:gitops_sync],
- kubernetes_agent_k8s_api_proxy_request: counters[:k8s_api_proxy_request]
+ kubernetes_agent_gitops_sync: request_count * counters[:gitops_sync],
+ kubernetes_agent_k8s_api_proxy_request: request_count * counters[:k8s_api_proxy_request]
}
+ expected_hll_count = unique_counters[:agent_users_using_ci_tunnel].uniq.count
- send_request(params: { counters: counters, unique_counters: unique_counters })
+ request_count.times do
+ send_request(params: { counters: counters, unique_counters: unique_counters })
+ end
expect(Gitlab::UsageDataCounters::KubernetesAgentCounter.totals).to eq(expected_counters)
@@ -121,7 +125,7 @@ RSpec.describe API::Internal::Kubernetes do
event_names: 'agent_users_using_ci_tunnel',
start_date: Date.current, end_date: Date.current + 10
)
- ).to eq(1)
+ ).to eq(expected_hll_count)
end
end
end
diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb
index ca38721cc7f..764d55e8b4a 100644
--- a/spec/serializers/pipeline_details_entity_spec.rb
+++ b/spec/serializers/pipeline_details_entity_spec.rb
@@ -11,16 +11,16 @@ RSpec.describe PipelineDetailsEntity do
described_class.represent(pipeline, request: request)
end
- it 'inherits from PipelineEntity' do
- expect(described_class).to be < Ci::PipelineEntity
- end
-
before do
stub_not_protect_default_branch
allow(request).to receive(:current_user).and_return(user)
end
+ it 'inherits from PipelineEntity' do
+ expect(described_class).to be < Ci::PipelineEntity
+ end
+
describe '#as_json' do
subject { entity.as_json }
diff --git a/spec/services/feature_flags/hook_service_spec.rb b/spec/services/feature_flags/hook_service_spec.rb
index 19c935e43f3..f3edaca52a9 100644
--- a/spec/services/feature_flags/hook_service_spec.rb
+++ b/spec/services/feature_flags/hook_service_spec.rb
@@ -14,14 +14,14 @@ RSpec.describe FeatureFlags::HookService do
subject(:service) { described_class.new(feature_flag, user) }
- describe 'HOOK_NAME' do
- specify { expect(described_class::HOOK_NAME).to eq(:feature_flag_hooks) }
- end
-
before do
allow(Gitlab::DataBuilder::FeatureFlag).to receive(:build).with(feature_flag, user).once.and_return(hook_data)
end
+ describe 'HOOK_NAME' do
+ specify { expect(described_class::HOOK_NAME).to eq(:feature_flag_hooks) }
+ end
+
it 'calls feature_flag.project.execute_hooks' do
expect(feature_flag.project).to receive(:execute_hooks).with(hook_data, described_class::HOOK_NAME)
diff --git a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
index 8d8907119f0..f03912dba80 100644
--- a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
@@ -24,6 +24,10 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do
context 'with tags to delete' do
let(:timeout) { 10 }
+ before do
+ stub_application_setting(container_registry_delete_tags_service_timeout: timeout)
+ end
+
it_behaves_like 'deleting tags'
it 'succeeds when tag delete returns 404' do
@@ -48,10 +52,6 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do
end
end
- before do
- stub_application_setting(container_registry_delete_tags_service_timeout: timeout)
- end
-
context 'with timeout' do
context 'set to a valid value' do
before do
diff --git a/spec/support/helpers/listbox_input_helper.rb b/spec/support/helpers/listbox_input_helper.rb
new file mode 100644
index 00000000000..ca7fbac5daa
--- /dev/null
+++ b/spec/support/helpers/listbox_input_helper.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module ListboxInputHelper
+ include WaitForRequests
+
+ def listbox_input(value, from:)
+ open_listbox_input(from) do
+ find('[role="option"]', text: value).click
+ end
+ end
+
+ def open_listbox_input(selector)
+ page.within(selector) do
+ page.find('button[aria-haspopup="listbox"]').click
+ yield
+ end
+ end
+end
diff --git a/spec/support/patches/rspec_mocks_prepended_methods.rb b/spec/support/patches/rspec_mocks_prepended_methods.rb
index fa3a74c670c..d51fb37a499 100644
--- a/spec/support/patches/rspec_mocks_prepended_methods.rb
+++ b/spec/support/patches/rspec_mocks_prepended_methods.rb
@@ -45,7 +45,7 @@ module RSpec
private
def method_owner
- @method_owner ||= Object.instance_method(:method).bind(object).call(@method_name).owner
+ @method_owner ||= Object.instance_method(:method).bind_call(object, @method_name).owner
end
end
end
diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb
index f01e3c88dad..efdf7513b2d 100644
--- a/spec/support/shared_examples/features/content_editor_shared_examples.rb
+++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb
@@ -327,7 +327,7 @@ RSpec.shared_examples 'edits content using the content editor' do
end
def dropdown_scroll_top
- evaluate_script("document.querySelector('#{suggestions_dropdown} .gl-new-dropdown-inner').scrollTop")
+ evaluate_script("document.querySelector('#{suggestions_dropdown} .gl-dropdown-inner').scrollTop")
end
end
end
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
index 281a70e46c4..95b306fdaaa 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb
@@ -17,7 +17,7 @@ RSpec.shared_examples 'labels sidebar widget' do
end
it 'shows labels list in the dropdown' do
- expect(labels_widget.find('.gl-new-dropdown-contents')).to have_selector('li.gl-new-dropdown-item', count: 4)
+ expect(labels_widget.find('.gl-dropdown-contents')).to have_selector('li.gl-dropdown-item', count: 4)
end
it 'adds a label' do
@@ -57,8 +57,8 @@ RSpec.shared_examples 'labels sidebar widget' do
expect(page).to have_css('.labels-fetch-loading')
wait_for_all_requests
- expect(page).to have_css('[data-testid="dropdown-content"] .gl-new-dropdown-item')
- expect(page.all(:css, '[data-testid="dropdown-content"] .gl-new-dropdown-item').length).to eq(1)
+ expect(page).to have_css('[data-testid="dropdown-content"] .gl-dropdown-item')
+ expect(page.all(:css, '[data-testid="dropdown-content"] .gl-dropdown-item').length).to eq(1)
find_field('Search').native.send_keys(:enter)
click_button 'Close'
diff --git a/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
index da730240e8e..f8982c242f8 100644
--- a/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
+++ b/spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb
@@ -20,15 +20,15 @@ RSpec.shared_examples 'milestone sidebar widget' do
it 'shows milestones list in the dropdown' do
# 5 milestones + "No milestone" = 6 items
- expect(milestone_widget.find('.gl-new-dropdown-contents')).to have_selector('li.gl-new-dropdown-item', count: 6)
+ expect(milestone_widget.find('.gl-dropdown-contents')).to have_selector('li.gl-dropdown-item', count: 6)
end
it 'shows expired milestone at the bottom of the list and milestone due earliest at the top of the list', :aggregate_failures do
- within(milestone_widget, '.gl-new-dropdown-contents') do
+ within(milestone_widget, '.gl-dropdown-contents') do
expect(page.find('li:last-child')).to have_content milestone_expired.title
[milestone3, milestone2, milestone1, milestone_no_duedate].each_with_index do |m, i|
- expect(page.all('li.gl-new-dropdown-item')[i + 1]).to have_content m.title
+ expect(page.all('li.gl-dropdown-item')[i + 1]).to have_content m.title
end
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
index 9ffc55f7e7e..d471a758f3e 100644
--- a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
@@ -62,14 +62,14 @@ RSpec.shared_examples 'deployment metrics examples' do
describe '#deployment_frequency' do
subject { stage_summary.fourth[:value] }
- it 'includes the unit: `/day`' do
- expect(stage_summary.fourth[:unit]).to eq _('/day')
- end
-
before do
travel_to(5.days.ago) { create_deployment(project: project) }
end
+ it 'includes the unit: `/day`' do
+ expect(stage_summary.fourth[:unit]).to eq _('/day')
+ end
+
it 'returns 0.0 when there were deploys but the frequency was too low' do
options[:from] = 30.days.ago
diff --git a/spec/workers/merge_requests/delete_branch_worker_spec.rb b/spec/workers/merge_requests/delete_branch_worker_spec.rb
deleted file mode 100644
index 80ca8c061f5..00000000000
--- a/spec/workers/merge_requests/delete_branch_worker_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe MergeRequests::DeleteBranchWorker do
- let_it_be(:merge_request) { create(:merge_request) }
- let_it_be(:user) { create(:user) }
-
- let(:branch) { merge_request.source_branch }
- let(:sha) { merge_request.source_branch_sha }
- let(:retarget_branch) { true }
- let(:worker) { described_class.new }
-
- describe '#perform' do
- context 'with a non-existing merge request' do
- it 'does nothing' do
- expect(::Branches::DeleteService).not_to receive(:new)
- worker.perform(non_existing_record_id, user.id, branch, retarget_branch)
- end
- end
-
- context 'with a non-existing user' do
- it 'does nothing' do
- expect(::Branches::DeleteService).not_to receive(:new)
-
- worker.perform(merge_request.id, non_existing_record_id, branch, retarget_branch)
- end
- end
-
- context 'with existing user and merge request' do
- it 'calls service to delete source branch' do
- expect_next_instance_of(::Branches::DeleteService) do |instance|
- expect(instance).to receive(:execute).with(branch)
- end
-
- worker.perform(merge_request.id, user.id, branch, retarget_branch)
- end
-
- context 'when retarget branch param is true' do
- it 'calls the retarget chain service' do
- expect_next_instance_of(::MergeRequests::RetargetChainService) do |instance|
- expect(instance).to receive(:execute).with(merge_request)
- end
-
- worker.perform(merge_request.id, user.id, branch, retarget_branch)
- end
- end
-
- context 'when retarget branch param is false' do
- let(:retarget_branch) { false }
-
- it 'does not call the retarget chain service' do
- expect(::MergeRequests::RetargetChainService).not_to receive(:new)
-
- worker.perform(merge_request.id, user.id, branch, retarget_branch)
- end
- end
- end
-
- it_behaves_like 'an idempotent worker' do
- let(:merge_request) { create(:merge_request) }
- let(:job_args) { [merge_request.id, sha, user.id, true] }
- end
- end
-end
diff --git a/spec/workers/merge_requests/delete_source_branch_worker_spec.rb b/spec/workers/merge_requests/delete_source_branch_worker_spec.rb
index 2935d3ef5dc..a7e4ffad259 100644
--- a/spec/workers/merge_requests/delete_source_branch_worker_spec.rb
+++ b/spec/workers/merge_requests/delete_source_branch_worker_spec.rb
@@ -3,17 +3,24 @@
require 'spec_helper'
RSpec.describe MergeRequests::DeleteSourceBranchWorker do
- let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:user) { create(:user) }
+ let_it_be(:merge_request) { create(:merge_request, author: user) }
let(:sha) { merge_request.source_branch_sha }
let(:worker) { described_class.new }
describe '#perform' do
+ before do
+ allow_next_instance_of(::Projects::DeleteBranchWorker) do |instance|
+ allow(instance).to receive(:perform).with(merge_request.source_project.id, user.id,
+ merge_request.source_branch)
+ end
+ end
+
context 'when the add_delete_branch_worker feature flag is enabled' do
context 'with a non-existing merge request' do
it 'does nothing' do
- expect(::MergeRequests::DeleteBranchWorker).not_to receive(:perform_async)
+ expect(::Projects::DeleteBranchWorker).not_to receive(:new)
worker.perform(non_existing_record_id, sha, user.id)
end
@@ -21,7 +28,7 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
context 'with a non-existing user' do
it 'does nothing' do
- expect(::MergeRequests::DeleteBranchWorker).not_to receive(:perform_async)
+ expect(::Projects::DeleteBranchWorker).not_to receive(:new)
worker.perform(merge_request.id, sha, non_existing_record_id)
end
@@ -29,15 +36,17 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
context 'with existing user and merge request' do
it 'creates a new delete branch worker async' do
- expect(::MergeRequests::DeleteBranchWorker).to receive(:perform_async).with(merge_request.id, user.id,
- merge_request.source_branch, true)
+ expect_next_instance_of(::Projects::DeleteBranchWorker) do |instance|
+ expect(instance).to receive(:perform).with(merge_request.source_project.id, user.id,
+ merge_request.source_branch)
+ end
worker.perform(merge_request.id, sha, user.id)
end
context 'source branch sha does not match' do
it 'does nothing' do
- expect(::MergeRequests::DeleteBranchWorker).not_to receive(:perform_async)
+ expect(::Projects::DeleteBranchWorker).not_to receive(:new)
worker.perform(merge_request.id, 'new-source-branch-sha', user.id)
end
@@ -45,7 +54,6 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
end
it_behaves_like 'an idempotent worker' do
- let(:merge_request) { create(:merge_request) }
let(:job_args) { [merge_request.id, sha, user.id] }
end
end
@@ -117,7 +125,6 @@ RSpec.describe MergeRequests::DeleteSourceBranchWorker do
end
it_behaves_like 'an idempotent worker' do
- let(:merge_request) { create(:merge_request) }
let(:job_args) { [merge_request.id, sha, user.id] }
end
end
diff --git a/spec/workers/pipeline_schedule_worker_spec.rb b/spec/workers/pipeline_schedule_worker_spec.rb
index 4a7db0eca56..d23907a8def 100644
--- a/spec/workers/pipeline_schedule_worker_spec.rb
+++ b/spec/workers/pipeline_schedule_worker_spec.rb
@@ -17,8 +17,12 @@ RSpec.describe PipelineScheduleWorker do
before do
stub_application_setting(auto_devops_enabled: false)
stub_ci_pipeline_to_return_yaml_file
+ end
- pipeline_schedule.update_column(:next_run_at, 1.day.ago)
+ around do |example|
+ travel_to(pipeline_schedule.next_run_at + 1.hour) do
+ example.run
+ end
end
context 'when the schedule is runnable by the user' do
@@ -26,16 +30,22 @@ RSpec.describe PipelineScheduleWorker do
project.add_maintainer(user)
end
- context 'when there is a scheduled pipeline within next_run_at' do
+ context 'when there is a scheduled pipeline within next_run_at', :sidekiq_inline do
shared_examples 'successful scheduling' do
- it 'creates a new pipeline', :sidekiq_might_not_need_inline do
+ it 'creates a new pipeline' do
expect { subject }.to change { project.ci_pipelines.count }.by(1)
- expect(Ci::Pipeline.last).to be_schedule
+ last_pipeline = project.ci_pipelines.last
+
+ expect(last_pipeline).to be_schedule
+ expect(last_pipeline.pipeline_schedule).to eq(pipeline_schedule)
+ end
+
+ it 'updates next_run_at' do
+ expect { subject }.to change { pipeline_schedule.reload.next_run_at }.by(1.day)
+ end
- pipeline_schedule.reload
- expect(pipeline_schedule.next_run_at).to be > Time.current
- expect(pipeline_schedule).to eq(project.ci_pipelines.last.pipeline_schedule)
- expect(pipeline_schedule).to be_active
+ it 'does not change active status' do
+ expect { subject }.not_to change { pipeline_schedule.reload.active? }.from(true)
end
end
diff --git a/spec/workers/projects/delete_branch_worker_spec.rb b/spec/workers/projects/delete_branch_worker_spec.rb
new file mode 100644
index 00000000000..c1289f56929
--- /dev/null
+++ b/spec/workers/projects/delete_branch_worker_spec.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+# rubocop: disable Gitlab/ServiceResponse
+
+require 'spec_helper'
+
+RSpec.describe Projects::DeleteBranchWorker, feature_category: :source_code_management do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ let(:branch) { 'master' }
+ let(:worker) { described_class.new }
+ let(:service_result) { ServiceResponse.success(message: 'placeholder', http_status: 200) }
+
+ before do
+ allow_next_instance_of(::Branches::DeleteService) do |instance|
+ allow(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+ end
+
+ describe '#perform' do
+ context 'when the branch does not exist' do
+ let(:branch) { 'non_existent_branch_name' }
+
+ it 'does nothing' do
+ expect(::Branches::DeleteService).not_to receive(:new)
+
+ worker.perform(project.id, user.id, branch)
+ end
+ end
+
+ context 'with a non-existing project' do
+ it 'does nothing' do
+ expect(::Branches::DeleteService).not_to receive(:new)
+
+ worker.perform(non_existing_record_id, user.id, branch)
+ end
+ end
+
+ context 'with a non-existing user' do
+ it 'does nothing' do
+ expect(::Branches::DeleteService).not_to receive(:new)
+
+ worker.perform(project.id, non_existing_record_id, branch)
+ end
+ end
+
+ context 'with existing user and project' do
+ it 'calls service to delete source branch' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ worker.perform(project.id, user.id, branch)
+ end
+
+ context 'when delete service returns an error' do
+ let(:service_result) { ServiceResponse.error(message: 'placeholder', http_status: status_code) }
+
+ context 'when the status code is 400' do
+ let(:status_code) { 400 }
+
+ it 'tracks and raises the exception' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ expect(service_result).to receive(:track_and_raise_exception).and_call_original
+
+ expect { worker.perform(project.id, user.id, branch) }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when the status code is not 400' do
+ let(:status_code) { 405 }
+
+ it 'does not track the exception' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ expect(service_result).not_to receive(:track_and_raise_exception)
+
+ expect { worker.perform(project.id, user.id, branch) }.not_to raise_error
+ end
+ end
+
+ context 'when track_and_raise_delete_source_errors is disabled' do
+ let(:status_code) { 400 }
+
+ before do
+ stub_feature_flags(track_and_raise_delete_source_errors: false)
+ end
+
+ it 'does not track the exception' do
+ expect_next_instance_of(::Branches::DeleteService) do |instance|
+ expect(instance).to receive(:execute).with(branch).and_return(service_result)
+ end
+
+ expect(service_result).not_to receive(:track_and_raise_exception)
+
+ expect { worker.perform(project.id, user.id, branch) }.not_to raise_error
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { [project.id, user.id, branch] }
+ end
+ end
+ # rubocop: enable Gitlab/ServiceResponse
+end
diff --git a/spec/workers/run_pipeline_schedule_worker_spec.rb b/spec/workers/run_pipeline_schedule_worker_spec.rb
index 5fa7c5d64db..4fdf6149435 100644
--- a/spec/workers/run_pipeline_schedule_worker_spec.rb
+++ b/spec/workers/run_pipeline_schedule_worker_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
RSpec.describe RunPipelineScheduleWorker do
+ it 'has an until_executed deduplicate strategy' do
+ expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
+ end
+
describe '#perform' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, namespace: group) }
@@ -11,6 +15,12 @@ RSpec.describe RunPipelineScheduleWorker do
let(:worker) { described_class.new }
+ around do |example|
+ travel_to(pipeline_schedule.next_run_at + 1.hour) do
+ example.run
+ end
+ end
+
context 'when a schedule not found' do
it 'does not call the Service' do
expect(Ci::CreatePipelineService).not_to receive(:new)
diff --git a/yarn.lock b/yarn.lock
index 87b8973b151..2a95e1f2a1f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1125,10 +1125,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.13.0.tgz#2d62286c956bd49ba7156b2aa4eed79507baca53"
integrity sha512-Yv4dZ4pOyUVMCZXNxLuMinZ/x8E6+g8/yM1z/2ERT0t7hSAC3bCUHn2OEFpujtYzFtwMZXMFPQFEJJipQ1I/+w==
-"@gitlab/ui@50.1.2":
- version "50.1.2"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-50.1.2.tgz#d24100b6d6fc77c7708d9139f09e2142b1292d31"
- integrity sha512-bRzF9SkDGY2WrmIuFriFLyRMym2ydAeJB71FCXfHvhei3EWAeaiZv5oEB/a4oMN8nmt0rt4GrPQ5PpGiQKoKfQ==
+"@gitlab/ui@51.1.1":
+ version "51.1.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-51.1.1.tgz#fa6d3bee9a08865fadbf6a7d753785b0fbb964a2"
+ integrity sha512-s2bupr0Fv4jR1Pdt7kMSTJa9gQ2IxGMx/3MWbqnrn20bUSaeI3eUSmrBzzar7My9/mJVS6JYgfN3JN+V0XuTTg==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"