diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-21 18:09:44 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-21 18:09:44 +0300 |
commit | b85aae44f96c152c62952f4d3051223cdf0692be (patch) | |
tree | 8ce4385b899e0f55b58368c8d5d15b430a8af205 | |
parent | 79850719759d6fe1b0682fd27573d479c9013f03 (diff) |
Add latest changes from gitlab-org/gitlab@master
75 files changed, 686 insertions, 227 deletions
diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js index e0d012cef23..77c85d85e27 100644 --- a/app/assets/javascripts/commons/index.js +++ b/app/assets/javascripts/commons/index.js @@ -1,5 +1,4 @@ import './polyfills'; -import './jquery'; import './bootstrap'; import './vue'; import '../lib/utils/axios_utils'; diff --git a/app/assets/javascripts/commons/jquery.js b/app/assets/javascripts/commons/jquery.js deleted file mode 100644 index 334f95bb27f..00000000000 --- a/app/assets/javascripts/commons/jquery.js +++ /dev/null @@ -1,4 +0,0 @@ -import 'jquery'; - -// common jQuery plugins -import 'jquery-ujs'; diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index 262d501bfba..7321e4d18cc 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import { Rails } from '~/lib/utils/rails_ujs'; import { rstrip } from './lib/utils/common_utils'; function openConfirmDangerModal($form, $modal, text) { @@ -21,9 +22,16 @@ function openConfirmDangerModal($form, $modal, text) { $submit.disable(); } }); + $('.js-confirm-danger-submit', $modal) .off('click') - .on('click', () => $form.submit()); + .on('click', () => { + if ($form.data('remote')) { + Rails.fire($form[0], 'submit'); + } else { + $form.submit(); + } + }); } function getModal($btn) { diff --git a/app/assets/javascripts/lib/utils/csrf.js b/app/assets/javascripts/lib/utils/csrf.js index ca9828c4682..3114a2a0dfb 100644 --- a/app/assets/javascripts/lib/utils/csrf.js +++ b/app/assets/javascripts/lib/utils/csrf.js @@ -1,5 +1,3 @@ -import $ from 'jquery'; - /* This module provides easy access to the CSRF token and caches it for re-use. It also exposes some values commonly used in relation @@ -20,7 +18,6 @@ If you need to compose a headers object, use the spread operator: see also http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf and https://github.com/rails/jquery-rails/blob/v4.3.1/vendor/assets/javascripts/jquery_ujs.js#L59-L62 */ - const csrf = { init() { const tokenEl = document.querySelector('meta[name=csrf-token]'); @@ -52,9 +49,4 @@ const csrf = { csrf.init(); -// use our cached token for any $.rails-generated AJAX requests -if ($.rails) { - $.rails.csrfToken = () => csrf.token; -} - export default csrf; diff --git a/app/assets/javascripts/lib/utils/rails_ujs.js b/app/assets/javascripts/lib/utils/rails_ujs.js new file mode 100644 index 00000000000..8b40cc7bd11 --- /dev/null +++ b/app/assets/javascripts/lib/utils/rails_ujs.js @@ -0,0 +1,20 @@ +import Rails from '@rails/ujs'; + +export const initRails = () => { + // eslint-disable-next-line no-underscore-dangle + if (!window._rails_loaded) { + Rails.start(); + + // Count XHR requests for tests. See spec/support/helpers/wait_for_requests.rb + window.pendingRailsUJSRequests = 0; + document.body.addEventListener('ajax:complete', () => { + window.pendingRailsUJSRequests -= 1; + }); + + document.body.addEventListener('ajax:beforeSend', () => { + window.pendingRailsUJSRequests += 1; + }); + } +}; + +export { Rails }; diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 9fcf881a1ac..f4882f3994f 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -11,6 +11,7 @@ import './behaviors'; // lib/utils import applyGitLabUIConfig from '@gitlab/ui/dist/config'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; +import { initRails } from '~/lib/utils/rails_ujs'; import { handleLocationHash, addSelectOnFocusBehaviour, @@ -169,6 +170,8 @@ function deferredInitialisation() { // Adding a helper class to activate animations only after all is rendered setTimeout(() => $body.addClass('page-initialised'), 1000); + + initRails(); } document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js index c3fbb5d6acf..68fca706ce5 100644 --- a/app/assets/javascripts/members.js +++ b/app/assets/javascripts/members.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import { Rails } from '~/lib/utils/rails_ujs'; import { disableButtonIfEmptyField } from '~/lib/utils/common_utils'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; @@ -54,8 +55,9 @@ export default class Members { formSubmit(e, $el = null) { const $this = e ? $(e.currentTarget) : $el; const { $toggle, $dateInput } = this.getMemberListItems($this); + const formEl = $this.closest('form').get(0); - $this.closest('form').trigger('submit.rails'); + Rails.fire(formEl, 'submit'); $toggle.disable(); $dateInput.disable(); diff --git a/app/assets/javascripts/monitoring/components/group_empty_state.vue b/app/assets/javascripts/monitoring/components/group_empty_state.vue index 499823fae3f..0365fc66331 100644 --- a/app/assets/javascripts/monitoring/components/group_empty_state.vue +++ b/app/assets/javascripts/monitoring/components/group_empty_state.vue @@ -1,6 +1,5 @@ <script> -/* eslint-disable vue/no-v-html */ -import { GlEmptyState } from '@gitlab/ui'; +import { GlEmptyState, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import { metricStates } from '../constants'; @@ -8,6 +7,9 @@ export default { components: { GlEmptyState, }, + directives: { + SafeHtml, + }, props: { documentationPath: { type: String, @@ -100,7 +102,7 @@ export default { :compact="true" > <template v-if="currentState.slottedDescription" #description> - <div v-html="currentState.slottedDescription"></div> + <div v-safe-html="currentState.slottedDescription"></div> </template> </gl-empty-state> </template> diff --git a/app/assets/javascripts/namespaces/leave_by_url.js b/app/assets/javascripts/namespaces/leave_by_url.js index bf77617d516..7b15253d872 100644 --- a/app/assets/javascripts/namespaces/leave_by_url.js +++ b/app/assets/javascripts/namespaces/leave_by_url.js @@ -1,3 +1,4 @@ +import { initRails } from '~/lib/utils/rails_ujs'; import { deprecatedCreateFlash as Flash } from '~/flash'; import { __, sprintf } from '~/locale'; import { getParameterByName } from '~/lib/utils/common_utils'; @@ -11,6 +12,8 @@ export default function leaveByUrl(namespaceType) { const param = getParameterByName(PARAMETER_NAME); if (!param) return; + initRails(); + const leaveLink = document.querySelector(LEAVE_LINK_SELECTOR); if (leaveLink) { leaveLink.click(); diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index 314fa762768..65b89b94eaa 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -45,7 +45,7 @@ export default { }, }, computed: { - ...mapGetters(['getDiscussion']), + ...mapGetters(['getDiscussion', 'suggestionsCount']), discussion() { if (!this.note.isDraft) return {}; @@ -125,6 +125,7 @@ export default { <suggestions v-if="hasSuggestion && !isEditing" :suggestions="note.suggestions" + :suggestions-count="suggestionsCount" :batch-suggestions-info="batchSuggestionsInfo" :note-html="note.note_html" :line-type="lineType" diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js index 7d60fbffb10..fbdb71925ea 100644 --- a/app/assets/javascripts/notes/stores/getters.js +++ b/app/assets/javascripts/notes/stores/getters.js @@ -231,3 +231,6 @@ export const getDiscussion = state => discussionId => state.discussions.find(discussion => discussion.id === discussionId); export const commentsDisabled = state => state.commentsDisabled; + +export const suggestionsCount = (state, getters) => + Object.values(getters.notesById).filter(n => n.suggestions.length).length; diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index 47fb5b271d1..ae992dd5dc5 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -1,4 +1,5 @@ import $ from 'jquery'; +import { Rails } from '~/lib/utils/rails_ujs'; import { deprecatedCreateFlash as Flash } from './flash'; import { __ } from '~/locale'; @@ -21,10 +22,12 @@ export default function notificationsDropdown() { form.find('.js-notifications-icon').toggleClass('hidden'); } form.find('#notification_setting_level').val(notificationLevel); - form.submit(); + Rails.fire(form[0], 'submit'); }); - $(document).on('ajax:success', '.notification-form', (e, data) => { + $(document).on('ajax:success', '.notification-form', e => { + const data = e.detail[0]; + if (data.saved) { $(e.currentTarget) .closest('.js-notification-dropdown') diff --git a/app/assets/javascripts/related_issues/components/add_issuable_form.vue b/app/assets/javascripts/related_issues/components/add_issuable_form.vue index 63d61989cba..6fbae95094a 100644 --- a/app/assets/javascripts/related_issues/components/add_issuable_form.vue +++ b/app/assets/javascripts/related_issues/components/add_issuable_form.vue @@ -195,7 +195,8 @@ export default { :disabled="isSubmitButtonDisabled" :loading="isSubmitting" type="submit" - class="js-add-issuable-form-add-button float-left qa-add-issue-button" + class="js-add-issuable-form-add-button float-left" + data-qa-selector="add_issue_button" > {{ __('Add') }} </gl-button> diff --git a/app/assets/javascripts/related_issues/components/related_issuable_input.vue b/app/assets/javascripts/related_issues/components/related_issuable_input.vue index 1931cfb2c00..9809b228308 100644 --- a/app/assets/javascripts/related_issues/components/related_issuable_input.vue +++ b/app/assets/javascripts/related_issues/components/related_issuable_input.vue @@ -219,7 +219,8 @@ export default { :value="inputValue" :placeholder="inputPlaceholder" type="text" - class="js-add-issuable-form-input add-issuable-form-input qa-add-issue-input" + class="js-add-issuable-form-input add-issuable-form-input" + data-qa-selector="add_issue_field" @input="onInput" @focus="onFocus" @blur="onBlur" diff --git a/app/assets/javascripts/releases/components/release_block_assets.vue b/app/assets/javascripts/releases/components/release_block_assets.vue index 8824cbefd7e..60d2b3adfc9 100644 --- a/app/assets/javascripts/releases/components/release_block_assets.vue +++ b/app/assets/javascripts/releases/components/release_block_assets.vue @@ -119,7 +119,7 @@ export default { {{ section.title }} </h5> <ul :key="`section-body-${index}`" class="list-unstyled gl-m-0"> - <li v-for="link in section.links" :key="link.url"> + <li v-for="link in section.links" :key="link.url" class="gl-display-flex"> <gl-link :href="link.directAssetUrl || link.url" class="gl-display-flex gl-align-items-center gl-line-height-24" diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue index 05ad7b4ea3e..406677941b7 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue @@ -26,11 +26,14 @@ export default { methods: { listenForQuickActions() { $(document).on('ajax:success', '.gfm-form', this.quickActionListened); + eventHub.$on('timeTrackingUpdated', data => { - this.quickActionListened(null, data); + this.quickActionListened({ detail: [data] }); }); }, - quickActionListened(e, data) { + quickActionListened(e) { + const data = e.detail[0]; + const subscribedCommands = ['spend_time', 'time_estimate']; let changedCommands; if (data !== undefined) { diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue index 13c42d35b04..13ec7a6ada9 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue @@ -27,6 +27,11 @@ export default { type: String, required: true, }, + suggestionsCount: { + type: Number, + required: false, + default: 0, + }, }, computed: { batchSuggestionsCount() { @@ -62,6 +67,7 @@ export default { <div class="md-suggestion"> <suggestion-diff-header class="qa-suggestion-diff-header js-suggestion-diff-header" + :suggestions-count="suggestionsCount" :can-apply="suggestion.appliable && suggestion.current_user.can_apply && !disabled" :is-applied="suggestion.applied" :is-batched="isBatched" diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue index 1fc54d2f52e..fb9636ba734 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue @@ -42,6 +42,11 @@ export default { required: false, default: null, }, + suggestionsCount: { + type: Number, + required: false, + default: 0, + }, }, data() { return { @@ -127,7 +132,7 @@ export default { </div> <div v-else class="d-flex align-items-center"> <gl-button - v-if="canBeBatched && !isDisableButton" + v-if="suggestionsCount > 1 && canBeBatched && !isDisableButton" class="btn-inverted js-add-to-batch-btn btn-grouped" :disabled="isDisableButton" @click="addSuggestionToBatch" diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index 083f581af05..927a93487e6 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -38,6 +38,11 @@ export default { type: String, required: true, }, + suggestionsCount: { + type: Number, + required: false, + default: 0, + }, }, data() { return { @@ -77,12 +82,12 @@ export default { this.isRendered = true; }, generateDiff(suggestionIndex) { - const { suggestions, disabled, batchSuggestionsInfo, helpPagePath } = this; + const { suggestions, disabled, batchSuggestionsInfo, helpPagePath, suggestionsCount } = this; const suggestion = suggestions && suggestions[suggestionIndex] ? suggestions[suggestionIndex] : {}; const SuggestionDiffComponent = Vue.extend(SuggestionDiff); const suggestionDiff = new SuggestionDiffComponent({ - propsData: { disabled, suggestion, batchSuggestionsInfo, helpPagePath }, + propsData: { disabled, suggestion, batchSuggestionsInfo, helpPagePath, suggestionsCount }, }); suggestionDiff.$on('apply', ({ suggestionId, callback }) => { diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb index dcff2be34da..ba7385973e7 100644 --- a/app/helpers/groups/group_members_helper.rb +++ b/app/helpers/groups/group_members_helper.rb @@ -14,7 +14,7 @@ module Groups::GroupMembersHelper end def linked_groups_data_json(group_links) - GroupGroupLinkSerializer.new.represent(group_links).to_json + GroupGroupLinkSerializer.new.represent(group_links, { current_user: current_user }).to_json end def members_data_json(group, members) @@ -47,10 +47,10 @@ module Groups::GroupMembersHelper } }.merge(member_created_by_data(member.created_by)) - if user.present? - data[:user] = member_user_data(user) - else + if member.invite? data[:invite] = member_invite_data(member) + elsif user.present? + data[:user] = member_user_data(user) end data @@ -77,6 +77,17 @@ module Groups::GroupMembersHelper avatar_url: avatar_icon_for_user(user, AVATAR_SIZE), blocked: user.blocked?, two_factor_enabled: user.two_factor_enabled? + }.merge(member_user_status_data(user.status)) + end + + def member_user_status_data(status) + return {} unless status.present? + + { + status: { + emoji: status.emoji, + message_html: status.message_html + } } end diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index 4983de83800..f35d938c10f 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -3,7 +3,7 @@ module Clusters module Applications class Runner < ApplicationRecord - VERSION = '0.20.1' + VERSION = '0.21.0' self.table_name = 'clusters_applications_runners' diff --git a/app/models/commit.rb b/app/models/commit.rb index 5e0fceb23a4..5fe1b451ccd 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -29,12 +29,6 @@ class Commit delegate :repository, to: :container delegate :project, to: :repository, allow_nil: true - DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] - - # Commits above this size will not be rendered in HTML - DIFF_HARD_LIMIT_FILES = 1000 - DIFF_HARD_LIMIT_LINES = 50000 - MIN_SHA_LENGTH = Gitlab::Git::Commit::MIN_SHA_LENGTH COMMIT_SHA_PATTERN = /\h{#{MIN_SHA_LENGTH},40}/.freeze EXACT_COMMIT_SHA_PATTERN = /\A#{COMMIT_SHA_PATTERN}\z/.freeze @@ -80,10 +74,30 @@ class Commit sha[0..MIN_SHA_LENGTH] end - def max_diff_options + def diff_safe_lines + Gitlab::Git::DiffCollection.default_limits[:max_lines] + end + + def diff_hard_limit_files(project: nil) + if Feature.enabled?(:increased_diff_limits, project) + 2000 + else + 1000 + end + end + + def diff_hard_limit_lines(project: nil) + if Feature.enabled?(:increased_diff_limits, project) + 75000 + else + 50000 + end + end + + def max_diff_options(project: nil) { - max_files: DIFF_HARD_LIMIT_FILES, - max_lines: DIFF_HARD_LIMIT_LINES + max_files: diff_hard_limit_files(project: project), + max_lines: diff_hard_limit_lines(project: project) } end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 639df749774..43baa1cb5cd 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -631,7 +631,7 @@ class MergeRequest < ApplicationRecord def diff_size # Calling `merge_request_diff.diffs.real_size` will also perform # highlighting, which we don't need here. - merge_request_diff&.real_size || diff_stats&.real_size || diffs.real_size + merge_request_diff&.real_size || diff_stats&.real_size(project: project) || diffs.real_size end def modified_paths(past_merge_request_diff: nil, fallback_on_overflow: false) diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 880e3cc1ba5..1e02724db9d 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -651,7 +651,7 @@ class MergeRequestDiff < ApplicationRecord if compare.commits.empty? new_attributes[:state] = :empty else - diff_collection = compare.diffs(Commit.max_diff_options) + diff_collection = compare.diffs(Commit.max_diff_options(project: merge_request.project)) new_attributes[:real_size] = diff_collection.real_size if diff_collection.any? diff --git a/app/models/note.rb b/app/models/note.rb index 812d77d5f86..954843505d4 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -322,8 +322,6 @@ class Note < ApplicationRecord end def contributor? - return false unless ::Feature.enabled?(:show_contributor_on_note, project) - project&.team&.contributor?(self.author_id) end diff --git a/app/models/service.rb b/app/models/service.rb index c96566612b6..af31cf4a335 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -7,11 +7,8 @@ class Service < ApplicationRecord include Importable include ProjectServicesLoggable include DataFields - include IgnorableColumns include FromUnion - ignore_columns %i[default], remove_with: '13.5', remove_after: '2020-10-22' - SERVICE_NAMES = %w[ alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker discord drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira diff --git a/app/serializers/group_group_link_entity.rb b/app/serializers/group_group_link_entity.rb index 7a51e1a9316..491c672233d 100644 --- a/app/serializers/group_group_link_entity.rb +++ b/app/serializers/group_group_link_entity.rb @@ -1,12 +1,22 @@ # frozen_string_literal: true class GroupGroupLinkEntity < Grape::Entity + include RequestAwareEntity + expose :id expose :created_at expose :expires_at do |group_link| group_link.expires_at&.to_time end + expose :can_update do |group_link| + can_manage?(group_link) + end + + expose :can_remove do |group_link| + can_manage?(group_link) + end + expose :access_level do expose :human_access, as: :string_value expose :group_access, as: :integer_value @@ -23,4 +33,14 @@ class GroupGroupLinkEntity < Grape::Entity expose :shared_with_group, merge: true, using: GroupBasicEntity end + + private + + def current_user + options[:current_user] + end + + def can_manage?(group_link) + can?(current_user, :admin_group_member, group_link.shared_group) + end end diff --git a/app/services/ci/update_build_state_service.rb b/app/services/ci/update_build_state_service.rb index 61e4c77c1e5..b73d2708b14 100644 --- a/app/services/ci/update_build_state_service.rb +++ b/app/services/ci/update_build_state_service.rb @@ -2,7 +2,7 @@ module Ci class UpdateBuildStateService - Result = Struct.new(:status, keyword_init: true) + Result = Struct.new(:status, :backoff, keyword_init: true) ACCEPT_TIMEOUT = 5.minutes.freeze @@ -28,7 +28,9 @@ module Ci private def accept_build_state! - if Time.current - ensure_pending_state.created_at > ACCEPT_TIMEOUT + state_created = ensure_pending_state.created_at + + if Time.current - state_created > ACCEPT_TIMEOUT metrics.increment_trace_operation(operation: :discarded) return update_build_state! @@ -40,7 +42,9 @@ module Ci metrics.increment_trace_operation(operation: :accepted) - Result.new(status: 202) + ::Gitlab::Ci::Runner::Backoff.new(state_created).then do |backoff| + Result.new(status: 202, backoff: backoff.to_seconds) + end end def overwrite_trace! diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index 641a0689c26..a945ff5aedf 100644 --- a/app/views/projects/diffs/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -1,4 +1,4 @@ -- too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES +- too_big = diff_file.diff_lines.count > Commit.diff_safe_lines - if too_big .suppressed-container %a.show-suppressed-diff.cursor-pointer.js-show-suppressed-diff= _("Changes suppressed. Click to show.") diff --git a/changelogs/unreleased/display-contributor-and-author-badges-on-notes.yml b/changelogs/unreleased/display-contributor-and-author-badges-on-notes.yml new file mode 100644 index 00000000000..163053c1b70 --- /dev/null +++ b/changelogs/unreleased/display-contributor-and-author-badges-on-notes.yml @@ -0,0 +1,5 @@ +--- +title: Display Contributor badges on notes +merge_request: 42576 +author: Mycroft Kang @TaehyeokKang +type: added diff --git a/changelogs/unreleased/nfriend-fix-release-asset-link-length.yml b/changelogs/unreleased/nfriend-fix-release-asset-link-length.yml new file mode 100644 index 00000000000..435929e247c --- /dev/null +++ b/changelogs/unreleased/nfriend-fix-release-asset-link-length.yml @@ -0,0 +1,5 @@ +--- +title: Fix clickable width of release asset links +merge_request: 42757 +author: +type: fixed diff --git a/changelogs/unreleased/ph-224141-hideBatchSuggestionsWithOnlyOneSuggestion.yml b/changelogs/unreleased/ph-224141-hideBatchSuggestionsWithOnlyOneSuggestion.yml new file mode 100644 index 00000000000..bdd30e0e946 --- /dev/null +++ b/changelogs/unreleased/ph-224141-hideBatchSuggestionsWithOnlyOneSuggestion.yml @@ -0,0 +1,5 @@ +--- +title: Hides batch suggestions button if there is only 1 suggestion +merge_request: 42681 +author: +type: fixed diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-21-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-21-0.yml new file mode 100644 index 00000000000..0b4b92717e7 --- /dev/null +++ b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-21-0.yml @@ -0,0 +1,5 @@ +--- +title: Update GitLab Runner Helm Chart to 0.21.0 +merge_request: 42844 +author: +type: other diff --git a/config/feature_flags/development/show_contributor_on_note.yml b/config/feature_flags/development/increased_diff_limits.yml index 89533037244..aeff8e3533d 100644 --- a/config/feature_flags/development/show_contributor_on_note.yml +++ b/config/feature_flags/development/increased_diff_limits.yml @@ -1,7 +1,7 @@ --- -name: show_contributor_on_note -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40198 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249179 -group: group::project management +name: increased_diff_limits +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40357 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241185 +group: group::source_code type: development default_enabled: false
\ No newline at end of file diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 305216f853a..7e94ff7b7f2 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -24,7 +24,8 @@ Parameters: - `path` (optional) - The path inside repository. Used to get content of subdirectories - `ref` (optional) - The name of a repository branch or tag or if not given the default branch - `recursive` (optional) - Boolean value used to get a recursive tree (false by default) -- `per_page` (optional) - Number of results to show per page. If not specified, defaults to `20` +- `per_page` (optional) - Number of results to show per page. If not specified, defaults to `20`. + Read more on [pagination](README.md#pagination). ```json [ diff --git a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md index 73896547675..4a0ff2fa6ac 100644 --- a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md +++ b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md @@ -41,15 +41,14 @@ The JWT's payload looks like this: "nbf": 1585798372, # Not valid before "exp": 1585713886, # Expire at "sub": "job_1212", # Subject (job id) - "namespace_id": "1", - "namespace_path": "mygroup", - "project_id": "22", - "project_path": "mygroup/myproject", - "user_id": "42", - "user_login": "myuser", - "user_email": "myuser@example.com", - "pipeline_id": "1212", - "job_id": "1212", + "namespace_id": "1", # Use this to scope to group or user level namespace by id + "namespace_path": "mygroup", # Use this to scope to group or user level namespace by path + "project_id": "22", # + "project_path": "mygroup/myproject", # + "user_id": "42", # Id of the user executing the job + "user_email": "myuser@example.com", # Email of the user executing the job + "pipeline_id": "1212", # + "job_id": "1212", # "ref": "auto-deploy-2020-04-01", # Git ref for this job "ref_type": "branch", # Git ref type, branch or tag "ref_protected": "true" # true if this git ref is protected, false otherwise diff --git a/doc/development/fe_guide/architecture.md b/doc/development/fe_guide/architecture.md index 3d27f67a8a6..c7e1ba59f23 100644 --- a/doc/development/fe_guide/architecture.md +++ b/doc/development/fe_guide/architecture.md @@ -15,5 +15,5 @@ You can find the Frontend Architecture experts on the [team page](https://about. ## Examples -You can find documentation about the desired architecture for a new feature -built with Vue.js [here](vue.md). +You can find [documentation about the desired architecture](vue.md) for a new +feature built with Vue.js. diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md index b539293e9cf..67add5709d9 100644 --- a/doc/development/fe_guide/icons.md +++ b/doc/development/fe_guide/icons.md @@ -1,9 +1,10 @@ # Icons and SVG Illustrations -We manage our own Icon and Illustration library in the [`gitlab-svgs`](https://gitlab.com/gitlab-org/gitlab-svgs) repository. -This repository is published on [npm](https://www.npmjs.com/package/@gitlab/svgs) and managed as a dependency via yarn. -You can browse all available Icons and Illustrations [here](https://gitlab-org.gitlab.io/gitlab-svgs). -To upgrade to a new version run `yarn upgrade @gitlab/svgs`. +We manage our own icon and illustration library in the [`gitlab-svgs`](https://gitlab.com/gitlab-org/gitlab-svgs) +repository. This repository is published on [npm](https://www.npmjs.com/package/@gitlab/svgs), +and is managed as a dependency with yarn. You can browse all available +[icons and illustrations](https://gitlab-org.gitlab.io/gitlab-svgs). To upgrade +to a new version run `yarn upgrade @gitlab/svgs`. ## Icons @@ -21,10 +22,11 @@ To use a sprite Icon in HAML or Rails we use a specific helper function : sprite_icon(icon_name, size: nil, css_class: '') ``` -- **icon_name** Use the icon_name that you can find in the SVG Sprite - ([Overview is available here](https://gitlab-org.gitlab.io/gitlab-svgs)). -- **size (optional)** Use one of the following sizes : 16, 24, 32, 48, 72 (this will be translated into a `s16` class) -- **css_class (optional)** If you want to add additional CSS classes +- **icon_name**: Use the icon_name for the SVG sprite in the list of + ([GitLab SVGs](https://gitlab-org.gitlab.io/gitlab-svgs)). +- **size (optional)**: Use one of the following sizes : 16, 24, 32, 48, 72 (this + will be translated into a `s16` class) +- **css_class (optional)**: If you want to add additional CSS classes. **Example** @@ -66,10 +68,12 @@ export default { </template> ``` -- **name** Name of the Icon in the SVG Sprite ([Overview is available here](https://gitlab-org.gitlab.io/gitlab-svgs)). -- **size (optional)** Number value for the size which is then mapped to a specific CSS class - (Available Sizes: 8, 12, 16, 18, 24, 32, 48, 72 are mapped to `sXX` CSS classes) -- **class (optional)** Additional CSS Classes to add to the SVG tag. +- **name**: Name of the icon of the SVG sprite, as listed in the + ([GitLab SVG Previewer](https://gitlab-org.gitlab.io/gitlab-svgs)). +- **size: (optional)** Number value for the size which is then mapped to a + specific CSS class (Available sizes: 8, 12, 16, 18, 24, 32, 48, 72 are mapped + to `sXX` CSS classes) +- **class (optional)**: Additional CSS classes to add to the SVG tag. ### Usage in HTML/JS diff --git a/doc/development/fe_guide/tooling.md b/doc/development/fe_guide/tooling.md index 5685ac5abcd..b93c0a9736b 100644 --- a/doc/development/fe_guide/tooling.md +++ b/doc/development/fe_guide/tooling.md @@ -101,7 +101,10 @@ Our code is automatically formatted with [Prettier](https://prettier.io) to foll ### Editor -The easiest way to include prettier in your workflow is by setting up your preferred editor (all major editors are supported) accordingly. We suggest setting up prettier to run automatically when each file is saved. Find [here](https://prettier.io/docs/en/editors.html) the best way to set it up in your preferred editor. +The recommended method to include Prettier in your workflow is to set up your +preferred editor (all major editors are supported) accordingly. We suggest +setting up Prettier to run when each file is saved. For instructions about using +Prettier in your preferred editor, see the [Prettier documentation](https://prettier.io/docs/en/editors.html). Please take care that you only let Prettier format the same file types as the global Yarn script does (`.js`, `.vue`, `.graphql`, and `.scss`). In VSCode by example you can easily exclude file formats in your settings file: diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md index 4badf3f0845..528dcb3b7f4 100644 --- a/doc/development/fe_guide/vuex.md +++ b/doc/development/fe_guide/vuex.md @@ -32,8 +32,9 @@ When using Vuex at GitLab, separate these concerns into different files to impro └── mutation_types.js # mutation types ``` -The following example shows an application that lists and adds users to the state. -(For a more complex example implementation take a look at the security applications store in [here](https://gitlab.com/gitlab-org/gitlab/tree/master/ee/app/assets/javascripts/vue_shared/security_reports/store)) +The following example shows an application that lists and adds users to the +state. (For a more complex example implementation, review the security +applications stored in this [repository](https://gitlab.com/gitlab-org/gitlab/tree/master/ee/app/assets/javascripts/vue_shared/security_reports/store)). ### `index.js` diff --git a/doc/development/new_fe_guide/development/accessibility.md b/doc/development/new_fe_guide/development/accessibility.md index b9ee5c3a549..9c63ccad6e1 100644 --- a/doc/development/new_fe_guide/development/accessibility.md +++ b/doc/development/new_fe_guide/development/accessibility.md @@ -12,7 +12,7 @@ WAI-ARIA (the Accessible Rich Internet Applications specification) defines a way The `role` attribute describes the role the element plays in the context of the document. -Check the list of WAI-ARIA roles [here](https://www.w3.org/TR/wai-aria-1.1/#landmark_roles) +Review the list of [WAI-ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#landmark_roles). ## Icons diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md index 1961d1dcc34..8e7cc1ec9f2 100644 --- a/doc/development/secure_coding_guidelines.md +++ b/doc/development/secure_coding_guidelines.md @@ -82,7 +82,8 @@ This Ruby Regex specialty can have security impact, as often regular expressions #### Examples -GitLab specific examples can be found [here](https://gitlab.com/gitlab-org/gitlab/-/issues/36029#note_251262187) and [there](https://gitlab.com/gitlab-org/gitlab/-/issues/33569). +GitLab-specific examples can be found in the following [path traversal](https://gitlab.com/gitlab-org/gitlab/-/issues/36029#note_251262187) +and [open redirect](https://gitlab.com/gitlab-org/gitlab/-/issues/33569) issues. Another example would be this fictional Ruby on Rails controller: diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 1c5654ae96c..6a4dc5426f8 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -24,7 +24,7 @@ connections to GitLab repositories. ## Requirements To support SSH, GitLab requires the installation of the OpenSSH client, which -comes pre-installed on GNU/Linux and macOS, but not on Windows. +comes pre-installed on GNU/Linux and macOS, as well as on Windows 10. Make sure that your system includes SSH version 6.5 or newer, as that excludes the now insecure MD5 signature scheme. The following command returns the version of diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index 08903dce3dc..e293c299d75 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -181,6 +181,7 @@ module API .new(job, declared_params(include_missing: false)) service.execute.then do |result| + header 'X-GitLab-Trace-Update-Interval', result.backoff status result.status end end diff --git a/lib/gitlab/ci/runner/backoff.rb b/lib/gitlab/ci/runner/backoff.rb new file mode 100644 index 00000000000..14bbf18941d --- /dev/null +++ b/lib/gitlab/ci/runner/backoff.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Runner + ## + # Runner Backoff class is an implementation of an exponential backoff + # used when a runner communicates with GitLab. We typically use it when a + # runner retries sending a build status after we created a build pending + # state. + # + # Backoff is calculated based on the backoff slot which is always a power + # of 2: + # + # 0s - 3s duration -> 1 second backoff + # 4s - 7s duration -> 2 seconds backoff + # 8s - 15s duration -> 4 seconds backoff + # 16s - 31s duration -> 8 seconds backoff + # 32s - 63s duration -> 16 seconds backoff + # 64s - 127s duration -> 32 seconds backoff + # 127s - 256s+ duration -> 64 seconds backoff + # + # It means that first 15 requests made by a runner will need to respect + # following backoffs: + # + # 0s -> 1 second backoff (backoff started, slot 0, 2^0 backoff) + # 1s -> 1 second backoff + # 2s -> 1 second backoff + # 3s -> 1 seconds backoff + # (slot 1 - 2^1 backoff) + # 4s -> 2 seconds backoff + # 6s -> 2 seconds backoff + # (slot 2 - 2^2 backoff) + # 8s -> 4 seconds backoff + # 12s -> 4 seconds backoff + # (slot 3 - 2^3 backoff) + # 16s -> 8 seconds backoff + # 24s -> 8 seconds backoff + # (slot 4 - 2^4 backoff) + # 32s -> 16 seconds backoff + # 48s -> 16 seconds backoff + # (slot 5 - 2^5 backoff) + # 64s -> 32 seconds backoff + # 96s -> 32 seconds backoff + # (slot 6 - 2^6 backoff) + # 128s -> 64 seconds backoff + # + # There is a cap on the backoff - it will never exceed 64 seconds. + # + class Backoff + def initialize(started) + @started = started + + if duration < 0 + raise ArgumentError, 'backoff duration negative' + end + end + + def duration + Time.current - @started + end + + def slot + return 0 if duration <= 1 + + Math.log(duration, 2).floor - 1 + end + + def to_seconds + 2**[slot, 6].min + end + end + end + end +end diff --git a/lib/gitlab/diff/file_collection/merge_request_diff_base.rb b/lib/gitlab/diff/file_collection/merge_request_diff_base.rb index d54e1aad19a..341572f9c94 100644 --- a/lib/gitlab/diff/file_collection/merge_request_diff_base.rb +++ b/lib/gitlab/diff/file_collection/merge_request_diff_base.rb @@ -11,7 +11,7 @@ module Gitlab super(merge_request_diff, project: merge_request_diff.project, - diff_options: diff_options, + diff_options: merged_diff_options(diff_options), diff_refs: merge_request_diff.diff_refs, fallback_diff_refs: merge_request_diff.fallback_diff_refs) end @@ -64,6 +64,11 @@ module Gitlab diff_stats_cache.read || super end end + + def merged_diff_options(diff_options) + max_diff_options = ::Commit.max_diff_options(project: @merge_request_diff.project) + diff_options.present? ? diff_options.merge(max_diff_options) : max_diff_options + end end end end diff --git a/lib/gitlab/git/diff_collection.rb b/lib/gitlab/git/diff_collection.rb index e6121d688ba..2fa88973bae 100644 --- a/lib/gitlab/git/diff_collection.rb +++ b/lib/gitlab/git/diff_collection.rb @@ -7,19 +7,23 @@ module Gitlab class DiffCollection include Enumerable - DEFAULT_LIMITS = { max_files: 100, max_lines: 5000 }.freeze - attr_reader :limits delegate :max_files, :max_lines, :max_bytes, :safe_max_files, :safe_max_lines, :safe_max_bytes, to: :limits + def self.default_limits + { max_files: 100, max_lines: 5000 } + end + def self.limits(options = {}) limits = {} - limits[:max_files] = options.fetch(:max_files, DEFAULT_LIMITS[:max_files]) - limits[:max_lines] = options.fetch(:max_lines, DEFAULT_LIMITS[:max_lines]) + defaults = default_limits + limits[:max_files] = options.fetch(:max_files, defaults[:max_files]) + limits[:max_lines] = options.fetch(:max_lines, defaults[:max_lines]) limits[:max_bytes] = limits[:max_files] * 5.kilobytes # Average 5 KB per file - limits[:safe_max_files] = [limits[:max_files], DEFAULT_LIMITS[:max_files]].min - limits[:safe_max_lines] = [limits[:max_lines], DEFAULT_LIMITS[:max_lines]].min + + limits[:safe_max_files] = [limits[:max_files], defaults[:max_files]].min + limits[:safe_max_lines] = [limits[:max_lines], defaults[:max_lines]].min limits[:safe_max_bytes] = limits[:safe_max_files] * 5.kilobytes # Average 5 KB per file limits[:max_patch_bytes] = Gitlab::Git::Diff.patch_hard_limit_bytes diff --git a/lib/gitlab/git/diff_stats_collection.rb b/lib/gitlab/git/diff_stats_collection.rb index 7e49d79676e..e30ec836a49 100644 --- a/lib/gitlab/git/diff_stats_collection.rb +++ b/lib/gitlab/git/diff_stats_collection.rb @@ -22,8 +22,8 @@ module Gitlab @collection.map(&:path) end - def real_size - max_files = ::Commit.max_diff_options[:max_files] + def real_size(project: nil) + max_files = ::Commit.max_diff_options(project: project)[:max_files] if paths.size > max_files "#{max_files}+" else diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1d9c44cfb71..d91a10cae95 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -21061,9 +21061,6 @@ msgstr "" msgid "Release|Something went wrong while saving the release details" msgstr "" -msgid "Remediated: needs review" -msgstr "" - msgid "Remediations" msgstr "" @@ -22424,6 +22421,9 @@ msgstr "" msgid "SecurityConfiguration|Available for on-demand DAST" msgstr "" +msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the Merge Request." +msgstr "" + msgid "SecurityConfiguration|Configure" msgstr "" @@ -22457,6 +22457,9 @@ msgstr "" msgid "SecurityConfiguration|Not enabled" msgstr "" +msgid "SecurityConfiguration|SAST Analyzers" +msgstr "" + msgid "SecurityConfiguration|SAST Configuration" msgstr "" @@ -26822,6 +26825,9 @@ msgstr "" msgid "Total cores (CPUs)" msgstr "" +msgid "Total days to completion" +msgstr "" + msgid "Total issues" msgstr "" @@ -28329,6 +28335,9 @@ msgstr "" msgid "Vulnerability|%{scannerName} (version %{scannerVersion})" msgstr "" +msgid "Vulnerability|Activity" +msgstr "" + msgid "Vulnerability|Class" msgstr "" diff --git a/package.json b/package.json index 307c24c80ce..b9fa6212dc5 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@gitlab/ui": "21.4.2", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-1", + "@rails/ujs": "^6.0.3-2", "@sentry/browser": "^5.22.3", "@sourcegraph/code-host-integration": "0.0.50", "@toast-ui/editor": "^2.4.0", @@ -99,7 +100,6 @@ "jed": "^1.1.1", "jest-transform-graphql": "^2.1.0", "jquery": "^3.5.0", - "jquery-ujs": "1.2.2", "jquery.caret": "^0.3.1", "jquery.waitforimages": "^2.2.0", "js-cookie": "^2.2.1", diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb index 826acaa2e0a..6e5d176b812 100644 --- a/qa/qa/page/project/issue/show.rb +++ b/qa/qa/page/project/issue/show.rb @@ -43,7 +43,7 @@ module QA end view 'app/assets/javascripts/related_issues/components/related_issuable_input.vue' do - element :add_issue_input + element :add_issue_field end view 'app/assets/javascripts/related_issues/components/related_issues_block.vue' do @@ -57,8 +57,8 @@ module QA def relate_issue(issue) click_element(:related_issues_plus_button) - fill_element(:add_issue_input, issue.web_url) - send_keys_to_element(:add_issue_input, :enter) + fill_element(:add_issue_field, issue.web_url) + send_keys_to_element(:add_issue_field, :enter) end def related_issuable_item diff --git a/qa/qa/support/wait_for_requests.rb b/qa/qa/support/wait_for_requests.rb index 2a72f833edc..ebc473a7d86 100644 --- a/qa/qa/support/wait_for_requests.rb +++ b/qa/qa/support/wait_for_requests.rb @@ -9,20 +9,14 @@ module QA def wait_for_requests(skip_finished_loading_check: false) Waiter.wait_until(log: false) do - finished_all_ajax_requests? && finished_all_axios_requests? && (!skip_finished_loading_check ? finished_loading?(wait: 1) : true) + finished_all_ajax_requests? && (!skip_finished_loading_check ? finished_loading?(wait: 1) : true) end rescue Repeater::WaitExceededError raise $!, 'Page did not fully load. This could be due to an unending async request or loading icon.' end - def finished_all_axios_requests? - Capybara.page.evaluate_script('window.pendingRequests || 0').zero? # rubocop:disable Style/NumericPredicate - end - def finished_all_ajax_requests? - return true if Capybara.page.evaluate_script('typeof jQuery === "undefined"') - - Capybara.page.evaluate_script('jQuery.active').zero? # rubocop:disable Style/NumericPredicate + Capybara.page.evaluate_script('window.pendingRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate end def finished_loading?(wait: DEFAULT_MAX_WAIT_TIME) diff --git a/qa/spec/support/wait_for_requests_spec.rb b/qa/spec/support/wait_for_requests_spec.rb index 79ee3eb5099..26c4951d198 100644 --- a/qa/spec/support/wait_for_requests_spec.rb +++ b/qa/spec/support/wait_for_requests_spec.rb @@ -3,7 +3,6 @@ describe QA::Support::WaitForRequests do describe '.wait_for_requests' do before do - allow(subject).to receive(:finished_all_axios_requests?).and_return(true) allow(subject).to receive(:finished_all_ajax_requests?).and_return(true) allow(subject).to receive(:finished_loading?).and_return(true) end diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index e705f2916da..49343cc7a57 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'Expand and collapse diffs', :js do let(:project) { create(:project, :repository) } before do + stub_feature_flags(increased_diff_limits: false) sign_in(create(:admin)) # Ensure that undiffable.md is in .gitattributes diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb index 0e39cce13a1..a857e6b0ddb 100644 --- a/spec/features/merge_request/user_expands_diff_spec.rb +++ b/spec/features/merge_request/user_expands_diff_spec.rb @@ -7,6 +7,7 @@ RSpec.describe 'User expands diff', :js do let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) } before do + stub_feature_flags(increased_diff_limits: false) visit(diffs_project_merge_request_path(project, merge_request)) wait_for_requests diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb index 39495832547..fe3307c0ceb 100644 --- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb +++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb @@ -130,7 +130,9 @@ RSpec.describe 'User comments on a diff', :js do wait_for_requests end end + end + files.each_with_index do |file, index| page.within("[id='#{file[:hash]}']") do expect(page).not_to have_content('Applied') diff --git a/spec/features/projects/show/user_manages_notifications_spec.rb b/spec/features/projects/show/user_manages_notifications_spec.rb index 9d9a75c22be..d444ea27d35 100644 --- a/spec/features/projects/show/user_manages_notifications_spec.rb +++ b/spec/features/projects/show/user_manages_notifications_spec.rb @@ -18,7 +18,9 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do click_notifications_button click_link 'On mention' - wait_for_requests + page.within('.notification-dropdown') do + expect(page).not_to have_css('.gl-spinner') + end click_notifications_button expect(find('.update-notification.is-active')).to have_content('On mention') @@ -30,7 +32,9 @@ RSpec.describe 'Projects > Show > User manages notifications', :js do click_notifications_button click_link 'Disabled' - wait_for_requests + page.within('.notification-dropdown') do + expect(page).not_to have_css('.gl-spinner') + end expect(page).to have_css('.notifications-icon[data-testid="notifications-off-icon"]') end diff --git a/spec/fixtures/api/schemas/entities/group_group_link.json b/spec/fixtures/api/schemas/entities/group_group_link.json index 4c9aae140d2..dda94dc3362 100644 --- a/spec/fixtures/api/schemas/entities/group_group_link.json +++ b/spec/fixtures/api/schemas/entities/group_group_link.json @@ -1,10 +1,12 @@ { "type": "object", - "required": ["id", "created_at", "expires_at", "access_level"], + "required": ["id", "created_at", "expires_at", "can_update", "can_remove", "access_level"], "properties": { "id": { "type": "integer" }, "created_at": { "type": "date-time" }, "expires_at": { "type": ["date-time", "null"] }, + "can_update": { "type": "boolean" }, + "can_remove": { "type": "boolean" }, "access_level": { "type": "object", "required": ["integer_value", "string_value"], diff --git a/spec/fixtures/api/schemas/group_member.json b/spec/fixtures/api/schemas/group_member.json index 035c862d229..eefd4aeb219 100644 --- a/spec/fixtures/api/schemas/group_member.json +++ b/spec/fixtures/api/schemas/group_member.json @@ -62,7 +62,18 @@ "avatar_url": { "type": ["string", "null"] }, "web_url": { "type": "string" }, "blocked": { "type": "boolean" }, - "two_factor_enabled": { "type": "boolean" } + "two_factor_enabled": { "type": "boolean" }, + "status": { + "type": "object", + "required": [ + "emoji", + "message_html" + ], + "properties": { + "emoji": { "type": "string" }, + "message_html": { "type": "string" } + } + } } }, "invite": { diff --git a/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap index c30fb572826..00fcc3323f5 100644 --- a/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap +++ b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap @@ -1,79 +1,139 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`GroupEmptyState Renders an empty state for BAD_QUERY 1`] = ` -<gl-empty-state-stub - compact="true" - primarybuttonlink="/path/to/settings" - primarybuttontext="Verify configuration" - svgpath="/path/to/empty-group-illustration.svg" - title="Query cannot be processed" -/> +exports[`GroupEmptyState given state BAD_QUERY passes the expected props to GlEmptyState 1`] = ` +Object { + "compact": true, + "description": null, + "primaryButtonLink": "/path/to/settings", + "primaryButtonText": "Verify configuration", + "secondaryButtonLink": null, + "secondaryButtonText": null, + "svgPath": "/path/to/empty-group-illustration.svg", + "title": "Query cannot be processed", +} `; -exports[`GroupEmptyState Renders an empty state for BAD_QUERY 2`] = `"The Prometheus server responded with \\"bad request\\". Please check your queries are correct and are supported in your Prometheus version. <a href=\\"/path/to/docs\\">More information</a>"`; +exports[`GroupEmptyState given state BAD_QUERY renders the slotted content 1`] = ` +<div> + <div> + The Prometheus server responded with "bad request". Please check your queries are correct and are supported in your Prometheus version. + <a + href="/path/to/docs" + > + More information + </a> + </div> +</div> +`; -exports[`GroupEmptyState Renders an empty state for CONNECTION_FAILED 1`] = ` -<gl-empty-state-stub - compact="true" - description="We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating." - primarybuttonlink="/path/to/settings" - primarybuttontext="Verify configuration" - svgpath="/path/to/empty-group-illustration.svg" - title="Connection failed" -/> +exports[`GroupEmptyState given state CONNECTION_FAILED passes the expected props to GlEmptyState 1`] = ` +Object { + "compact": true, + "description": "We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating.", + "primaryButtonLink": "/path/to/settings", + "primaryButtonText": "Verify configuration", + "secondaryButtonLink": null, + "secondaryButtonText": null, + "svgPath": "/path/to/empty-group-illustration.svg", + "title": "Connection failed", +} `; -exports[`GroupEmptyState Renders an empty state for CONNECTION_FAILED 2`] = `undefined`; +exports[`GroupEmptyState given state CONNECTION_FAILED renders the slotted content 1`] = `<div />`; -exports[`GroupEmptyState Renders an empty state for FOO STATE 1`] = ` -<gl-empty-state-stub - compact="true" - description="An error occurred while loading the data. Please try again." - svgpath="/path/to/empty-group-illustration.svg" - title="An error has occurred" -/> +exports[`GroupEmptyState given state FOO STATE passes the expected props to GlEmptyState 1`] = ` +Object { + "compact": true, + "description": "An error occurred while loading the data. Please try again.", + "primaryButtonLink": null, + "primaryButtonText": null, + "secondaryButtonLink": null, + "secondaryButtonText": null, + "svgPath": "/path/to/empty-group-illustration.svg", + "title": "An error has occurred", +} `; -exports[`GroupEmptyState Renders an empty state for FOO STATE 2`] = `undefined`; +exports[`GroupEmptyState given state FOO STATE renders the slotted content 1`] = `<div />`; -exports[`GroupEmptyState Renders an empty state for LOADING 1`] = ` -<gl-empty-state-stub - compact="true" - description="Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available." - svgpath="/path/to/empty-group-illustration.svg" - title="Waiting for performance data" -/> +exports[`GroupEmptyState given state LOADING passes the expected props to GlEmptyState 1`] = ` +Object { + "compact": true, + "description": "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.", + "primaryButtonLink": null, + "primaryButtonText": null, + "secondaryButtonLink": null, + "secondaryButtonText": null, + "svgPath": "/path/to/empty-group-illustration.svg", + "title": "Waiting for performance data", +} `; -exports[`GroupEmptyState Renders an empty state for LOADING 2`] = `undefined`; +exports[`GroupEmptyState given state LOADING renders the slotted content 1`] = `<div />`; -exports[`GroupEmptyState Renders an empty state for NO_DATA 1`] = ` -<gl-empty-state-stub - compact="true" - svgpath="/path/to/empty-group-illustration.svg" - title="No data to display" -/> +exports[`GroupEmptyState given state NO_DATA passes the expected props to GlEmptyState 1`] = ` +Object { + "compact": true, + "description": null, + "primaryButtonLink": null, + "primaryButtonText": null, + "secondaryButtonLink": null, + "secondaryButtonText": null, + "svgPath": "/path/to/empty-group-illustration.svg", + "title": "No data to display", +} `; -exports[`GroupEmptyState Renders an empty state for NO_DATA 2`] = `"The data source is connected, but there is no data to display. <a href=\\"/path/to/docs\\">More information</a>"`; +exports[`GroupEmptyState given state NO_DATA renders the slotted content 1`] = ` +<div> + <div> + The data source is connected, but there is no data to display. + <a + href="/path/to/docs" + > + More information + </a> + </div> +</div> +`; -exports[`GroupEmptyState Renders an empty state for TIMEOUT 1`] = ` -<gl-empty-state-stub - compact="true" - svgpath="/path/to/empty-group-illustration.svg" - title="Connection timed out" -/> +exports[`GroupEmptyState given state TIMEOUT passes the expected props to GlEmptyState 1`] = ` +Object { + "compact": true, + "description": null, + "primaryButtonLink": null, + "primaryButtonText": null, + "secondaryButtonLink": null, + "secondaryButtonText": null, + "svgPath": "/path/to/empty-group-illustration.svg", + "title": "Connection timed out", +} `; -exports[`GroupEmptyState Renders an empty state for TIMEOUT 2`] = `"Charts can't be displayed as the request for data has timed out. <a href=\\"/path/to/docs\\">More information</a>"`; +exports[`GroupEmptyState given state TIMEOUT renders the slotted content 1`] = ` +<div> + <div> + Charts can't be displayed as the request for data has timed out. + <a + href="/path/to/docs" + > + More information + </a> + </div> +</div> +`; -exports[`GroupEmptyState Renders an empty state for UNKNOWN_ERROR 1`] = ` -<gl-empty-state-stub - compact="true" - description="An error occurred while loading the data. Please try again." - svgpath="/path/to/empty-group-illustration.svg" - title="An error has occurred" -/> +exports[`GroupEmptyState given state UNKNOWN_ERROR passes the expected props to GlEmptyState 1`] = ` +Object { + "compact": true, + "description": "An error occurred while loading the data. Please try again.", + "primaryButtonLink": null, + "primaryButtonText": null, + "secondaryButtonLink": null, + "secondaryButtonText": null, + "svgPath": "/path/to/empty-group-illustration.svg", + "title": "An error has occurred", +} `; -exports[`GroupEmptyState Renders an empty state for UNKNOWN_ERROR 2`] = `undefined`; +exports[`GroupEmptyState given state UNKNOWN_ERROR renders the slotted content 1`] = `<div />`; diff --git a/spec/frontend/monitoring/components/group_empty_state_spec.js b/spec/frontend/monitoring/components/group_empty_state_spec.js index 90bd6f67196..3b94c4c6806 100644 --- a/spec/frontend/monitoring/components/group_empty_state_spec.js +++ b/spec/frontend/monitoring/components/group_empty_state_spec.js @@ -1,7 +1,13 @@ +import { GlEmptyState } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import GroupEmptyState from '~/monitoring/components/group_empty_state.vue'; import { metricStates } from '~/monitoring/constants'; +const MockGlEmptyState = { + props: GlEmptyState.props, + template: '<div><slot name="description"></slot></div>', +}; + function createComponent(props) { return shallowMount(GroupEmptyState, { propsData: { @@ -10,11 +16,20 @@ function createComponent(props) { settingsPath: '/path/to/settings', svgPath: '/path/to/empty-group-illustration.svg', }, + stubs: { + GlEmptyState: MockGlEmptyState, + }, }); } describe('GroupEmptyState', () => { - const supportedStates = [ + let wrapper; + + afterEach(() => { + wrapper.destroy(); + }); + + describe.each([ metricStates.NO_DATA, metricStates.TIMEOUT, metricStates.CONNECTION_FAILED, @@ -22,13 +37,17 @@ describe('GroupEmptyState', () => { metricStates.LOADING, metricStates.UNKNOWN_ERROR, 'FOO STATE', // does not fail with unknown states - ]; + ])('given state %s', selectedState => { + beforeEach(() => { + wrapper = createComponent({ selectedState }); + }); - it.each(supportedStates)('Renders an empty state for %s', selectedState => { - const wrapper = createComponent({ selectedState }); + it('renders the slotted content', () => { + expect(wrapper.element).toMatchSnapshot(); + }); - expect(wrapper.element).toMatchSnapshot(); - // slot is not rendered by the stub, test it separately - expect(wrapper.vm.currentState.slottedDescription).toMatchSnapshot(); + it('passes the expected props to GlEmptyState', () => { + expect(wrapper.find(MockGlEmptyState).props()).toMatchSnapshot(); + }); }); }); diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js index 544c19da57b..eebec7de9d4 100644 --- a/spec/frontend/test_setup.js +++ b/spec/frontend/test_setup.js @@ -1,4 +1,6 @@ import Vue from 'vue'; +import 'jquery'; + import * as jqueryMatchers from 'custom-jquery-matchers'; import { config as testUtilsConfig } from '@vue/test-utils'; import Translate from '~/vue_shared/translate'; @@ -9,7 +11,6 @@ import customMatchers from './matchers'; import './helpers/dom_shims'; import './helpers/jquery'; -import '~/commons/jquery'; import '~/commons/bootstrap'; process.on('unhandledRejection', global.promiseRejectionHandler); diff --git a/spec/frontend/vue_shared/components/markdown/__snapshots__/suggestion_diff_spec.js.snap b/spec/frontend/vue_shared/components/markdown/__snapshots__/suggestion_diff_spec.js.snap index cdd7a3ccaf0..b8a9143bc79 100644 --- a/spec/frontend/vue_shared/components/markdown/__snapshots__/suggestion_diff_spec.js.snap +++ b/spec/frontend/vue_shared/components/markdown/__snapshots__/suggestion_diff_spec.js.snap @@ -10,6 +10,7 @@ exports[`Suggestion Diff component matches snapshot 1`] = ` helppagepath="path_to_docs" isapplyingbatch="true" isbatched="true" + suggestionscount="0" /> <table diff --git a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js index a521668b15c..b19e74b5b11 100644 --- a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js +++ b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js @@ -57,7 +57,9 @@ describe('Suggestion Diff component', () => { }); it('renders apply suggestion and add to batch buttons', () => { - createComponent(); + createComponent({ + suggestionsCount: 2, + }); const applyBtn = findApplyButton(); const addToBatchBtn = findAddToBatchButton(); @@ -104,7 +106,9 @@ describe('Suggestion Diff component', () => { describe('when add to batch is clicked', () => { it('emits addToBatch', () => { - createComponent(); + createComponent({ + suggestionsCount: 2, + }); findAddToBatchButton().vm.$emit('click'); diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb index a25bf1c4157..abf621aee10 100644 --- a/spec/helpers/groups/group_members_helper_spec.rb +++ b/spec/helpers/groups/group_members_helper_spec.rb @@ -5,6 +5,8 @@ require "spec_helper" RSpec.describe Groups::GroupMembersHelper do include MembersPresentation + let_it_be(:current_user) { create(:user) } + describe '.group_member_select_options' do let(:group) { create(:group) } @@ -20,6 +22,10 @@ RSpec.describe Groups::GroupMembersHelper do describe '#linked_groups_data_json' do include_context 'group_group_link' + before do + allow(helper).to receive(:current_user).and_return(current_user) + end + it 'matches json schema' do json = helper.linked_groups_data_json(shared_group.shared_with_group_links) @@ -28,7 +34,6 @@ RSpec.describe Groups::GroupMembersHelper do end describe '#members_data_json' do - let(:current_user) { create(:user) } let(:group) { create(:group) } before do @@ -48,6 +53,14 @@ RSpec.describe Groups::GroupMembersHelper do let(:group_member) { create(:group_member, group: group, created_by: current_user) } it_behaves_like 'group_members.json' + + context 'with user status set' do + let(:user) { create(:user) } + let!(:status) { create(:user_status, user: user) } + let(:group_member) { create(:group_member, group: group, user: user, created_by: current_user) } + + it_behaves_like 'group_members.json' + end end context 'for an invited group member' do diff --git a/spec/lib/gitlab/ci/runner/backoff_spec.rb b/spec/lib/gitlab/ci/runner/backoff_spec.rb new file mode 100644 index 00000000000..13455ccd692 --- /dev/null +++ b/spec/lib/gitlab/ci/runner/backoff_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rspec-parameterized' +require 'active_support/testing/time_helpers' + +RSpec.describe Gitlab::Ci::Runner::Backoff do + include ActiveSupport::Testing::TimeHelpers + + describe '#duration' do + it 'returns backoff duration from start' do + freeze_time do + described_class.new(5.minutes.ago).then do |backoff| + expect(backoff.duration).to eq 5.minutes + end + end + end + end + + describe '#slot' do + using RSpec::Parameterized::TableSyntax + + where(:started, :slot) do + 1 | 0 + 2 | 0 + 3 | 0 + 4 | 1 + 5 | 1 + 6 | 1 + 7 | 1 + 8 | 2 + 9 | 2 + 10 | 2 + 15 | 2 + 16 | 3 + 31 | 3 + 32 | 4 + 63 | 4 + 64 | 5 + 127 | 5 + 128 | 6 + 250 | 6 + 310 | 7 + 520 | 8 + 999 | 8 + end + + with_them do + it 'falls into an appropaite backoff slot' do + freeze_time do + backoff = described_class.new(started.seconds.ago) + expect(backoff.slot).to eq slot + end + end + end + end + + describe '#to_seconds' do + using RSpec::Parameterized::TableSyntax + + where(:started, :backoff) do + 1 | 1 + 2 | 1 + 3 | 1 + 4 | 2 + 5 | 2 + 6 | 2 + 7 | 2 + 8 | 4 + 9 | 4 + 10 | 4 + 15 | 4 + 16 | 8 + 31 | 8 + 32 | 16 + 63 | 16 + 64 | 32 + 127 | 32 + 128 | 64 + 250 | 64 + 310 | 64 + 520 | 64 + 999 | 64 + end + + with_them do + it 'calculates backoff based on an appropriate slot' do + freeze_time do + described_class.new(started.seconds.ago).then do |delay| + expect(delay.to_seconds).to eq backoff + end + end + end + end + end +end diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb index b202015464f..8198c2651a7 100644 --- a/spec/lib/gitlab/git/diff_collection_spec.rb +++ b/spec/lib/gitlab/git/diff_collection_spec.rb @@ -531,7 +531,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do let(:iterator) { [fake_diff(1, 1)] * 4 } before do - stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: 2, max_lines: max_lines }) + allow(Gitlab::Git::DiffCollection) + .to receive(:default_limits) + .and_return({ max_files: 2, max_lines: max_lines }) end it 'prunes diffs by default even little ones' do @@ -556,7 +558,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do end before do - stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: max_files, max_lines: 80 }) + allow(Gitlab::Git::DiffCollection) + .to receive(:default_limits) + .and_return({ max_files: max_files, max_lines: 80 }) end it 'prunes diffs by default even little ones' do @@ -581,7 +585,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do end before do - stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: max_files, max_lines: 80 }) + allow(Gitlab::Git::DiffCollection) + .to receive(:default_limits) + .and_return({ max_files: max_files, max_lines: 80 }) end it 'prunes diffs by default even little ones' do @@ -665,8 +671,9 @@ RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do end before do - stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', - { max_files: max_files, max_lines: 80 }) + allow(Gitlab::Git::DiffCollection) + .to receive(:default_limits) + .and_return({ max_files: max_files, max_lines: 80 }) end it 'considers size of diffs before the offset for prunning' do diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 9581b017839..f977fe1638f 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -13,6 +13,10 @@ RSpec.describe Gitlab::GitalyClient::CommitService do let(:client) { described_class.new(repository) } describe '#diff_from_parent' do + before do + stub_feature_flags(increased_diff_limits: false) + end + context 'when a commit has a parent' do it 'sends an RPC request with the parent ID as left commit' do request = Gitaly::CommitDiffRequest.new( diff --git a/spec/requests/api/ci/runner/jobs_put_spec.rb b/spec/requests/api/ci/runner/jobs_put_spec.rb index 183a3b26e00..4feb65aa3cf 100644 --- a/spec/requests/api/ci/runner/jobs_put_spec.rb +++ b/spec/requests/api/ci/runner/jobs_put_spec.rb @@ -46,64 +46,59 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do end context 'when status is given' do - it 'mark job as succeeded' do + it 'marks job as succeeded' do update_job(state: 'success') - job.reload - expect(job).to be_success + expect(job.reload).to be_success + expect(response.header).not_to have_key('X-GitLab-Trace-Update-Interval') end - it 'mark job as failed' do + it 'marks job as failed' do update_job(state: 'failed') - job.reload - expect(job).to be_failed + expect(job.reload).to be_failed expect(job).to be_unknown_failure + expect(response.header).not_to have_key('X-GitLab-Trace-Update-Interval') end context 'when failure_reason is script_failure' do before do update_job(state: 'failed', failure_reason: 'script_failure') - job.reload end - it { expect(job).to be_script_failure } + it { expect(job.reload).to be_script_failure } end context 'when failure_reason is runner_system_failure' do before do update_job(state: 'failed', failure_reason: 'runner_system_failure') - job.reload end - it { expect(job).to be_runner_system_failure } + it { expect(job.reload).to be_runner_system_failure } end context 'when failure_reason is unrecognized value' do before do update_job(state: 'failed', failure_reason: 'what_is_this') - job.reload end - it { expect(job).to be_unknown_failure } + it { expect(job.reload).to be_unknown_failure } end context 'when failure_reason is job_execution_timeout' do before do update_job(state: 'failed', failure_reason: 'job_execution_timeout') - job.reload end - it { expect(job).to be_job_execution_timeout } + it { expect(job.reload).to be_job_execution_timeout } end context 'when failure_reason is unmet_prerequisites' do before do update_job(state: 'failed', failure_reason: 'unmet_prerequisites') - job.reload end - it { expect(job).to be_unmet_prerequisites } + it { expect(job.reload).to be_unmet_prerequisites } end context 'when unmigrated live trace chunks exist' do @@ -119,6 +114,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do expect(job.pending_state).to be_present expect(response).to have_gitlab_http_status(:accepted) + expect(response.header['X-GitLab-Trace-Update-Interval']).to be > 0 end end @@ -151,6 +147,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do expect(job.reload).to be_success expect(job.pending_state).not_to be_present expect(response).to have_gitlab_http_status(:ok) + expect(response.header).not_to have_key('X-GitLab-Trace-Update-Interval') end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 2757c56e0fe..0e5fa24ad66 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1140,7 +1140,7 @@ RSpec.describe API::MergeRequests do context 'when a merge request has more than the changes limit' do it "returns a string indicating that more changes were made" do - stub_const('Commit::DIFF_HARD_LIMIT_FILES', 5) + allow(Commit).to receive(:diff_hard_limit_files).and_return(5) merge_request_overflow = create(:merge_request, :simple, author: user, diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 36707f32d04..45bce8c8a5c 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -402,7 +402,9 @@ RSpec.describe API::Repositories do end it "returns an empty string when the diff overflows" do - stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: 2, max_lines: 2 }) + allow(Gitlab::Git::DiffCollection) + .to receive(:default_limits) + .and_return({ max_files: 2, max_lines: 2 }) get api(route, current_user), params: { from: 'master', to: 'feature' } diff --git a/spec/serializers/group_group_link_entity_spec.rb b/spec/serializers/group_group_link_entity_spec.rb index 8384563e3e6..9affe4af381 100644 --- a/spec/serializers/group_group_link_entity_spec.rb +++ b/spec/serializers/group_group_link_entity_spec.rb @@ -5,9 +5,27 @@ require 'spec_helper' RSpec.describe GroupGroupLinkEntity do include_context 'group_group_link' - subject(:json) { described_class.new(group_group_link).to_json } + let_it_be(:current_user) { create(:user) } + let(:entity) { described_class.new(group_group_link) } + + before do + allow(entity).to receive(:current_user).and_return(current_user) + end it 'matches json schema' do - expect(json).to match_schema('entities/group_group_link') + expect(entity.to_json).to match_schema('entities/group_group_link') + end + + context 'a user with :admin_group_member permissions' do + before do + allow(entity).to receive(:can?).with(current_user, :admin_group_member, shared_group).and_return(true) + end + + it 'sets `can_update` and `can_remove` to `true`' do + json = entity.as_json + + expect(json[:can_update]).to be true + expect(json[:can_remove]).to be true + end end end diff --git a/spec/services/ci/update_build_state_service_spec.rb b/spec/services/ci/update_build_state_service_spec.rb index f5ad732bf7e..333231cbd83 100644 --- a/spec/services/ci/update_build_state_service_spec.rb +++ b/spec/services/ci/update_build_state_service_spec.rb @@ -100,6 +100,12 @@ RSpec.describe Ci::UpdateBuildStateService do expect(result.status).to eq 200 end + it 'does not set a backoff value' do + result = subject.execute + + expect(result.backoff).to be_nil + end + it 'increments trace finalized operation metric' do execute_with_stubbed_metrics! @@ -126,6 +132,12 @@ RSpec.describe Ci::UpdateBuildStateService do expect(result.status).to eq 202 end + it 'sets a request backoff value' do + result = subject.execute + + expect(result.backoff.to_i).to be > 0 + end + it 'schedules live chunks for migration' do expect(Ci::BuildTraceChunkFlushWorker) .to receive(:perform_async) diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb index 2cfd47634ca..43060e571a9 100644 --- a/spec/support/helpers/wait_for_requests.rb +++ b/spec/support/helpers/wait_for_requests.rb @@ -48,17 +48,10 @@ module WaitForRequests def finished_all_js_requests? return true unless javascript_test? - finished_all_ajax_requests? && - finished_all_axios_requests? - end - - def finished_all_axios_requests? - Capybara.page.evaluate_script('window.pendingRequests || 0').zero? # rubocop:disable Style/NumericPredicate + finished_all_ajax_requests? end def finished_all_ajax_requests? - return true if Capybara.page.evaluate_script('typeof jQuery === "undefined"') - - Capybara.page.evaluate_script('jQuery.active').zero? # rubocop:disable Style/NumericPredicate + Capybara.page.evaluate_script('window.pendingRequests || window.pendingRailsUJSRequests || 0').zero? # rubocop:disable Style/NumericPredicate end end diff --git a/yarn.lock b/yarn.lock index d9967c03d4e..0752825b270 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1076,6 +1076,11 @@ resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.3-1.tgz#9b9eb8858a6507162911007d355d9a206e1c5caa" integrity sha512-szFhWD+V5TAxVNVIG16klgq+ypqA5k5AecLarTTrXgOG8cawVbQdOAwLbCmzkwiQ60rGSxAFoC1u2LrzxSK2Aw== +"@rails/ujs@^6.0.3-2": + version "6.0.3-2" + resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.0.3-2.tgz#e14c1f29086858215ce7ccd9ad6d8888c458b4a3" + integrity sha512-WcpIEftNCfGDEgk6KerOugiet75Mir5q/HT1yt3dDhpBI91BaZ15lfSQIsZwMw2nyeDz9A9QBz8dAFAd4gXIzg== + "@sentry/browser@^5.22.3": version "5.22.3" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.22.3.tgz#7a64bd1cf01bf393741a3e4bf35f82aa927f5b4e" @@ -7068,13 +7073,6 @@ jmespath@0.15.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= -jquery-ujs@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.2.tgz#6a8ef1020e6b6dda385b90a4bddc128c21c56397" - integrity sha1-ao7xAg5rbdo4W5CkvdwSjCHFY5c= - dependencies: - jquery ">=1.8.0" - jquery.caret@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/jquery.caret/-/jquery.caret-0.3.1.tgz#9c093318faf327eff322e826ca9f3241368bc7b8" @@ -7085,7 +7083,7 @@ jquery.waitforimages@^2.2.0: resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b" integrity sha1-Y/IxMQVaGwYNyRPm2HS8ybnmsWs= -"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^3.5.0: +"jquery@>= 1.9.1", jquery@^3.5.0: version "3.5.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5" integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== |