Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7985071975
commit
b85aae44f9
75 changed files with 689 additions and 230 deletions
|
@ -1,5 +1,4 @@
|
|||
import './polyfills';
|
||||
import './jquery';
|
||||
import './bootstrap';
|
||||
import './vue';
|
||||
import '../lib/utils/axios_utils';
|
||||
|
|
4
app/assets/javascripts/commons/jquery.js
vendored
4
app/assets/javascripts/commons/jquery.js
vendored
|
@ -1,4 +0,0 @@
|
|||
import 'jquery';
|
||||
|
||||
// common jQuery plugins
|
||||
import 'jquery-ujs';
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
20
app/assets/javascripts/lib/utils/rails_ujs.js
Normal file
20
app/assets/javascripts/lib/utils/rails_ujs.js
Normal file
|
@ -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 };
|
|
@ -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', () => {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Display Contributor badges on notes
|
||||
merge_request: 42576
|
||||
author: Mycroft Kang @TaehyeokKang
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix clickable width of release asset links
|
||||
merge_request: 42757
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Hides batch suggestions button if there is only 1 suggestion
|
||||
merge_request: 42681
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update GitLab Runner Helm Chart to 0.21.0
|
||||
merge_request: 42844
|
||||
author:
|
||||
type: other
|
|
@ -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
|
|
@ -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
|
||||
[
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
75
lib/gitlab/ci/runner/backoff.rb
Normal file
75
lib/gitlab/ci/runner/backoff.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"],
|
||||
|
|
13
spec/fixtures/api/schemas/group_member.json
vendored
13
spec/fixtures/api/schemas/group_member.json
vendored
|
@ -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": {
|
||||
|
|
|
@ -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 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 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 2`] = `undefined`;
|
||||
|
||||
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 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 FOO STATE 2`] = `undefined`;
|
||||
exports[`GroupEmptyState given state CONNECTION_FAILED 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 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 LOADING 2`] = `undefined`;
|
||||
exports[`GroupEmptyState given state FOO STATE 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 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 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 LOADING renders the slotted content 1`] = `<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 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 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 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 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 UNKNOWN_ERROR 2`] = `undefined`;
|
||||
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 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 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 given state UNKNOWN_ERROR renders the slotted content 1`] = `<div />`;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -10,6 +10,7 @@ exports[`Suggestion Diff component matches snapshot 1`] = `
|
|||
helppagepath="path_to_docs"
|
||||
isapplyingbatch="true"
|
||||
isbatched="true"
|
||||
suggestionscount="0"
|
||||
/>
|
||||
|
||||
<table
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
96
spec/lib/gitlab/ci/runner/backoff_spec.rb
Normal file
96
spec/lib/gitlab/ci/runner/backoff_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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' }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
14
yarn.lock
14
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==
|
||||
|
|
Loading…
Reference in a new issue