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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_manual_todo.yml49
-rw-r--r--app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue1
-rw-r--r--app/assets/javascripts/members/components/action_buttons/remove_member_button.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/remove_member_modal.vue18
-rw-r--r--app/graphql/types/ci/job_type.rb6
-rw-r--r--app/uploaders/object_storage.rb2
-rw-r--r--app/views/projects/diffs/_diffs.html.haml4
-rw-r--r--app/views/projects/diffs/_file.html.haml6
-rw-r--r--app/views/shared/notes/_comment_button.html.haml8
-rw-r--r--changelogs/unreleased/259794-members-view-update-revoke-invite-modal-header-and-button.yml5
-rw-r--r--changelogs/unreleased/273284-update-commit-page-buttons.yml5
-rw-r--r--changelogs/unreleased/issue-220040-fix-rails-savebang-requests-api-module.yml5
-rw-r--r--changelogs/unreleased/issue-325836-fix-empty-line-after-let-it-be-models-concerns.yml5
-rw-r--r--changelogs/unreleased/jivanvl-add-tags-job-type-graphql.yml5
-rw-r--r--changelogs/unreleased/pl-rubocop-style-hashtransformation-lib-directory.yml5
-rw-r--r--changelogs/unreleased/rails-save-bang-features-issues.yml5
-rw-r--r--changelogs/unreleased/sh-enable-workhorse-extract-filename-base-default.yml5
-rw-r--r--config/feature_flags/development/workhorse_extract_filename_base.yml2
-rw-r--r--doc/administration/environment_variables.md7
-rw-r--r--doc/administration/external_pipeline_validation.md21
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/development/cicd/index.md14
-rw-r--r--doc/development/testing_guide/frontend_testing.md59
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb16
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/atlassian/jira_connect/client.rb4
-rw-r--r--lib/banzai/filter/repository_link_filter.rb2
-rw-r--r--lib/gitlab/ci/config/entry/product/variables.rb3
-rw-r--r--lib/gitlab/ci/config/entry/variables.rb4
-rw-r--r--lib/gitlab/ci/variables/helpers.rb2
-rw-r--r--lib/gitlab/git/commit.rb2
-rw-r--r--lib/gitlab/import_sources.rb2
-rw-r--r--lib/gitlab/language_detection.rb5
-rw-r--r--lib/gitlab/metrics/samplers/database_sampler.rb4
-rw-r--r--lib/gitlab/phabricator_import/project_creator.rb13
-rw-r--r--lib/gitlab/prometheus_client.rb2
-rw-r--r--lib/gitlab/repository_hash_cache.rb2
-rw-r--r--lib/gitlab/static_site_editor/config/file_config.rb2
-rw-r--r--lib/gitlab/template/base_template.rb4
-rw-r--r--lib/gitlab/usage_data_counters/base_counter.rb4
-rw-r--r--lib/gitlab/usage_data_counters/note_counter.rb6
-rw-r--r--spec/features/issues/bulk_assignment_labels_spec.rb4
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb2
-rw-r--r--spec/features/issues/note_polling_spec.rb2
-rw-r--r--spec/features/issues/user_creates_branch_and_merge_request_spec.rb2
-rw-r--r--spec/features/issues/user_creates_confidential_merge_request_spec.rb2
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb4
-rw-r--r--spec/features/issues/user_filters_issues_spec.rb2
-rw-r--r--spec/features/issues/user_sees_live_update_spec.rb2
-rw-r--r--spec/features/issues/user_sorts_issues_spec.rb46
-rw-r--r--spec/frontend/__helpers__/vue_test_utils_helper.js73
-rw-r--r--spec/frontend/__helpers__/vue_test_utils_helper_spec.js208
-rw-r--r--spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js1
-rw-r--r--spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js1
-rw-r--r--spec/frontend/members/components/action_buttons/remove_member_button_spec.js2
-rw-r--r--spec/frontend/members/components/action_buttons/user_action_buttons_spec.js1
-rw-r--r--spec/frontend/vue_shared/components/remove_member_modal_spec.js11
-rw-r--r--spec/graphql/resolvers/ci/jobs_resolver_spec.rb17
-rw-r--r--spec/graphql/types/ci/job_type_spec.rb1
-rw-r--r--spec/models/concerns/batch_destroy_dependent_associations_spec.rb1
-rw-r--r--spec/models/concerns/featurable_spec.rb1
-rw-r--r--spec/requests/api/commit_statuses_spec.rb8
-rw-r--r--spec/requests/api/deployments_spec.rb4
-rw-r--r--spec/requests/api/environments_spec.rb2
-rw-r--r--spec/requests/api/go_proxy_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb2
-rw-r--r--spec/requests/api/group_import_spec.rb2
-rw-r--r--spec/requests/api/group_milestones_spec.rb6
-rw-r--r--spec/requests/api/internal/base_spec.rb2
-rw-r--r--spec/requests/api/issues/get_group_issues_spec.rb2
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb2
-rw-r--r--spec/requests/api/jobs_spec.rb12
-rw-r--r--spec/requests/api/labels_spec.rb6
-rw-r--r--spec/requests/api/project_import_spec.rb4
74 files changed, 557 insertions, 205 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 97b36b84172..a8e2f98e174 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -181,16 +181,6 @@ Rails/SaveBang:
- 'spec/features/dashboard/issuables_counter_spec.rb'
- 'spec/features/dashboard/project_member_activity_index_spec.rb'
- 'spec/features/dashboard/projects_spec.rb'
- - 'spec/features/issues/bulk_assignment_labels_spec.rb'
- - 'spec/features/issues/gfm_autocomplete_spec.rb'
- - 'spec/features/issues/issue_sidebar_spec.rb'
- - 'spec/features/issues/note_polling_spec.rb'
- - 'spec/features/issues/user_creates_branch_and_merge_request_spec.rb'
- - 'spec/features/issues/user_creates_confidential_merge_request_spec.rb'
- - 'spec/features/issues/user_edits_issue_spec.rb'
- - 'spec/features/issues/user_filters_issues_spec.rb'
- - 'spec/features/issues/user_sees_live_update_spec.rb'
- - 'spec/features/issues/user_sorts_issues_spec.rb'
- 'spec/frontend/fixtures/issues.rb'
- 'spec/frontend/fixtures/merge_requests.rb'
- 'spec/graphql/mutations/merge_requests/set_locked_spec.rb'
@@ -302,23 +292,6 @@ Rails/SaveBang:
- 'spec/models/user_status_spec.rb'
- 'spec/models/wiki_page/meta_spec.rb'
- 'spec/models/wiki_page_spec.rb'
- - 'spec/requests/api/ci/runner_spec.rb'
- - 'spec/requests/api/commit_statuses_spec.rb'
- - 'spec/requests/api/conan_packages_spec.rb'
- - 'spec/requests/api/deployments_spec.rb'
- - 'spec/requests/api/environments_spec.rb'
- - 'spec/requests/api/go_proxy_spec.rb'
- - 'spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb'
- - 'spec/requests/api/graphql/user_query_spec.rb'
- - 'spec/requests/api/graphql_spec.rb'
- - 'spec/requests/api/group_import_spec.rb'
- - 'spec/requests/api/group_milestones_spec.rb'
- - 'spec/requests/api/internal/base_spec.rb'
- - 'spec/requests/api/issues/get_group_issues_spec.rb'
- - 'spec/requests/api/issues/post_projects_issues_spec.rb'
- - 'spec/requests/api/jobs_spec.rb'
- - 'spec/requests/api/labels_spec.rb'
- - 'spec/requests/api/project_import_spec.rb'
Rails/TimeZone:
Enabled: true
@@ -1102,8 +1075,6 @@ RSpec/EmptyLineAfterFinalLetItBe:
- spec/models/ci/stage_spec.rb
- spec/models/clusters/kubernetes_namespace_spec.rb
- spec/models/commit_spec.rb
- - spec/models/concerns/batch_destroy_dependent_associations_spec.rb
- - spec/models/concerns/featurable_spec.rb
- spec/models/deploy_token_spec.rb
- spec/models/deployment_spec.rb
- spec/models/diff_viewer/server_side_spec.rb
@@ -3335,26 +3306,6 @@ Style/HashTransformation:
- 'ee/lib/gitlab/custom_file_templates.rb'
- 'ee/spec/elastic_integration/global_search_spec.rb'
- 'ee/spec/lib/ee/gitlab/application_context_spec.rb'
- - 'lib/api/helpers/packages/conan/api_helpers.rb'
- - 'lib/api/projects.rb'
- - 'lib/atlassian/jira_connect/client.rb'
- - 'lib/banzai/filter/repository_link_filter.rb'
- - 'lib/gitlab/ci/config/entry/product/variables.rb'
- - 'lib/gitlab/ci/config/entry/variables.rb'
- - 'lib/gitlab/ci/variables/collection.rb'
- - 'lib/gitlab/ci/variables/helpers.rb'
- - 'lib/gitlab/git/commit.rb'
- - 'lib/gitlab/import_sources.rb'
- - 'lib/gitlab/language_detection.rb'
- - 'lib/gitlab/metrics/samplers/database_sampler.rb'
- - 'lib/gitlab/metrics/subscribers/active_record.rb'
- - 'lib/gitlab/phabricator_import/project_creator.rb'
- - 'lib/gitlab/prometheus_client.rb'
- - 'lib/gitlab/repository_hash_cache.rb'
- - 'lib/gitlab/static_site_editor/config/file_config.rb'
- - 'lib/gitlab/template/base_template.rb'
- - 'lib/gitlab/usage_data_counters/base_counter.rb'
- - 'lib/gitlab/usage_data_counters/note_counter.rb'
- 'spec/lib/atlassian/jira_connect/serializers/pull_request_entity_spec.rb'
- 'spec/lib/gitlab/ci/status/composite_spec.rb'
- 'spec/lib/gitlab/conflict/file_spec.rb'
diff --git a/app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue b/app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue
index 0bcc85157f1..91062c222f4 100644
--- a/app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue
+++ b/app/assets/javascripts/members/components/action_buttons/invite_action_buttons.vue
@@ -42,6 +42,7 @@ export default {
:member-id="member.id"
:message="message"
:title="s__('Member|Revoke invite')"
+ is-invite
/>
</div>
</action-button-group>
diff --git a/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue b/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
index 3777f1dbb97..9954da3e0d4 100644
--- a/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
+++ b/app/assets/javascripts/members/components/action_buttons/remove_member_button.vue
@@ -36,6 +36,11 @@ export default {
required: false,
default: false,
},
+ isInvite: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
...mapState(['memberPath']),
@@ -57,6 +62,7 @@ export default {
:data-member-path="computedMemberPath"
:data-member-type="memberType"
:data-is-access-request="isAccessRequest"
+ :data-is-invite="isInvite"
:data-message="message"
data-qa-selector="delete_member_button"
/>
diff --git a/app/assets/javascripts/vue_shared/components/remove_member_modal.vue b/app/assets/javascripts/vue_shared/components/remove_member_modal.vue
index 2a19537f820..9968240b5b1 100644
--- a/app/assets/javascripts/vue_shared/components/remove_member_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/remove_member_modal.vue
@@ -2,7 +2,7 @@
import { GlFormCheckbox, GlModal } from '@gitlab/ui';
import { parseBoolean } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf';
-import { __ } from '~/locale';
+import { s__, __ } from '~/locale';
export default {
actionCancel: {
@@ -22,11 +22,20 @@ export default {
isAccessRequest() {
return parseBoolean(this.modalData.isAccessRequest);
},
+ isInvite() {
+ return parseBoolean(this.modalData.isInvite);
+ },
isGroupMember() {
return this.modalData.memberType === 'GroupMember';
},
actionText() {
- return this.isAccessRequest ? __('Deny access request') : __('Remove member');
+ if (this.isAccessRequest) {
+ return __('Deny access request');
+ } else if (this.isInvite) {
+ return s__('Member|Revoke invite');
+ }
+
+ return __('Remove member');
},
actionPrimary() {
return {
@@ -36,6 +45,9 @@ export default {
},
};
},
+ showUnassignIssuablesCheckbox() {
+ return !this.isAccessRequest && !this.isInvite;
+ },
},
mounted() {
document.addEventListener('click', this.handleClick);
@@ -76,7 +88,7 @@ export default {
<gl-form-checkbox v-if="isGroupMember" name="remove_sub_memberships">
{{ __('Also remove direct user membership from subgroups and projects') }}
</gl-form-checkbox>
- <gl-form-checkbox v-if="!isAccessRequest" name="unassign_issuables">
+ <gl-form-checkbox v-if="showUnassignIssuablesCheckbox" name="unassign_issuables">
{{ __('Also unassign this user from related issues and merge requests') }}
</gl-form-checkbox>
</form>
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index 2d766a14291..94a256fed3d 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -26,6 +26,8 @@ module Types
description: 'Whether this job is allowed to fail.'
field :duration, GraphQL::INT_TYPE, null: true,
description: 'Duration of the job in seconds.'
+ field :tags, [GraphQL::STRING_TYPE], null: true,
+ description: 'Tags for the current job.'
# Life-cycle timestamps:
field :created_at, Types::TimeType, null: false,
@@ -68,6 +70,10 @@ module Types
Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
end
+ def tags
+ object.tags.map(&:name) if object.is_a?(::Ci::Build)
+ end
+
def detailed_status
object.detailed_status(context[:current_user])
end
diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb
index 8c9c861e1fe..ea71930062c 100644
--- a/app/uploaders/object_storage.rb
+++ b/app/uploaders/object_storage.rb
@@ -187,7 +187,7 @@ module ObjectStorage
hash[:TempPath] = workhorse_local_upload_path
end
- hash[:FeatureFlagExtractBase] = Feature.enabled?(:workhorse_extract_filename_base)
+ hash[:FeatureFlagExtractBase] = Feature.enabled?(:workhorse_extract_filename_base, default_enabled: :yaml)
hash[:MaximumSize] = maximum_size if maximum_size.present?
end
end
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index cec8948aaa4..1c7a9ffe0bb 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -8,7 +8,7 @@
.content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed
.files-changed-inner
- .inline-parallel-buttons.d-none.d-md-block
+ .inline-parallel-buttons.gl-display-none.gl-md-display-flex
- if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? }
= link_to _('Expand all'), url_for(safe_params.merge(expanded: 1, format: nil)), class: 'gl-button btn btn-default'
- if show_whitespace_toggle
@@ -20,7 +20,7 @@
= diff_compare_whitespace_link(diffs.project, params[:from], params[:to], class: 'd-none d-sm-inline-block')
- elsif current_controller?(:wikis)
= toggle_whitespace_link(url_for(params_with_whitespace), class: 'd-none d-sm-inline-block')
- .btn-group
+ .btn-group.gl-ml-3
= inline_diff_btn
= parallel_diff_btn
= render 'projects/diffs/stats', diff_files: diff_files
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 9197b177b7b..35e2fe1b398 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -14,10 +14,10 @@
= submodule_diff_compare_link(diff_file)
- unless diff_file.submodule?
- .file-actions.d-none.d-sm-block
+ .file-actions.gl-display-none.gl-sm-display-flex
- if diff_file.blob&.readable_text?
- %span.has-tooltip{ title: _("Toggle comments for this file") }
- = link_to '#', class: 'js-toggle-diff-comments btn gl-button btn-default selected', disabled: @diff_notes_disabled do
+ %span.has-tooltip.gl-mr-3{ title: _("Toggle comments for this file") }
+ = link_to '#', class: 'js-toggle-diff-comments btn gl-button btn-default btn-icon selected', disabled: @diff_notes_disabled do
= sprite_icon('comment')
\
- if editable_diff?(diff_file)
diff --git a/app/views/shared/notes/_comment_button.html.haml b/app/views/shared/notes/_comment_button.html.haml
index 3e81b1fcb13..d415c64b929 100644
--- a/app/views/shared/notes/_comment_button.html.haml
+++ b/app/views/shared/notes/_comment_button.html.haml
@@ -1,15 +1,15 @@
- noteable_name = @note.noteable.human_class_name
.float-left.btn-group.gl-mr-3.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown
- %input.btn.gl-button.btn-success.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment'), data: { qa_selector: 'comment_button' } }
+ %input.btn.gl-button.btn-confirm.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment'), data: { qa_selector: 'comment_button' } }
- if @note.can_be_discussion_note?
- = button_tag type: 'button', class: 'gl-button btn dropdown-toggle btn-success js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do
+ = button_tag type: 'button', class: 'gl-button btn dropdown-toggle btn-confirm btn-icon js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do
= sprite_icon('chevron-down')
%ul#resolvable-comment-menu.dropdown-menu.dropdown-open-top{ data: { dropdown: true } }
%li#comment.droplab-item-selected{ data: { value: '', 'submit-text' => _('Comment'), 'close-text' => _("Comment & close %{noteable_name}") % { noteable_name: noteable_name }, 'reopen-text' => _("Comment & reopen %{noteable_name}") % { noteable_name: noteable_name } } }
- %button{ type: 'button' }
+ %button.btn.gl-button.btn-default-tertiary
= sprite_icon('check', css_class: 'icon')
.description
%strong= _("Comment")
@@ -19,7 +19,7 @@
%li.divider.droplab-item-ignore
%li#discussion{ data: { value: 'DiscussionNote', 'submit-text' => _('Start thread'), 'close-text' => _("Start thread & close %{noteable_name}") % { noteable_name: noteable_name }, 'reopen-text' => _("Start thread & reopen %{noteable_name}") % { noteable_name: noteable_name } } }
- %button{ type: 'button' }
+ %button.btn.gl-button.btn-default-tertiary
= sprite_icon('check', css_class: 'icon')
.description
%strong= _("Start thread")
diff --git a/changelogs/unreleased/259794-members-view-update-revoke-invite-modal-header-and-button.yml b/changelogs/unreleased/259794-members-view-update-revoke-invite-modal-header-and-button.yml
new file mode 100644
index 00000000000..4aa3e7ee55d
--- /dev/null
+++ b/changelogs/unreleased/259794-members-view-update-revoke-invite-modal-header-and-button.yml
@@ -0,0 +1,5 @@
+---
+title: Update title on revoke member invite modal and hide unneeded related issues and merge requests checkbox
+merge_request: 57755
+author:
+type: changed
diff --git a/changelogs/unreleased/273284-update-commit-page-buttons.yml b/changelogs/unreleased/273284-update-commit-page-buttons.yml
new file mode 100644
index 00000000000..95257d0f6d3
--- /dev/null
+++ b/changelogs/unreleased/273284-update-commit-page-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Update buttons and spacing on commit page
+merge_request: 56793
+author:
+type: changed
diff --git a/changelogs/unreleased/issue-220040-fix-rails-savebang-requests-api-module.yml b/changelogs/unreleased/issue-220040-fix-rails-savebang-requests-api-module.yml
new file mode 100644
index 00000000000..316bc1d5cc0
--- /dev/null
+++ b/changelogs/unreleased/issue-220040-fix-rails-savebang-requests-api-module.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Rails/SaveBang Rubocop offenses for requests/api module
+merge_request: 57887
+author: Huzaifa Iftikhar @huzaifaiftikhar
+type: fixed
diff --git a/changelogs/unreleased/issue-325836-fix-empty-line-after-let-it-be-models-concerns.yml b/changelogs/unreleased/issue-325836-fix-empty-line-after-let-it-be-models-concerns.yml
new file mode 100644
index 00000000000..27eb4d38a23
--- /dev/null
+++ b/changelogs/unreleased/issue-325836-fix-empty-line-after-let-it-be-models-concerns.yml
@@ -0,0 +1,5 @@
+---
+title: Fix EmptyLineAfterFinalLetItBe offenses in spec/models/concerns
+merge_request: 58367
+author: Huzaifa Iftikhar @huzaifaiftikhar
+type: fixed
diff --git a/changelogs/unreleased/jivanvl-add-tags-job-type-graphql.yml b/changelogs/unreleased/jivanvl-add-tags-job-type-graphql.yml
new file mode 100644
index 00000000000..fbfed90706d
--- /dev/null
+++ b/changelogs/unreleased/jivanvl-add-tags-job-type-graphql.yml
@@ -0,0 +1,5 @@
+---
+title: Add tags field to jobType in the CI namespace
+merge_request: 57631
+author:
+type: changed
diff --git a/changelogs/unreleased/pl-rubocop-style-hashtransformation-lib-directory.yml b/changelogs/unreleased/pl-rubocop-style-hashtransformation-lib-directory.yml
new file mode 100644
index 00000000000..e2ee3eade14
--- /dev/null
+++ b/changelogs/unreleased/pl-rubocop-style-hashtransformation-lib-directory.yml
@@ -0,0 +1,5 @@
+---
+title: Fix cop offenses for Style/HashTransformation in lib directory
+merge_request: 56583
+author: Karthik Sivadas @karthik.sivadas
+type: other
diff --git a/changelogs/unreleased/rails-save-bang-features-issues.yml b/changelogs/unreleased/rails-save-bang-features-issues.yml
new file mode 100644
index 00000000000..6942bd383dd
--- /dev/null
+++ b/changelogs/unreleased/rails-save-bang-features-issues.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Rails/SaveBang rubocop offenses in spec/features/issues
+merge_request: 57900
+author: Abdul Wadood @abdulwd
+type: fixed
diff --git a/changelogs/unreleased/sh-enable-workhorse-extract-filename-base-default.yml b/changelogs/unreleased/sh-enable-workhorse-extract-filename-base-default.yml
new file mode 100644
index 00000000000..cd2d5e8410c
--- /dev/null
+++ b/changelogs/unreleased/sh-enable-workhorse-extract-filename-base-default.yml
@@ -0,0 +1,5 @@
+---
+title: Set workhorse_extract_filename_base feature flag to default
+merge_request: 58504
+author:
+type: changed
diff --git a/config/feature_flags/development/workhorse_extract_filename_base.yml b/config/feature_flags/development/workhorse_extract_filename_base.yml
index ee0442bb016..a80f6c45f28 100644
--- a/config/feature_flags/development/workhorse_extract_filename_base.yml
+++ b/config/feature_flags/development/workhorse_extract_filename_base.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326379
milestone: '13.11'
type: development
group: group::source code
-default_enabled: false
+default_enabled: true
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index f886e19b13d..a168584e754 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -21,6 +21,9 @@ You can use the following environment variables to override certain values:
|--------------------------------------------|---------|---------------------------------------------------------------------------------------------------------|
| `DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development`. |
| `ENABLE_BOOTSNAP` | string | Enables Bootsnap for speeding up initial Rails boot (`1` to enable). |
+| `EXTERNAL_VALIDATION_SERVICE_TIMEOUT` | integer | Timeout, in seconds, for an [external CI/CD pipeline validation service](external_pipeline_validation.md). Default is `5`. |
+| `EXTERNAL_VALIDATION_SERVICE_URL` | string | URL to an [external CI/CD pipeline validation service](external_pipeline_validation.md). |
+| `EXTERNAL_VALIDATION_SERVICE_TOKEN` | string | The `X-Gitlab-Token` for authentication with an [external CI/CD pipeline validation service](external_pipeline_validation.md). |
| `GITLAB_CDN_HOST` | string | Sets the base URL for a CDN to serve static assets (for example, `//mycdnsubdomain.fictional-cdn.com`). |
| `GITLAB_EMAIL_DISPLAY_NAME` | string | The name used in the **From** field in emails sent by GitLab. |
| `GITLAB_EMAIL_FROM` | string | The email address used in the **From** field in emails sent by GitLab. |
@@ -29,8 +32,8 @@ You can use the following environment variables to override certain values:
| `GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`). |
| `GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation. |
| `GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN` | string | Sets the initial registration token used for runners. |
-| `GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
-| `GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
+| `GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
+| `GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). |
| `RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging`, or `test`. |
| `UNSTRUCTURED_RAILS_LOG` | string | Enables the unstructured log in addition to JSON logs (defaults to `true`). |
diff --git a/doc/administration/external_pipeline_validation.md b/doc/administration/external_pipeline_validation.md
index 482ca320736..f8329b24d6c 100644
--- a/doc/administration/external_pipeline_validation.md
+++ b/doc/administration/external_pipeline_validation.md
@@ -7,7 +7,7 @@ type: reference, howto
# External Pipeline Validation
-You can use an external service for validating a pipeline before it's created.
+You can use an external service to validate a pipeline before it's created.
WARNING:
This is an experimental feature and subject to change without notice.
@@ -19,19 +19,17 @@ data as payload. GitLab then invalidates the pipeline based on the response
code. If there's an error or the request times out, the pipeline is not
invalidated.
-Response Code Legend:
+Response codes:
-- `200` - Accepted
-- `406` - Not Accepted
-- Other Codes - Accepted and Logged
+- `200`: Accepted
+- `4XX`: Not accepted
+- All other codes: accepted and logged
## Configuration
-To configure external pipeline validation:
-
-1. Set the `EXTERNAL_VALIDATION_SERVICE_URL` environment variable to the external
- service URL.
-1. Enable the `ci_external_validation_service` feature flag.
+To configure external pipeline validation, add the
+[`EXTERNAL_VALIDATION_SERVICE_URL` environment variable](environment_variables.md)
+and set it to the external service URL.
By default, requests to the external service time out after five seconds. To override
the default, set the `EXTERNAL_VALIDATION_SERVICE_TIMEOUT` environment variable to the
@@ -131,3 +129,6 @@ required number of seconds.
}
}
```
+
+The `namespace` field is only available in [GitLab Premium](https://about.gitlab.com/pricing/)
+and higher.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index fe85d43fd14..303b8c5b574 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1235,6 +1235,7 @@ An edge in a connection.
| `stage` | [`CiStage`](#cistage) | Stage of the job. |
| `startedAt` | [`Time`](#time) | When the job was started. |
| `status` | [`CiJobStatus`](#cijobstatus) | Status of the job. |
+| `tags` | [`[String!]`](#string) | Tags for the current job. |
### `CiJobArtifact`
diff --git a/doc/development/cicd/index.md b/doc/development/cicd/index.md
index eb2224d710a..242446590bb 100644
--- a/doc/development/cicd/index.md
+++ b/doc/development/cicd/index.md
@@ -182,3 +182,17 @@ Watch a walkthrough of this feature in details in the video below.
<figure class="video-container">
<iframe src="https://www.youtube.com/embed/NmdWRGT8kZg" frameborder="0" allowfullscreen="true"> </iframe>
</figure>
+
+## External pipeline validation service
+
+The [external CI/CD pipeline validation service](../../administration/external_pipeline_validation.md)
+is available for use on self-managed GitLab instances, but is not in use on GitLab.com.
+It is configured with [environment variables](../../administration/environment_variables.md)
+on the instance.
+
+To enable the feature on GitLab.com, enable the `ci_external_validation_service`
+[feature flag](../feature_flags/index.md). The valid "Not accepted" response code
+for GitLab.com is `406` only.
+
+For more details, see the linked issues and MRs in the
+[feature flag rollout issue](https://gitlab.com/gitlab-org/gitlab/-/issues/325982).
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 78f8e320269..1842bf5e7f8 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -212,8 +212,8 @@ When it comes to querying DOM elements in your tests, it is best to uniquely and
the element.
Preferentially, this is done by targeting what the user actually sees using [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro/).
-When selecting by text it is best to use [`getByRole` or `findByRole`](https://testing-library.com/docs/queries/byrole/)
-as these enforce accessibility best practices as well. The examples below demonstrate the order of preference.
+When selecting by text it is best to use the [`byRole`](https://testing-library.com/docs/queries/byrole) query
+as it helps enforce accessibility best practices. `findByRole` and the other [DOM Testing Library queries](https://testing-library.com/docs/queries/about) are available when using [`shallowMountExtended` or `mountExtended`](#shallowmountextended-and-mountextended).
When writing Vue component unit tests, it can be wise to query children by component, so that the unit test can focus on comprehensive value coverage
rather than dealing with the complexity of a child component's behavior.
@@ -223,25 +223,27 @@ possible selectors include:
- A semantic attribute like `name` (also verifies that `name` was setup properly)
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
- optionally combined with [`findByTestId`](#extendedwrapper-and-findbytestid)
+ optionally combined with [`shallowMountExtended` or `mountExtended`](#shallowmountextended-and-mountextended)
- a Vue `ref` (if using `@vue/test-utils`)
```javascript
-import { getByRole, getByText } from '@testing-library/dom'
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper'
+
+const wrapper = shallowMountExtended(ExampleComponent);
// In this example, `wrapper` is a `@vue/test-utils` wrapper returned from `mount` or `shallowMount`.
it('exists', () => {
// Best (especially for integration tests)
- getByRole(wrapper.element, 'link', { name: /Click Me/i })
- getByRole(wrapper.element, 'link', { name: 'Click Me' })
- getByText(wrapper.element, 'Click Me')
- getByText(wrapper.element, /Click Me/i)
+ wrapper.findByRole('link', { name: /Click Me/i })
+ wrapper.findByRole('link', { name: 'Click Me' })
+ wrapper.findByText('Click Me')
+ wrapper.findByText(/Click Me/i)
// Good (especially for unit tests)
wrapper.find(FooComponent);
wrapper.find('input[name=foo]');
wrapper.find('[data-testid="my-foo-id"]');
- wrapper.findByTestId('my-foo-id'); // with the extendedWrapper utility – check below
+ wrapper.findByTestId('my-foo-id'); // with shallowMountExtended or mountExtended – check below
wrapper.find({ ref: 'foo'});
// Bad
@@ -1138,23 +1140,40 @@ These are very useful if you don't have a handle to the request's Promise, for e
Both functions run `callback` on the next tick after the requests finish (using `setImmediate()`), to allow any `.then()` or `.catch()` handlers to run.
-### `extendedWrapper` and `findByTestId`
+### `shallowMountExtended` and `mountExtended`
-Using `data-testid` is one of the [recommended ways to query DOM elements](#how-to-query-dom-elements).
-You can use the `extendedWrapper` utility on the `wrapper` returned by `shalowMount`/`mount`.
-By doing so, the `wrapper` provides you with the ability to perform a `findByTestId`,
-which is a shortcut to the more verbose `wrapper.find('[data-testid="my-test-id"]');`
+The `shallowMountExtended` and `mountExtended` utilities provide you with the ability to perform
+any of the available [DOM Testing Library queries](https://testing-library.com/docs/queries/about)
+by prefixing them with `find` or `findAll`.
```javascript
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
describe('FooComponent', () => {
- const wrapper = extendedWrapper(shallowMount({
- template: `<div data-testid="my-test-id"></div>`,
- }));
+ const wrapper = shallowMountExtended({
+ template: `
+ <div data-testid="gitlab-frontend-stack">
+ <p>GitLab frontend stack</p>
+ <div role="tablist">
+ <button role="tab" aria-selected="true">Vue.js</button>
+ <button role="tab" aria-selected="false">GraphQL</button>
+ <button role="tab" aria-selected="false">SCSS</button>
+ </div>
+ </div>
+ `,
+ });
+
+ it('finds elements with `findByTestId`', () => {
+ expect(wrapper.findByTestId('gitlab-frontend-stack').exists()).toBe(true);
+ });
+
+ it('finds elements with `findByText`', () => {
+ expect(wrapper.findByText('GitLab frontend stack').exists()).toBe(true);
+ expect(wrapper.findByText('TypeScript').exists()).toBe(false);
+ });
- it('exists', () => {
- expect(wrapper.findByTestId('my-test-id').exists()).toBe(true);
+ it('finds elements with `findAllByRole`', () => {
+ expect(wrapper.findAllByRole('tab').length).toBe(3);
});
});
```
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index 24ebeb007d3..5499b6fe653 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -32,19 +32,15 @@ module API
end
def recipe_upload_urls
- { upload_urls: Hash[
- file_names.select(&method(:recipe_file?)).map do |file_name|
- [file_name, build_recipe_file_upload_url(file_name)]
- end
- ] }
+ { upload_urls: file_names.select(&method(:recipe_file?)).to_h do |file_name|
+ [file_name, build_recipe_file_upload_url(file_name)]
+ end }
end
def package_upload_urls
- { upload_urls: Hash[
- file_names.select(&method(:package_file?)).map do |file_name|
- [file_name, build_package_file_upload_url(file_name)]
- end
- ] }
+ { upload_urls: file_names.select(&method(:package_file?)).to_h do |file_name|
+ [file_name, build_package_file_upload_url(file_name)]
+ end }
end
def recipe_file?(file_name)
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 880ece9048a..7674e67cd8f 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -485,7 +485,7 @@ module API
get ':id/languages', feature_category: :source_code_management do
::Projects::RepositoryLanguagesService
.new(user_project, current_user)
- .execute.map { |lang| [lang.name, lang.share] }.to_h
+ .execute.to_h { |lang| [lang.name, lang.share] }
end
desc 'Delete a project'
diff --git a/lib/atlassian/jira_connect/client.rb b/lib/atlassian/jira_connect/client.rb
index 6f87b7b7d3c..ea83076c49b 100644
--- a/lib/atlassian/jira_connect/client.rb
+++ b/lib/atlassian/jira_connect/client.rb
@@ -141,9 +141,9 @@ module Atlassian
def user_notes_count(merge_requests)
return unless merge_requests
- Note.count_for_collection(merge_requests.map(&:id), 'MergeRequest').map do |count_group|
+ Note.count_for_collection(merge_requests.map(&:id), 'MergeRequest').to_h do |count_group|
[count_group.noteable_id, count_group.count]
- end.to_h
+ end
end
def jwt_token(http_method, uri)
diff --git a/lib/banzai/filter/repository_link_filter.rb b/lib/banzai/filter/repository_link_filter.rb
index 66b9aac3e7e..04bbcabd93f 100644
--- a/lib/banzai/filter/repository_link_filter.rb
+++ b/lib/banzai/filter/repository_link_filter.rb
@@ -60,7 +60,7 @@ module Banzai
def get_uri_types(paths)
return {} if paths.empty?
- uri_types = Hash[paths.collect { |name| [name, nil] }]
+ uri_types = paths.to_h { |name| [name, nil] }
get_blob_types(paths).each do |name, type|
if type == :blob
diff --git a/lib/gitlab/ci/config/entry/product/variables.rb b/lib/gitlab/ci/config/entry/product/variables.rb
index aa34cfb3acc..e869e0bbb31 100644
--- a/lib/gitlab/ci/config/entry/product/variables.rb
+++ b/lib/gitlab/ci/config/entry/product/variables.rb
@@ -25,8 +25,7 @@ module Gitlab
def value
@config
- .map { |key, value| [key.to_s, Array(value).map(&:to_s)] }
- .to_h
+ .to_h { |key, value| [key.to_s, Array(value).map(&:to_s)] }
end
end
end
diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb
index dc164d752be..efb469ee32a 100644
--- a/lib/gitlab/ci/config/entry/variables.rb
+++ b/lib/gitlab/ci/config/entry/variables.rb
@@ -18,7 +18,7 @@ module Gitlab
end
def value
- Hash[@config.map { |key, value| [key.to_s, expand_value(value)[:value]] }]
+ @config.to_h { |key, value| [key.to_s, expand_value(value)[:value]] }
end
def self.default(**)
@@ -26,7 +26,7 @@ module Gitlab
end
def value_with_data
- Hash[@config.map { |key, value| [key.to_s, expand_value(value)] }]
+ @config.to_h { |key, value| [key.to_s, expand_value(value)] }
end
def use_value_data?
diff --git a/lib/gitlab/ci/variables/helpers.rb b/lib/gitlab/ci/variables/helpers.rb
index 2c3457e0265..3a62f01e2e3 100644
--- a/lib/gitlab/ci/variables/helpers.rb
+++ b/lib/gitlab/ci/variables/helpers.rb
@@ -23,7 +23,7 @@ module Gitlab
def transform_from_yaml_variables(vars)
return vars.stringify_keys if vars.is_a?(Hash)
- vars.to_a.map { |var| [var[:key].to_s, var[:value]] }.to_h
+ vars.to_a.to_h { |var| [var[:key].to_s, var[:value]] }
end
def inherit_yaml_variables(from:, to:, inheritance:)
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index ff99803d8de..51baed32935 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -390,7 +390,7 @@ module Gitlab
@committer_name = commit.committer.name.dup
@committer_email = commit.committer.email.dup
@parent_ids = Array(commit.parent_ids)
- @trailers = Hash[commit.trailers.map { |t| [t.key, t.value] }]
+ @trailers = commit.trailers.to_h { |t| [t.key, t.value] }
end
# Gitaly provides a UNIX timestamp in author.date.seconds, and a timezone
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 88753e80391..95c002edf0a 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -28,7 +28,7 @@ module Gitlab
prepend_if_ee('EE::Gitlab::ImportSources') # rubocop: disable Cop/InjectEnterpriseEditionModule
def options
- Hash[import_table.map { |importer| [importer.title, importer.name] }]
+ import_table.to_h { |importer| [importer.title, importer.name] }
end
def values
diff --git a/lib/gitlab/language_detection.rb b/lib/gitlab/language_detection.rb
index 7600e60b904..1e5edb79f10 100644
--- a/lib/gitlab/language_detection.rb
+++ b/lib/gitlab/language_detection.rb
@@ -20,7 +20,7 @@ module Gitlab
# Newly detected languages, returned in a structure accepted by
# Gitlab::Database.bulk_insert
def insertions(programming_languages)
- lang_to_id = programming_languages.map { |p| [p.name, p.id] }.to_h
+ lang_to_id = programming_languages.to_h { |p| [p.name, p.id] }
(languages - previous_language_names).map do |new_lang|
{
@@ -63,8 +63,7 @@ module Gitlab
@repository
.languages
.first(MAX_LANGUAGES)
- .map { |l| [l[:label], l] }
- .to_h
+ .to_h { |l| [l[:label], l] }
end
end
end
diff --git a/lib/gitlab/metrics/samplers/database_sampler.rb b/lib/gitlab/metrics/samplers/database_sampler.rb
index 60ae22df607..c0336a4d0fb 100644
--- a/lib/gitlab/metrics/samplers/database_sampler.rb
+++ b/lib/gitlab/metrics/samplers/database_sampler.rb
@@ -32,9 +32,9 @@ module Gitlab
private
def init_metrics
- METRIC_DESCRIPTIONS.map do |name, description|
+ METRIC_DESCRIPTIONS.to_h do |name, description|
[name, ::Gitlab::Metrics.gauge(:"#{METRIC_PREFIX}#{name}", description)]
- end.to_h
+ end
end
def host_stats
diff --git a/lib/gitlab/phabricator_import/project_creator.rb b/lib/gitlab/phabricator_import/project_creator.rb
index 5ace31386e0..c842798ca74 100644
--- a/lib/gitlab/phabricator_import/project_creator.rb
+++ b/lib/gitlab/phabricator_import/project_creator.rb
@@ -55,12 +55,13 @@ module Gitlab
end
def project_feature_attributes
- @project_features_attributes ||= begin
- # everything disabled except for issues
- ProjectFeature::FEATURES.map do |feature|
- [ProjectFeature.access_level_attribute(feature), ProjectFeature::DISABLED]
- end.to_h.merge(ProjectFeature.access_level_attribute(:issues) => ProjectFeature::ENABLED)
- end
+ @project_features_attributes ||=
+ begin
+ # everything disabled except for issues
+ ProjectFeature::FEATURES.to_h do |feature|
+ [ProjectFeature.access_level_attribute(feature), ProjectFeature::DISABLED]
+ end.merge(ProjectFeature.access_level_attribute(:issues) => ProjectFeature::ENABLED)
+ end
end
def import_data
diff --git a/lib/gitlab/prometheus_client.rb b/lib/gitlab/prometheus_client.rb
index 965349ad711..0fcf63d03fc 100644
--- a/lib/gitlab/prometheus_client.rb
+++ b/lib/gitlab/prometheus_client.rb
@@ -140,7 +140,7 @@ module Gitlab
end
def mapped_options
- options.keys.map { |k| [gitlab_http_key(k), options[k]] }.to_h
+ options.keys.to_h { |k| [gitlab_http_key(k), options[k]] }
end
def http_options
diff --git a/lib/gitlab/repository_hash_cache.rb b/lib/gitlab/repository_hash_cache.rb
index d479d3115a6..430f3e8d162 100644
--- a/lib/gitlab/repository_hash_cache.rb
+++ b/lib/gitlab/repository_hash_cache.rb
@@ -148,7 +148,7 @@ module Gitlab
# @param hash [Hash]
# @return [Hash] the stringified hash
def standardize_hash(hash)
- hash.map { |k, v| [k.to_s, v.to_s] }.to_h
+ hash.to_h { |k, v| [k.to_s, v.to_s] }
end
# Record metrics in Prometheus.
diff --git a/lib/gitlab/static_site_editor/config/file_config.rb b/lib/gitlab/static_site_editor/config/file_config.rb
index 315c603c1dd..4180f6ccf00 100644
--- a/lib/gitlab/static_site_editor/config/file_config.rb
+++ b/lib/gitlab/static_site_editor/config/file_config.rb
@@ -28,7 +28,7 @@ module Gitlab
def to_hash_with_defaults
# NOTE: The current approach of simply mapping all the descendents' keys and values ('config')
# into a flat hash may need to be enhanced as we add more complex, non-scalar entries.
- @global.descendants.map { |descendant| [descendant.key, descendant.config] }.to_h
+ @global.descendants.to_h { |descendant| [descendant.key, descendant.config] }
end
private
diff --git a/lib/gitlab/template/base_template.rb b/lib/gitlab/template/base_template.rb
index dc006877129..31e11f73fe7 100644
--- a/lib/gitlab/template/base_template.rb
+++ b/lib/gitlab/template/base_template.rb
@@ -130,10 +130,10 @@ module Gitlab
return [] if project && !project.repository.exists?
if categories.any?
- categories.keys.map do |category|
+ categories.keys.to_h do |category|
files = self.by_category(category, project)
[category, files.map { |t| { key: t.key, name: t.name, content: t.content } }]
- end.to_h
+ end
else
files = self.all(project)
files.map { |t| { key: t.key, name: t.name, content: t.content } }
diff --git a/lib/gitlab/usage_data_counters/base_counter.rb b/lib/gitlab/usage_data_counters/base_counter.rb
index d28fd17a989..4ab310a2519 100644
--- a/lib/gitlab/usage_data_counters/base_counter.rb
+++ b/lib/gitlab/usage_data_counters/base_counter.rb
@@ -22,11 +22,11 @@ module Gitlab::UsageDataCounters
end
def totals
- known_events.map { |event| [counter_key(event), read(event)] }.to_h
+ known_events.to_h { |event| [counter_key(event), read(event)] }
end
def fallback_totals
- known_events.map { |event| [counter_key(event), -1] }.to_h
+ known_events.to_h { |event| [counter_key(event), -1] }
end
def fetch_supported_event(event_name)
diff --git a/lib/gitlab/usage_data_counters/note_counter.rb b/lib/gitlab/usage_data_counters/note_counter.rb
index 7a76180cb08..aae2d144c5b 100644
--- a/lib/gitlab/usage_data_counters/note_counter.rb
+++ b/lib/gitlab/usage_data_counters/note_counter.rb
@@ -24,13 +24,13 @@ module Gitlab::UsageDataCounters
end
def totals
- COUNTABLE_TYPES.map do |countable_type|
+ COUNTABLE_TYPES.to_h do |countable_type|
[counter_key(countable_type), read(:create, countable_type)]
- end.to_h
+ end
end
def fallback_totals
- COUNTABLE_TYPES.map { |counter_key| [counter_key(counter_key), -1] }.to_h
+ COUNTABLE_TYPES.to_h { |counter_key| [counter_key(counter_key), -1] }
end
private
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index aa61aff3b05..80bf964e2ee 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -295,8 +295,8 @@ RSpec.describe 'Issues > Labels bulk assignment' do
before do
issue1.milestone = milestone
issue2.milestone = milestone
- issue1.save
- issue2.save
+ issue1.save!
+ issue2.save!
issue1.labels << bug
issue2.labels << feature
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index ca44978d223..bbf2a889880 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -171,7 +171,7 @@ RSpec.describe 'Issue Sidebar' do
context 'editing issue labels', :js do
before do
- issue.update(labels: [label])
+ issue.update!(labels: [label])
page.within('.block.labels') do
click_on 'Edit'
end
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index bc4c67fdd79..5e02d5ad038 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -103,7 +103,7 @@ RSpec.describe 'Issue notes polling', :js do
end
def update_note(note, new_text)
- note.update(note: new_text)
+ note.update!(note: new_text)
wait_for_requests
end
diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
index e225a45481d..6e8b3e4fb7c 100644
--- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
@@ -150,7 +150,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do
context 'when merge requests are disabled' do
before do
- project.project_feature.update(merge_requests_access_level: 0)
+ project.project_feature.update!(merge_requests_access_level: 0)
visit project_issue_path(project, issue)
end
diff --git a/spec/features/issues/user_creates_confidential_merge_request_spec.rb b/spec/features/issues/user_creates_confidential_merge_request_spec.rb
index ea96165d7b7..6b4526cd624 100644
--- a/spec/features/issues/user_creates_confidential_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_confidential_merge_request_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe 'User creates confidential merge request on issue page', :js do
let(:forked_project) { fork_project(project, user, repository: true) }
before do
- forked_project.update(visibility: Gitlab::VisibilityLevel::PRIVATE)
+ forked_project.update!(visibility: Gitlab::VisibilityLevel::PRIVATE)
visit_confidential_issue
end
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index b8e35c9afa3..4b0886ee85e 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -78,7 +78,7 @@ RSpec.describe "Issues > User edits issue", :js do
end
it 'warns about version conflict' do
- issue.update(title: "New title")
+ issue.update!(title: "New title")
fill_in 'issue_title', with: 'bug 345'
fill_in 'issue_description', with: 'bug description'
@@ -307,7 +307,7 @@ RSpec.describe "Issues > User edits issue", :js do
before do
project.add_guest(guest)
issue.milestone = milestone
- issue.save
+ issue.save!
end
it 'shows milestone text' do
diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb
index 1b246181523..5d05df6aaf0 100644
--- a/spec/features/issues/user_filters_issues_spec.rb
+++ b/spec/features/issues/user_filters_issues_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'User filters issues', :js do
@issue = Issue.find_by(title: 'foobar')
@issue.milestone = create(:milestone, project: project)
@issue.assignees = []
- @issue.save
+ @issue.save!
end
let(:issue) { @issue }
diff --git a/spec/features/issues/user_sees_live_update_spec.rb b/spec/features/issues/user_sees_live_update_spec.rb
index 79c6978cbc0..7e4880f209e 100644
--- a/spec/features/issues/user_sees_live_update_spec.rb
+++ b/spec/features/issues/user_sees_live_update_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'Issues > User sees live update', :js do
expect(page).to have_text("new title")
- issue.update(title: "updated title")
+ issue.update!(title: "updated title")
wait_for_requests
expect(page).to have_text("updated title")
diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb
index f0bb055c6f2..c161e1deb83 100644
--- a/spec/features/issues/user_sorts_issues_spec.rb
+++ b/spec/features/issues/user_sorts_issues_spec.rb
@@ -77,7 +77,7 @@ RSpec.describe "User sorts issues" do
it 'sorts by most recently updated', :js do
issue3.updated_at = Time.now + 100
- issue3.save
+ issue3.save!
visit project_issues_path(project, sort: sort_value_recently_updated)
expect(first_issue).to include('baz')
@@ -85,8 +85,8 @@ RSpec.describe "User sorts issues" do
describe 'sorting by due date', :js do
before do
- issue1.update(due_date: 1.day.from_now)
- issue2.update(due_date: 6.days.from_now)
+ issue1.update!(due_date: 1.day.from_now)
+ issue2.update!(due_date: 6.days.from_now)
end
it 'sorts by due date' do
@@ -96,7 +96,7 @@ RSpec.describe "User sorts issues" do
end
it 'sorts by due date by excluding nil due dates' do
- issue2.update(due_date: nil)
+ issue2.update!(due_date: nil)
visit project_issues_path(project, sort: sort_value_due_date)
@@ -111,7 +111,7 @@ RSpec.describe "User sorts issues" do
end
it 'sorts by least recently due date by excluding nil due dates' do
- issue2.update(due_date: nil)
+ issue2.update!(due_date: nil)
visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later)
@@ -122,8 +122,8 @@ RSpec.describe "User sorts issues" do
describe 'filtering by due date', :js do
before do
- issue1.update(due_date: 1.day.from_now)
- issue2.update(due_date: 6.days.from_now)
+ issue1.update!(due_date: 1.day.from_now)
+ issue2.update!(due_date: 6.days.from_now)
end
it 'filters by none' do
@@ -147,9 +147,9 @@ RSpec.describe "User sorts issues" do
end
it 'filters by due this week' do
- issue1.update(due_date: Date.today.beginning_of_week + 2.days)
- issue2.update(due_date: Date.today.end_of_week)
- issue3.update(due_date: Date.today - 8.days)
+ issue1.update!(due_date: Date.today.beginning_of_week + 2.days)
+ issue2.update!(due_date: Date.today.end_of_week)
+ issue3.update!(due_date: Date.today - 8.days)
visit project_issues_path(project, due_date: Issue::DueThisWeek.name)
@@ -161,9 +161,9 @@ RSpec.describe "User sorts issues" do
end
it 'filters by due this month' do
- issue1.update(due_date: Date.today.beginning_of_month + 2.days)
- issue2.update(due_date: Date.today.end_of_month)
- issue3.update(due_date: Date.today - 50.days)
+ issue1.update!(due_date: Date.today.beginning_of_month + 2.days)
+ issue2.update!(due_date: Date.today.end_of_month)
+ issue3.update!(due_date: Date.today - 50.days)
visit project_issues_path(project, due_date: Issue::DueThisMonth.name)
@@ -175,9 +175,9 @@ RSpec.describe "User sorts issues" do
end
it 'filters by overdue' do
- issue1.update(due_date: Date.today + 2.days)
- issue2.update(due_date: Date.today + 20.days)
- issue3.update(due_date: Date.yesterday)
+ issue1.update!(due_date: Date.today + 2.days)
+ issue2.update!(due_date: Date.today + 20.days)
+ issue3.update!(due_date: Date.yesterday)
visit project_issues_path(project, due_date: Issue::Overdue.name)
@@ -189,9 +189,9 @@ RSpec.describe "User sorts issues" do
end
it 'filters by due next month and previous two weeks' do
- issue1.update(due_date: Date.today - 4.weeks)
- issue2.update(due_date: (Date.today + 2.months).beginning_of_month)
- issue3.update(due_date: Date.yesterday)
+ issue1.update!(due_date: Date.today - 4.weeks)
+ issue2.update!(due_date: (Date.today + 2.months).beginning_of_month)
+ issue3.update!(due_date: Date.yesterday)
visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name)
@@ -206,9 +206,9 @@ RSpec.describe "User sorts issues" do
describe 'sorting by milestone', :js do
before do
issue1.milestone = newer_due_milestone
- issue1.save
+ issue1.save!
issue2.milestone = later_due_milestone
- issue2.save
+ issue2.save!
end
it 'sorts by milestone' do
@@ -224,9 +224,9 @@ RSpec.describe "User sorts issues" do
before do
issue1.assignees << user2
- issue1.save
+ issue1.save!
issue2.assignees << user2
- issue2.save
+ issue2.save!
end
it 'sorts with a filter applied' do
diff --git a/spec/frontend/__helpers__/vue_test_utils_helper.js b/spec/frontend/__helpers__/vue_test_utils_helper.js
index d6132ef84ac..a94cee84f74 100644
--- a/spec/frontend/__helpers__/vue_test_utils_helper.js
+++ b/spec/frontend/__helpers__/vue_test_utils_helper.js
@@ -1,4 +1,6 @@
-import { isArray } from 'lodash';
+import * as testingLibrary from '@testing-library/dom';
+import { createWrapper, WrapperArray, mount, shallowMount } from '@vue/test-utils';
+import { isArray, upperFirst } from 'lodash';
const vNodeContainsText = (vnode, text) =>
(vnode.text && vnode.text.includes(text)) ||
@@ -37,6 +39,17 @@ export const waitForMutation = (store, expectedMutationType) =>
});
export const extendedWrapper = (wrapper) => {
+ // https://testing-library.com/docs/queries/about
+ const AVAILABLE_QUERIES = [
+ 'byRole',
+ 'byLabelText',
+ 'byPlaceholderText',
+ 'byText',
+ 'byDisplayValue',
+ 'byAltText',
+ 'byTitle',
+ ];
+
if (isArray(wrapper) || !wrapper?.find) {
// eslint-disable-next-line no-console
console.warn(
@@ -56,5 +69,63 @@ export const extendedWrapper = (wrapper) => {
return this.findAll(`[data-testid="${id}"]`);
},
},
+ // `findBy`
+ ...AVAILABLE_QUERIES.reduce((accumulator, query) => {
+ return {
+ ...accumulator,
+ [`find${upperFirst(query)}`]: {
+ value(text, options = {}) {
+ const elements = testingLibrary[`queryAll${upperFirst(query)}`](
+ wrapper.element,
+ text,
+ options,
+ );
+
+ // Return VTU `ErrorWrapper` if element is not found
+ // https://github.com/vuejs/vue-test-utils/blob/dev/packages/test-utils/src/error-wrapper.js
+ // VTU does not expose `ErrorWrapper` so, as of now, this is the best way to
+ // create an `ErrorWrapper`
+ if (!elements.length) {
+ const emptyElement = document.createElement('div');
+
+ return createWrapper(emptyElement).find('testing-library-element-not-found');
+ }
+
+ return createWrapper(elements[0], this.options || {});
+ },
+ },
+ };
+ }, {}),
+ // `findAllBy`
+ ...AVAILABLE_QUERIES.reduce((accumulator, query) => {
+ return {
+ ...accumulator,
+ [`findAll${upperFirst(query)}`]: {
+ value(text, options = {}) {
+ const elements = testingLibrary[`queryAll${upperFirst(query)}`](
+ wrapper.element,
+ text,
+ options,
+ );
+
+ const wrappers = elements.map((element) => {
+ const elementWrapper = createWrapper(element, this.options || {});
+ elementWrapper.selector = text;
+
+ return elementWrapper;
+ });
+
+ const wrapperArray = new WrapperArray(wrappers);
+ wrapperArray.selector = text;
+
+ return wrapperArray;
+ },
+ },
+ };
+ }, {}),
});
};
+
+export const shallowMountExtended = (...args) => extendedWrapper(shallowMount(...args));
+
+export const mountExtended = (...args) => extendedWrapper(mount(...args));
diff --git a/spec/frontend/__helpers__/vue_test_utils_helper_spec.js b/spec/frontend/__helpers__/vue_test_utils_helper_spec.js
index d4f8e36c169..dfe5a483223 100644
--- a/spec/frontend/__helpers__/vue_test_utils_helper_spec.js
+++ b/spec/frontend/__helpers__/vue_test_utils_helper_spec.js
@@ -1,7 +1,27 @@
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper, shallowWrapperContainsSlotText } from './vue_test_utils_helper';
+import * as testingLibrary from '@testing-library/dom';
+import * as vtu from '@vue/test-utils';
+import {
+ shallowMount,
+ Wrapper as VTUWrapper,
+ WrapperArray as VTUWrapperArray,
+} from '@vue/test-utils';
+import {
+ extendedWrapper,
+ shallowMountExtended,
+ mountExtended,
+ shallowWrapperContainsSlotText,
+} from './vue_test_utils_helper';
+
+jest.mock('@testing-library/dom', () => ({
+ __esModule: true,
+ ...jest.requireActual('@testing-library/dom'),
+}));
describe('Vue test utils helpers', () => {
+ afterAll(() => {
+ jest.unmock('@testing-library/dom');
+ });
+
describe('shallowWrapperContainsSlotText', () => {
const mockText = 'text';
const mockSlot = `<div>${mockText}</div>`;
@@ -84,7 +104,7 @@ describe('Vue test utils helpers', () => {
);
});
- it('should find the component by test id', () => {
+ it('should find the element by test id', () => {
expect(mockComponent.findByTestId(testId).exists()).toBe(true);
});
});
@@ -105,5 +125,187 @@ describe('Vue test utils helpers', () => {
expect(mockComponent.findAllByTestId(testId)).toHaveLength(2);
});
});
+
+ describe.each`
+ findMethod | expectedQuery
+ ${'findByRole'} | ${'queryAllByRole'}
+ ${'findByLabelText'} | ${'queryAllByLabelText'}
+ ${'findByPlaceholderText'} | ${'queryAllByPlaceholderText'}
+ ${'findByText'} | ${'queryAllByText'}
+ ${'findByDisplayValue'} | ${'queryAllByDisplayValue'}
+ ${'findByAltText'} | ${'queryAllByAltText'}
+ `('$findMethod', ({ findMethod, expectedQuery }) => {
+ const text = 'foo bar';
+ const options = { selector: 'div' };
+ const mockDiv = document.createElement('div');
+
+ let wrapper;
+ beforeEach(() => {
+ wrapper = extendedWrapper(
+ shallowMount({
+ template: `<div>foo bar</div>`,
+ }),
+ );
+ });
+
+ it(`calls Testing Library \`${expectedQuery}\` function with correct parameters`, () => {
+ jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv]);
+
+ wrapper[findMethod](text, options);
+
+ expect(testingLibrary[expectedQuery]).toHaveBeenLastCalledWith(
+ wrapper.element,
+ text,
+ options,
+ );
+ });
+
+ describe('when element is found', () => {
+ beforeEach(() => {
+ jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv]);
+ jest.spyOn(vtu, 'createWrapper');
+ });
+
+ it('returns a VTU wrapper', () => {
+ const result = wrapper[findMethod](text, options);
+
+ expect(vtu.createWrapper).toHaveBeenCalledWith(mockDiv, wrapper.options);
+ expect(result).toBeInstanceOf(VTUWrapper);
+ });
+ });
+
+ describe('when multiple elements are found', () => {
+ beforeEach(() => {
+ const mockSpan = document.createElement('span');
+ jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => [mockDiv, mockSpan]);
+ jest.spyOn(vtu, 'createWrapper');
+ });
+
+ it('returns the first element as a VTU wrapper', () => {
+ const result = wrapper[findMethod](text, options);
+
+ expect(vtu.createWrapper).toHaveBeenCalledWith(mockDiv, wrapper.options);
+ expect(result).toBeInstanceOf(VTUWrapper);
+ });
+ });
+
+ describe('when element is not found', () => {
+ beforeEach(() => {
+ jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => []);
+ });
+
+ it('returns a VTU error wrapper', () => {
+ expect(wrapper[findMethod](text, options).exists()).toBe(false);
+ });
+ });
+ });
+
+ describe.each`
+ findMethod | expectedQuery
+ ${'findAllByRole'} | ${'queryAllByRole'}
+ ${'findAllByLabelText'} | ${'queryAllByLabelText'}
+ ${'findAllByPlaceholderText'} | ${'queryAllByPlaceholderText'}
+ ${'findAllByText'} | ${'queryAllByText'}
+ ${'findAllByDisplayValue'} | ${'queryAllByDisplayValue'}
+ ${'findAllByAltText'} | ${'queryAllByAltText'}
+ `('$findMethod', ({ findMethod, expectedQuery }) => {
+ const text = 'foo bar';
+ const options = { selector: 'div' };
+ const mockElements = [
+ document.createElement('li'),
+ document.createElement('li'),
+ document.createElement('li'),
+ ];
+
+ let wrapper;
+ beforeEach(() => {
+ wrapper = extendedWrapper(
+ shallowMount({
+ template: `
+ <ul>
+ <li>foo</li>
+ <li>bar</li>
+ <li>baz</li>
+ </ul>
+ `,
+ }),
+ );
+ });
+
+ it(`calls Testing Library \`${expectedQuery}\` function with correct parameters`, () => {
+ jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => mockElements);
+
+ wrapper[findMethod](text, options);
+
+ expect(testingLibrary[expectedQuery]).toHaveBeenLastCalledWith(
+ wrapper.element,
+ text,
+ options,
+ );
+ });
+
+ describe('when elements are found', () => {
+ beforeEach(() => {
+ jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => mockElements);
+ });
+
+ it('returns a VTU wrapper array', () => {
+ const result = wrapper[findMethod](text, options);
+
+ expect(result).toBeInstanceOf(VTUWrapperArray);
+ expect(
+ result.wrappers.every(
+ (resultWrapper) =>
+ resultWrapper instanceof VTUWrapper && resultWrapper.options === wrapper.options,
+ ),
+ ).toBe(true);
+ expect(result.length).toBe(3);
+ });
+ });
+
+ describe('when elements are not found', () => {
+ beforeEach(() => {
+ jest.spyOn(testingLibrary, expectedQuery).mockImplementation(() => []);
+ });
+
+ it('returns an empty VTU wrapper array', () => {
+ const result = wrapper[findMethod](text, options);
+
+ expect(result).toBeInstanceOf(VTUWrapperArray);
+ expect(result.length).toBe(0);
+ });
+ });
+ });
+ });
+
+ describe.each`
+ mountExtendedFunction | expectedMountFunction
+ ${shallowMountExtended} | ${'shallowMount'}
+ ${mountExtended} | ${'mount'}
+ `('$mountExtendedFunction', ({ mountExtendedFunction, expectedMountFunction }) => {
+ const FakeComponent = jest.fn();
+ const options = {
+ propsData: {
+ foo: 'bar',
+ },
+ };
+
+ beforeEach(() => {
+ const mockWrapper = { find: jest.fn() };
+ jest.spyOn(vtu, expectedMountFunction).mockImplementation(() => mockWrapper);
+ });
+
+ it(`calls \`${expectedMountFunction}\` with passed arguments`, () => {
+ mountExtendedFunction(FakeComponent, options);
+
+ expect(vtu[expectedMountFunction]).toHaveBeenCalledWith(FakeComponent, options);
+ });
+
+ it('returns extended wrapper', () => {
+ const result = mountExtendedFunction(FakeComponent, options);
+
+ expect(result).toHaveProperty('find');
+ expect(result).toHaveProperty('findByTestId');
+ });
});
});
diff --git a/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js
index f86237dc160..f1471f625f8 100644
--- a/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js
+++ b/spec/frontend/members/components/action_buttons/access_request_action_buttons_spec.js
@@ -42,6 +42,7 @@ describe('AccessRequestActionButtons', () => {
memberId: member.id,
title: 'Deny access',
isAccessRequest: true,
+ isInvite: false,
icon: 'close',
});
});
diff --git a/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js
index 55866f90baa..e7a99a96da6 100644
--- a/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js
+++ b/spec/frontend/members/components/action_buttons/invite_action_buttons_spec.js
@@ -43,6 +43,7 @@ describe('InviteActionButtons', () => {
message: `Are you sure you want to revoke the invitation for ${member.invite.email} to join "${member.source.fullName}"`,
title: 'Revoke invite',
isAccessRequest: false,
+ isInvite: true,
icon: 'remove',
});
});
diff --git a/spec/frontend/members/components/action_buttons/remove_member_button_spec.js b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js
index 0d66f343fda..952ce679a2f 100644
--- a/spec/frontend/members/components/action_buttons/remove_member_button_spec.js
+++ b/spec/frontend/members/components/action_buttons/remove_member_button_spec.js
@@ -28,6 +28,7 @@ describe('RemoveMemberButton', () => {
message: 'Are you sure you want to remove John Smith?',
title: 'Remove member',
isAccessRequest: true,
+ isInvite: true,
...propsData,
},
directives: {
@@ -48,6 +49,7 @@ describe('RemoveMemberButton', () => {
'data-member-type': 'GroupMember',
'data-message': 'Are you sure you want to remove John Smith?',
'data-is-access-request': 'true',
+ 'data-is-invite': 'true',
'aria-label': 'Remove member',
title: 'Remove member',
icon: 'remove',
diff --git a/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js
index f43779b8970..2f13c436fd9 100644
--- a/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js
+++ b/spec/frontend/members/components/action_buttons/user_action_buttons_spec.js
@@ -43,6 +43,7 @@ describe('UserActionButtons', () => {
message: `Are you sure you want to remove ${member.user.name} from "${member.source.fullName}"`,
title: 'Remove member',
isAccessRequest: false,
+ isInvite: false,
icon: 'remove',
});
});
diff --git a/spec/frontend/vue_shared/components/remove_member_modal_spec.js b/spec/frontend/vue_shared/components/remove_member_modal_spec.js
index 1ef3cc348bd..7a04de13615 100644
--- a/spec/frontend/vue_shared/components/remove_member_modal_spec.js
+++ b/spec/frontend/vue_shared/components/remove_member_modal_spec.js
@@ -15,16 +15,18 @@ describe('RemoveMemberModal', () => {
});
describe.each`
- state | memberType | isAccessRequest | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message
- ${'removing a group member'} | ${'GroupMember'} | ${'false'} | ${'Remove member'} | ${true} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
- ${'removing a project member'} | ${'ProjectMember'} | ${'false'} | ${'Remove member'} | ${false} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
- ${'denying an access request'} | ${'ProjectMember'} | ${'true'} | ${'Deny access request'} | ${false} | ${false} | ${"Are you sure you want to deny Jane Doe's request to join the Gitlab Org / Gitlab Test project?"}
+ state | memberType | isAccessRequest | isInvite | actionText | removeSubMembershipsCheckboxExpected | unassignIssuablesCheckboxExpected | message
+ ${'removing a group member'} | ${'GroupMember'} | ${'false'} | ${'false'} | ${'Remove member'} | ${true} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
+ ${'removing a project member'} | ${'ProjectMember'} | ${'false'} | ${'false'} | ${'Remove member'} | ${false} | ${true} | ${'Are you sure you want to remove Jane Doe from the Gitlab Org / Gitlab Test project?'}
+ ${'denying an access request'} | ${'ProjectMember'} | ${'true'} | ${'false'} | ${'Deny access request'} | ${false} | ${false} | ${"Are you sure you want to deny Jane Doe's request to join the Gitlab Org / Gitlab Test project?"}
+ ${'revoking invite'} | ${'ProjectMember'} | ${'false'} | ${'true'} | ${'Revoke invite'} | ${false} | ${false} | ${'Are you sure you want to revoke the invitation for foo@bar.com to join the Gitlab Org / Gitlab Test project?'}
`(
'when $state',
({
actionText,
memberType,
isAccessRequest,
+ isInvite,
message,
removeSubMembershipsCheckboxExpected,
unassignIssuablesCheckboxExpected,
@@ -35,6 +37,7 @@ describe('RemoveMemberModal', () => {
return {
modalData: {
isAccessRequest,
+ isInvite,
message,
memberPath,
memberType,
diff --git a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
index c44f6b623d7..1b69bf7f63a 100644
--- a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe Resolvers::Ci::JobsResolver do
create(:ci_build, :sast, name: 'DAST job', pipeline: pipeline)
create(:ci_build, :dast, name: 'SAST job', pipeline: pipeline)
create(:ci_build, :container_scanning, name: 'Container scanning job', pipeline: pipeline)
+ create(:ci_build, name: 'Job with tags', pipeline: pipeline, tag_list: ['review'])
end
describe '#resolve' do
@@ -24,7 +25,8 @@ RSpec.describe Resolvers::Ci::JobsResolver do
have_attributes(name: 'Normal job'),
have_attributes(name: 'DAST job'),
have_attributes(name: 'SAST job'),
- have_attributes(name: 'Container scanning job')
+ have_attributes(name: 'Container scanning job'),
+ have_attributes(name: 'Job with tags')
)
end
end
@@ -43,5 +45,18 @@ RSpec.describe Resolvers::Ci::JobsResolver do
)
end
end
+
+ context 'when a job has tags' do
+ it "returns jobs with tags when applicable" do
+ jobs = resolve(described_class, obj: pipeline)
+ expect(jobs).to contain_exactly(
+ have_attributes(tag_list: []),
+ have_attributes(tag_list: []),
+ have_attributes(tag_list: []),
+ have_attributes(tag_list: []),
+ have_attributes(tag_list: ['review'])
+ )
+ end
+ end
end
end
diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb
index 75a178bc8f6..787e2174070 100644
--- a/spec/graphql/types/ci/job_type_spec.rb
+++ b/spec/graphql/types/ci/job_type_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe Types::Ci::JobType do
stage
started_at
status
+ tags
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
index a8fcb714c64..993afd47a57 100644
--- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
+++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
@@ -26,6 +26,7 @@ RSpec.describe BatchDestroyDependentAssociations do
let_it_be(:project) { create(:project) }
let_it_be(:build) { create(:ci_build, project: project) }
let_it_be(:notification_setting) { create(:notification_setting, project: project) }
+
let!(:todos) { create(:todo, project: project) }
it 'destroys multiple builds' do
diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb
index b550d22f686..bcff5ce383e 100644
--- a/spec/models/concerns/featurable_spec.rb
+++ b/spec/models/concerns/featurable_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Featurable do
let_it_be(:user) { create(:user) }
+
let(:project) { create(:project) }
let(:feature_class) { subject.class }
let(:features) { feature_class::FEATURES }
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 10fa15d468f..ac125e81acd 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -14,8 +14,8 @@ RSpec.describe API::CommitStatuses do
let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
context 'ci commit exists' do
- let!(:master) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', protected: false) }
- let!(:develop) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'develop', protected: false) }
+ let!(:master) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', protected: false) }
+ let!(:develop) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'develop', protected: false) }
context "reporter user" do
let(:statuses_id) { json_response.map { |status| status['id'] } }
@@ -270,8 +270,8 @@ RSpec.describe API::CommitStatuses do
end
context 'when a pipeline id is specified' do
- let!(:first_pipeline) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', status: 'created') }
- let!(:other_pipeline) { project.ci_pipelines.create(source: :push, sha: commit.id, ref: 'master', status: 'created') }
+ let!(:first_pipeline) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', status: 'created') }
+ let!(:other_pipeline) { project.ci_pipelines.create!(source: :push, sha: commit.id, ref: 'master', status: 'created') }
subject do
post api(post_url, developer), params: {
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index d6a0423e83c..c89c59a2151 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -345,7 +345,7 @@ RSpec.describe API::Deployments do
context 'as a maintainer' do
it 'returns a 403 when updating a deployment with a build' do
- deploy.update(deployable: build)
+ deploy.update!(deployable: build)
put(
api("/projects/#{project.id}/deployments/#{deploy.id}", user),
@@ -394,7 +394,7 @@ RSpec.describe API::Deployments do
end
it 'returns a 403 when updating a deployment with a build' do
- deploy.update(deployable: build)
+ deploy.update!(deployable: build)
put(
api("/projects/#{project.id}/deployments/#{deploy.id}", developer),
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index 303e510883d..aa1a4643593 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -214,7 +214,7 @@ RSpec.describe API::Environments do
context 'as a maintainer' do
context 'with a stoppable environment' do
before do
- environment.update(state: :available)
+ environment.update!(state: :available)
post api("/projects/#{project.id}/environments/#{environment.id}/stop", user)
end
diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb
index d45e24241b2..e678b6cf1c8 100644
--- a/spec/requests/api/go_proxy_spec.rb
+++ b/spec/requests/api/go_proxy_spec.rb
@@ -363,7 +363,7 @@ RSpec.describe API::GoProxy do
let(:module_name) { base }
before do
- project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
end
describe 'GET /projects/:id/packages/go/*module_name/@v/list' do
@@ -412,7 +412,7 @@ RSpec.describe API::GoProxy do
let(:module_name) { base }
before do
- project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
describe 'GET /projects/:id/packages/go/*module_name/@v/list' do
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb
index 34d347c76fd..0d0cc66c52a 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe 'Setting labels of a merge request' do
end
it 'sets the merge request labels, removing existing ones' do
- merge_request.update(labels: [label2])
+ merge_request.update!(labels: [label2])
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/requests/api/group_import_spec.rb b/spec/requests/api/group_import_spec.rb
index bb7436502ed..f632e49bf3a 100644
--- a/spec/requests/api/group_import_spec.rb
+++ b/spec/requests/api/group_import_spec.rb
@@ -218,12 +218,14 @@ RSpec.describe API::GroupImport do
stub_uploads_object_storage(ImportExportUploader, direct_upload: true)
end
+ # rubocop:disable Rails/SaveBang
let(:tmp_object) do
fog_connection.directories.new(key: 'uploads').files.create(
key: "tmp/uploads/#{file_name}",
body: file_upload
)
end
+ # rubocop:enable Rails/SaveBang
let(:fog_file) { fog_to_uploaded_file(tmp_object) }
let(:params) do
diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb
index 7d387079b3a..e3e0164e5a7 100644
--- a/spec/requests/api/group_milestones_spec.rb
+++ b/spec/requests/api/group_milestones_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe API::GroupMilestones do
let_it_be(:params) { { include_parent_milestones: true } }
before_all do
- group.update(parent: ancestor_group)
+ group.update!(parent: ancestor_group)
end
shared_examples 'listing all milestones' do
@@ -83,9 +83,9 @@ RSpec.describe API::GroupMilestones do
end
def setup_for_group
- context_group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ context_group.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
context_group.add_developer(user)
- public_project.update(namespace: context_group)
+ public_project.update!(namespace: context_group)
context_group.reload
end
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 8b060fa2217..6bedd43e5c4 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -887,7 +887,7 @@ RSpec.describe API::Internal::Base do
context 'project does not exist' do
context 'git pull' do
it 'returns a 200 response with status: false' do
- project.destroy
+ project.destroy!
pull(key, project)
diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb
index 3870c78deee..cebde747210 100644
--- a/spec/requests/api/issues/get_group_issues_spec.rb
+++ b/spec/requests/api/issues/get_group_issues_spec.rb
@@ -754,7 +754,7 @@ RSpec.describe API::Issues do
let(:parent_group) { create(:group) }
before do
- group.update(parent_id: parent_group.id)
+ group.update!(parent_id: parent_group.id)
group_closed_issue.reload
end
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index 5b3e2363669..7f1db620d4f 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe API::Issues do
let(:not_member) { create(:user) }
before do
- project.project_feature.update(issues_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
end
it 'renders 403' do
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index fe00b654d3b..4f73917763f 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -215,7 +215,7 @@ RSpec.describe API::Jobs do
first_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline)
first_build.runner = create(:ci_runner)
first_build.user = create(:user)
- first_build.save
+ first_build.save!
control_count = ActiveRecord::QueryRecorder.new { go }.count
@@ -223,7 +223,7 @@ RSpec.describe API::Jobs do
second_build = create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: second_pipeline)
second_build.runner = create(:ci_runner)
second_build.user = create(:user)
- second_build.save
+ second_build.save!
expect { go }.not_to exceed_query_limit(control_count)
end
@@ -684,7 +684,7 @@ RSpec.describe API::Jobs do
context 'with regular branch' do
before do
pipeline.reload
- pipeline.update(ref: 'master',
+ pipeline.update!(ref: 'master',
sha: project.commit('master').sha)
get_for_ref('master')
@@ -696,7 +696,7 @@ RSpec.describe API::Jobs do
context 'with branch name containing slash' do
before do
pipeline.reload
- pipeline.update(ref: 'improve/awesome',
+ pipeline.update!(ref: 'improve/awesome',
sha: project.commit('improve/awesome').sha)
end
@@ -732,7 +732,7 @@ RSpec.describe API::Jobs do
stub_artifacts_object_storage
job.success
- project.update(visibility_level: visibility_level,
+ project.update!(visibility_level: visibility_level,
public_builds: public_builds)
get_artifact_file(artifact)
@@ -826,7 +826,7 @@ RSpec.describe API::Jobs do
context 'with branch name containing slash' do
before do
pipeline.reload
- pipeline.update(ref: 'improve/awesome',
+ pipeline.update!(ref: 'improve/awesome',
sha: project.commit('improve/awesome').sha)
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index e3fffd3e3fd..26377c40b73 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -119,7 +119,7 @@ RSpec.describe API::Labels do
expect(label).not_to be_nil
- label.priorities.create(project: label.project, priority: 1)
+ label.priorities.create!(project: label.project, priority: 1)
label.save!
request_params = {
@@ -139,7 +139,7 @@ RSpec.describe API::Labels do
expect(label).not_to be_nil
label_id = spec_params[:name] || spec_params[:label_id]
- label.priorities.create(project: label.project, priority: 1)
+ label.priorities.create!(project: label.project, priority: 1)
label.save!
request_params = {
@@ -383,7 +383,7 @@ RSpec.describe API::Labels do
it 'returns 409 if label already exists in group' do
group = create(:group)
group_label = create(:group_label, group: group)
- project.update(group: group)
+ project.update!(group: group)
post api("/projects/#{project.id}/labels", user),
params: {
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index a049d7d7515..f6cdf370e5c 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -235,12 +235,14 @@ RSpec.describe API::ProjectImport do
stub_uploads_object_storage(ImportExportUploader, direct_upload: true)
end
+ # rubocop:disable Rails/SaveBang
let(:tmp_object) do
fog_connection.directories.new(key: 'uploads').files.create(
key: "tmp/uploads/#{file_name}",
body: fixture_file_upload(file)
)
end
+ # rubocop:enable Rails/SaveBang
let(:file_upload) { fog_to_uploaded_file(tmp_object) }
@@ -285,7 +287,7 @@ RSpec.describe API::ProjectImport do
it 'returns the import status and the error if failed' do
project = create(:project, :import_failed)
project.add_maintainer(user)
- project.import_state.update(last_error: 'error')
+ project.import_state.update!(last_error: 'error')
get api("/projects/#{project.id}/import", user)