Commit 4ee20b8e authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 7812b4de 84bd4dff
......@@ -39,6 +39,8 @@ import initPersistentUserCallouts from './persistent_user_callouts';
import { initUserTracking, initDefaultTrackers } from './tracking';
import { __ } from './locale';
import * as tooltips from '~/tooltips';
import 'ee_else_ce/main_ee';
applyGitLabUIConfig();
......@@ -77,7 +79,7 @@ document.addEventListener('beforeunload', () => {
// Unbind scroll events
$(document).off('scroll');
// Close any open tooltips
$('.has-tooltip, [data-toggle="tooltip"]').tooltip('dispose');
tooltips.dispose(document.querySelectorAll('.has-tooltip, [data-toggle="tooltip"]'));
// Close any open popover
$('[data-toggle="popover"]').popover('dispose');
});
......@@ -133,8 +135,10 @@ function deferredInitialisation() {
addSelectOnFocusBehaviour('.js-select-on-focus');
$('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() {
tooltips.dispose(this);
// eslint-disable-next-line no-jquery/no-fade
$(this)
.tooltip('dispose')
.closest('li')
.fadeOut();
});
......@@ -154,7 +158,7 @@ function deferredInitialisation() {
const delay = glTooltipDelay ? JSON.parse(glTooltipDelay) : 0;
// Initialize tooltips
$body.tooltip({
tooltips.initTooltips({
selector: '.has-tooltip, [data-toggle="tooltip"]',
trigger: 'hover',
boundary: 'viewport',
......
......@@ -3,11 +3,12 @@
import $ from 'jquery';
import axios from './lib/utils/axios_utils';
import { __ } from '~/locale';
import eventHub from '~/vue_merge_request_widget/event_hub';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import TaskList from './task_list';
import MergeRequestTabs from './merge_request_tabs';
import IssuablesHelper from './helpers/issuables_helper';
import { addDelimiter } from './lib/utils/text_utility';
import { getParameterValues, setUrlParams } from './lib/utils/url_utility';
function MergeRequest(opts) {
// Initialize MergeRequest behavior
......@@ -23,7 +24,6 @@ function MergeRequest(opts) {
this.initTabs();
this.initMRBtnListeners();
this.initCommitMessageListeners();
this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport();
if ($('.description.js-task-list-container').length) {
this.taskList = new TaskList({
......@@ -66,13 +66,38 @@ MergeRequest.prototype.showAllCommits = function() {
MergeRequest.prototype.initMRBtnListeners = function() {
const _this = this;
const draftToggles = document.querySelectorAll('.js-draft-toggle-button');
$('.report-abuse-link').on('click', e => {
// this is needed because of the implementation of
// the dropdown toggle and Report Abuse needing to be
// linked to another page.
e.stopPropagation();
if (draftToggles.length) {
draftToggles.forEach(draftToggle => {
draftToggle.addEventListener('click', e => {
e.preventDefault();
e.stopImmediatePropagation();
const url = draftToggle.href;
const wipEvent = getParameterValues('merge_request[wip_event]', url)[0];
const mobileDropdown = draftToggle.closest('.dropdown.show');
if (mobileDropdown) {
$(mobileDropdown.firstElementChild).dropdown('toggle');
}
draftToggle.setAttribute('disabled', 'disabled');
axios
.put(draftToggle.href, null, { params: { format: 'json' } })
.then(({ data }) => {
draftToggle.removeAttribute('disabled');
eventHub.$emit('MRWidgetUpdateRequested');
MergeRequest.toggleDraftStatus(data.title, wipEvent === 'unwip');
})
.catch(() => {
draftToggle.removeAttribute('disabled');
createFlash(__('Something went wrong. Please try again.'));
});
});
});
}
return $('.btn-close, .btn-reopen').on('click', function(e) {
const $this = $(this);
......@@ -89,8 +114,6 @@ MergeRequest.prototype.initMRBtnListeners = function() {
return;
}
if (this.closeReopenReportToggle) this.closeReopenReportToggle.setDisable();
if (shouldSubmit) {
if ($this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')) {
e.preventDefault();
......@@ -151,14 +174,35 @@ MergeRequest.hideCloseButton = function() {
const closeDropdownItem = el.querySelector('li.close-item');
if (closeDropdownItem) {
closeDropdownItem.classList.add('hidden');
// Selects the next dropdown item
el.querySelector('li.report-item').click();
} else {
// No dropdown just hide the Close button
el.querySelector('.btn-close').classList.add('hidden');
}
// Dropdown for mobile screen
el.querySelector('li.js-close-item').classList.add('hidden');
};
MergeRequest.toggleDraftStatus = function(title, isReady) {
if (isReady) {
createFlash(__('The merge request can now be merged.'), 'notice');
}
const titleEl = document.querySelector('.merge-request .detail-page-description .title');
if (titleEl) {
titleEl.textContent = title;
}
const draftToggles = document.querySelectorAll('.js-draft-toggle-button');
if (draftToggles.length) {
draftToggles.forEach(el => {
const draftToggle = el;
const url = setUrlParams(
{ 'merge_request[wip_event]': isReady ? 'wip' : 'unwip' },
draftToggle.href,
);
draftToggle.setAttribute('href', url);
draftToggle.textContent = isReady ? __('Mark as draft') : __('Mark as ready');
});
}
};
export default MergeRequest;
......@@ -57,6 +57,10 @@ export default {
text: s__('ProjectTemplates|Static Site Editor/Middleman'),
icon: '.template-option .icon-sse_middleman',
},
gitpod_spring_petclinic: {
text: s__('ProjectTemplates|Gitpod/Spring Petclinic'),
icon: '.template-option .icon-gitpod_spring_petclinic',
},
nfhugo: {
text: s__('ProjectTemplates|Netlify/Hugo'),
icon: '.template-option .icon-nfhugo',
......
......@@ -58,6 +58,8 @@ const applyToElements = (elements, handler) => toArray(elements).forEach(handler
const invokeBootstrapApi = (elements, method) => {
if (isFunction(elements.tooltip)) {
elements.tooltip(method);
} else {
jQuery(elements).tooltip(method);
}
};
......
......@@ -3,6 +3,7 @@ import $ from 'jquery';
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import MergeRequest from '~/merge_request';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import mergeRequestQueryVariablesMixin from '../../mixins/merge_request_query_variables';
import getStateQuery from '../../queries/get_state.query.graphql';
......@@ -128,8 +129,7 @@ export default {
.then(res => res.data)
.then(data => {
eventHub.$emit('UpdateWidgetData', data);
createFlash(__('The merge request can now be merged.'), 'notice');
$('.merge-request .detail-page-description .title').text(this.mr.title);
MergeRequest.toggleDraftStatus(this.mr.title, true);
})
.catch(() => {
this.isMakingRequest = false;
......
......@@ -35,6 +35,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
:source_branch,
:source_project_id,
:state_event,
:wip_event,
:squash,
:target_branch,
:target_project_id,
......
......@@ -342,6 +342,12 @@ module IssuablesHelper
issuable.closed? ^ should_inverse ? reopen_issuable_path(issuable) : close_issuable_path(issuable)
end
def toggle_draft_issuable_path(issuable)
wip_event = issuable.work_in_progress? ? 'unwip' : 'wip'
issuable_path(issuable, { merge_request: { wip_event: wip_event } })
end
def issuable_path(issuable, *options)
polymorphic_path(issuable, *options)
end
......
......@@ -37,7 +37,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
def remove_wip_path
if work_in_progress? && can?(current_user, :update_merge_request, merge_request.project)
if can?(current_user, :update_merge_request, merge_request.project)
remove_wip_project_merge_request_path(project, merge_request)
end
end
......
# frozen_string_literal: true
class MergeRequestBasicEntity < Grape::Entity
expose :title
expose :public_merge_status, as: :merge_status
expose :merge_error
expose :state
......
......@@ -25,6 +25,6 @@
'secondary-action': s_('AdminUsers|Block user') } }
= s_('AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues,
merge requests, and groups linked to them. To avoid data loss,
consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end},
consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd},
it cannot be undone or recovered.')
......@@ -75,12 +75,12 @@
= javascript_include_tag locale_path unless I18n.locale == :en
= webpack_bundle_tag "sentry" if Gitlab.config.sentry.enabled
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
= webpack_controller_bundle_tags
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
= webpack_bundle_tag "chrome_84_icon_fix" if browser.chrome?([">=84", "<84.0.4147.125"]) || browser.edge?([">=84", "<84.0.522.59"])
= yield :project_javascripts
......
......@@ -32,16 +32,19 @@
%ul
- if can_update_merge_request
%li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- unless current_user == @merge_request.author
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request))
- if can_update_merge_request
- unless @merge_request.closed?
%li
= link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_issuable_path(@merge_request), method: :put, class: "js-draft-toggle-button"
%li{ class: [merge_request_button_visibility(@merge_request, true), 'js-close-item'] }
= link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
- if can_reopen_merge_request
%li{ class: merge_request_button_visibility(@merge_request, false) }
= link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request'
- unless current_user == @merge_request.author
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request))
- if can_update_merge_request
= link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-sm-none d-md-block btn btn-grouped js-issuable-edit qa-edit-button"
= link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-sm-none d-md-block btn gl-button btn-grouped js-issuable-edit qa-edit-button"
= render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request, can_reopen: can_reopen_merge_request
......@@ -5,7 +5,7 @@
- if defined? warn_before_close
- add_blocked_class = warn_before_close
- if is_current_user
- if is_current_user && !issuable.is_a?(MergeRequest)
- if can_update
%button{ class: "d-none d-sm-none d-md-block btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)} #{(add_blocked_class ? 'btn-issue-blocked' : '')}",
data: { remote: 'true', endpoint: close_issuable_path(issuable), qa_selector: 'close_issue_button' } }
......@@ -16,6 +16,9 @@
= _("Reopen %{display_issuable_type}") % { display_issuable_type: display_issuable_type }
- else
- if can_update && !are_close_and_open_buttons_hidden
- if issuable.is_a?(MergeRequest)
= render 'shared/issuable/close_reopen_draft_report_toggle', issuable: issuable
- else
= render 'shared/issuable/close_reopen_report_toggle', issuable: issuable, warn_before_close: add_blocked_class
- else
= link_to _('Report abuse'), new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
......
- display_issuable_type = issuable_display_type(issuable)
- button_action_class = issuable.closed? ? 'btn-default' : 'btn-warning btn-warning-secondary'
- button_class = "btn gl-button #{!issuable.closed? && 'js-draft-toggle-button'}"
- toggle_class = "btn gl-button dropdown-toggle"
.float-left.btn-group.gl-ml-3.issuable-close-dropdown.d-none.d-md-inline-flex.js-issuable-close-dropdown
= link_to issuable.closed? ? reopen_issuable_path(issuable) : toggle_draft_issuable_path(issuable), method: :put, class: "#{button_class} #{button_action_class}" do
- if issuable.closed?
= _('Reopen')
= display_issuable_type
- else
= issuable.work_in_progress? ? _('Mark as ready') : _('Mark as draft')
- if !issuable.closed? || !issuable_author_is_current_user(issuable)
= button_tag type: 'button', class: "#{toggle_class} #{button_action_class}", data: { 'toggle' => 'dropdown' } do
%span.sr-only= _('Toggle dropdown')
= sprite_icon "angle-down", size: 12
%ul.js-issuable-close-menu.dropdown-menu.dropdown-menu-right
- if issuable.open?
%li
= link_to close_issuable_path(issuable), method: :put do
.description
%strong.title
= _('Close')
= display_issuable_type
- unless issuable_author_is_current_user(issuable)
- unless issuable.closed?
%li.divider.droplab-item-ignore
%li.report-item
%a.report-abuse-link{ href: new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)) }
.description
%strong.title= _('Report abuse')
%p.text
= _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize }
---
title: Fix verifying LFS uploads with GitHub
merge_request: 43852
author:
type: fixed
---
title: "Add Gitpod Spring Petclinic to Project Templates"
merge_request: 43319
author:
type: added
---
title: Fix Delete User dialog formatted strings
merge_request: 43871
author:
type: fixed
---
title: Update node-sass from 4.12.0 to 4.14.1
merge_request: 43808
author: Takuya Noguchi
type: other
---
title: Adds button to update merge request draft status on merge request show page
merge_request: 43098
author:
type: added
......@@ -19,6 +19,7 @@
- auto_devops
- backup_restore
- behavior_analytics
- boards
- chatops
- cloud_native_installation
- cluster_cost_optimization
......@@ -61,15 +62,15 @@
- importers
- incident_management
- infrastructure_as_code
- insights
- instance_statistics
- integrations
- interactive_application_security_testing
- internationalization
- instance_statistics
- issue_tracking
- jenkins_importer
- jira_importer
- jupyter_notebooks
- kanban_boards
- kubernetes_management
- license_compliance
- live_preview
......@@ -82,6 +83,7 @@
- omnibus_package
- package_registry
- pages
- pipeline_authoring
- pki_management
- planning_analytics
- product_analytics
......
......@@ -267,6 +267,7 @@ POST /groups/:id/epics
| `labels` | string | no | The comma separated list of labels |
| `description` | string | no | The description of the epic. Limited to 1,048,576 characters. |
| `confidential` | boolean | no | Whether the epic should be confidential |
| `created_at` | string | no | When the epic was created. Date time string, ISO 8601 formatted, for example `2016-03-11T03:45:40Z` . Requires administrator or project/group owner privileges ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/255309) in GitLab 13.5) |
| `start_date_is_fixed` | boolean | no | Whether start date should be sourced from `start_date_fixed` or from milestones (since 11.3) |
| `start_date_fixed` | string | no | The fixed start date of an epic (since 11.3) |
| `due_date_is_fixed` | boolean | no | Whether due date should be sourced from `due_date_fixed` or from milestones (since 11.3) |
......@@ -349,6 +350,7 @@ PUT /groups/:id/epics/:epic_iid
| `description` | string | no | The description of an epic. Limited to 1,048,576 characters. |
| `confidential` | boolean | no | Whether the epic should be confidential |
| `labels` | string | no | The comma separated list of labels |
| `updated_at` | string | no | When the epic was updated. Date time string, ISO 8601 formatted, for example `2016-03-11T03:45:40Z` . Requires administrator or project/group owner privileges ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/255309) in GitLab 13.5) |
| `start_date_is_fixed` | boolean | no | Whether start date should be sourced from `start_date_fixed` or from milestones (since 11.3) |
| `start_date_fixed` | string | no | The fixed start date of an epic (since 11.3) |
| `due_date_is_fixed` | boolean | no | Whether due date should be sourced from `due_date_fixed` or from milestones (since 11.3) |
......
......@@ -127,6 +127,7 @@ field).
| title | `issue[title]` | |
| description | `issue[description]` | |
| description template | `issuable_template` | |
| issue type | `issue[issue_type]` | Either `incident` or `issue` |
| confidential | `issue[confidential]` | Parameter value must be `true` to set to confidential |
Follow these examples to form your new issue URL with prefilled fields.
......
......@@ -223,6 +223,7 @@ export default {
first: this.$options.PROJECTS_PER_PAGE,
after: pageInfo.endCursor,
searchNamespaces: true,
sort: 'similarity',
},
});
},
......
......@@ -122,10 +122,15 @@ module EE
enable :admin_wiki
end
rule { owner }.policy do
rule { owner | admin }.policy do
enable :owner_access
end
rule { can?(:owner_access) }.policy do
enable :set_epic_created_at
enable :set_epic_updated_at
end
rule { can?(:read_cluster) & cluster_deployments_available }
.enable :read_cluster_environments
......
---
title: Use similarity sort when searching projects from Security Dashboard
merge_request: 43610
author:
type: changed
---
title: Allow created_at and updated_at to be set through Epics API
merge_request: 43279
author: stingrayza
type: added
......@@ -70,6 +70,7 @@ module API
requires :title, type: String, desc: 'The title of an epic'
optional :description, type: String, desc: 'The description of an epic'
optional :confidential, type: Boolean, desc: 'Indicates if the epic is confidential'
optional :created_at, type: DateTime, desc: 'Date time when the epic was created. Available only for admins and project owners.'
optional :start_date, as: :start_date_fixed, type: String, desc: 'The start date of an epic'
optional :start_date_is_fixed, type: Boolean, desc: 'Indicates start date should be sourced from start_date_fixed field not the issue milestones'
optional :end_date, as: :due_date_fixed, type: String, desc: 'The due date of an epic'
......@@ -80,6 +81,9 @@ module API
post ':id/(-/)epics' do
authorize_can_create!
# Setting created_at is allowed only for admins and owners
params.delete(:created_at) unless current_user.can?(:set_epic_created_at, user_group)
epic = ::Epics::CreateService.new(user_group, current_user, declared_params(include_missing: false)).execute
if epic.valid?
present epic, epic_options
......@@ -96,6 +100,7 @@ module API
optional :title, type: String, desc: 'The title of an epic'
optional :description, type: String, desc: 'The description of an epic'
optional :confidential, type: Boolean, desc: 'Indicates if the epic is confidential'
optional :updated_at, type: DateTime, desc: 'Date time when the epic was updated. Available only for admins and project owners.'
optional :start_date, as: :start_date_fixed, type: String, desc: 'The start date of an epic'
optional :start_date_is_fixed, type: Boolean, desc: 'Indicates start date should be sourced from start_date_fixed field not the issue milestones'
optional :end_date, as: :due_date_fixed, type: String, desc: 'The due date of an epic'
......@@ -108,6 +113,10 @@ module API
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/194104')
authorize_can_admin_epic!
# Setting updated_at is allowed only for admins and owners
params.delete(:updated_at) unless current_user.can?(:set_epic_updated_at, user_group)
update_params = declared_params(include_missing: false)
update_params.delete(:epic_iid)
......
......@@ -87,6 +87,7 @@ describe('Project Manager component', () => {
first: wrapper.vm.$options.PROJECTS_PER_PAGE,
after: '',
searchNamespaces: true,
sort: 'similarity',
},
});
});
......
......@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Graphql::Aggregations::Vulnerabilities::LazyUserNotesCoun
it 'uses lazy_user_notes_count_aggregate to collect aggregates' do
subject = described_class.new({ lazy_user_notes_count_aggregate: { pending_vulnerability_ids: [10, 20, 30].to_set, loaded_objects: {} } }, vulnerability)
expect(subject.lazy_state[:pending_vulnerability_ids]).to match [10, 20, 30, vulnerability.id]
expect(subject.lazy_state[:pending_vulnerability_ids]).to match_array [10, 20, 30, vulnerability.id]
expect(subject.vulnerability).to match vulnerability
end
end
......
......@@ -22,6 +22,12 @@ RSpec.describe GroupPolicy do
it { is_expected.to be_allowed(:read_epic, :create_epic, :admin_epic, :destroy_epic, :read_confidential_epic, :destroy_epic_link) }
end
context 'when user is admin' do
let(:current_user) { admin }
it { is_expected.to be_allowed(:read_epic, :create_epic, :admin_epic, :destroy_epic, :read_confidential_epic, :destroy_epic_link) }
end
context 'when user is maintainer' do
let(:current_user) { maintainer }
......
......@@ -610,6 +610,39 @@ RSpec.describe API::Epics do
end
end
context 'setting created_at' do
let(:creation_time) { 2.weeks.ago }
let(:params) { { title: 'new epic', created_at: creation_time } }
it 'sets the creation time on the new epic if the user is an admin' do
admin = create(:user, :admin)
post api(url, admin), params: params
expect(response).to have_gitlab_http_status(:created)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
end
it 'sets the creation time on the new epic if the user is a group owner' do
group.add_owner(user)
post api(url, user), params: params
expect(response).to have_gitlab_http_status(:created)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
end
it 'ignores the given creation time if the user is another user' do
user2 = create(:user)
group.add_developer(user2)
post api(url, user2), params: params
expect(response).to have_gitlab_http_status(:created)
expect(Time.parse(json_response['created_at'])).not_to be_like_time(creation_time)
end
end
it 'creates a new epic with labels param as array' do
params[:labels] = ['label1', 'label2', 'foo, bar', '&,?']
......@@ -765,6 +798,38 @@ RSpec.describe API::Epics do
end
end
context 'setting updated_at' do
let(:update_time) { 1.week.ago }
it 'ignores the given update time when run by another user' do
user2 = create(:user)
group.add_developer(user2)
put api(url, user2), params: { title: 'updated by other user', updated_at: update_time }
expect(response).to have_gitlab_http_status(:ok)
expect(Time.parse(json_response['updated_at'])).not_to be_like_time(update_time)
end
it 'sets the update time on the epic when run by an admin' do
admin = create(:user, :admin)
put api(url, admin), params: { title: 'updated by admin', updated_at: update_time }
expect(response).to have_gitlab_http_status(:ok)
expect(Time.parse(json_response['updated_at'])).to be_like_time(update_time)
end
it 'sets the update time on the epic when run by a group owner' do
group.add_owner(user)
put api(url, user), params: { title: 'updated by owner', updated_at: update_time }
expect(response).to have_gitlab_http_status(:ok)
expect(Time.parse(json_response['updated_at'])).to be_like_time(update_time)
end
end
context 'when deprecated start_date and end_date params are present' do
let(:epic) { create(:epic, :use_fixed_dates, group: group) }
let(:new_start_date) { epic.start_date + 1.day }
......
......@@ -17,11 +17,9 @@ RSpec.describe API::ProjectClusters do
stub_kubeclient_get_secret(
api_url,
{
metadata_name: "#{namespace}-token",
token: Base64.encode64('sample-token'),
namespace: namespace
}
)
stub_kubeclient_put_secret(api_url, "#{namespace}-token", namespace: namespace)
......
......@@ -7,9 +7,11 @@ module Gitlab
# * https://github.com/git-lfs/git-lfs/blob/master/docs/api/basic-transfers.md
class Client
GIT_LFS_CONTENT_TYPE = 'application/vnd.git-lfs+json'
GIT_LFS_USER_AGENT = "GitLab #{Gitlab::VERSION} LFS client"
DEFAULT_HEADERS = {
'Accept' => GIT_LFS_CONTENT_TYPE,
'Content-Type' => GIT_LFS_CONTENT_TYPE
'Content-Type' => GIT_LFS_CONTENT_TYPE,
'User-Agent' => GIT_LFS_USER_AGENT
}.freeze
attr_reader :base_url
......@@ -53,7 +55,8 @@ module Gitlab
body_stream: file,
headers: {
'Content-Length' => object.size.to_s,
'Content-Type' => 'application/octet-stream'
'Content-Type' => 'application/octet-stream',
'User-Agent' => GIT_LFS_USER_AGENT
}.merge(upload_action['header'] || {})
}
......
......@@ -52,6 +52,7 @@ module Gitlab
ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook.'), 'https://gitlab.com/pages/gitbook', 'illustrations/logos/gitbook.svg'),
ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo', 'illustrations/logos/hexo.svg'),
ProjectTemplate.new('sse_middleman', 'Static Site Editor/Middleman', _('Middleman project with Static Site Editor support'), 'https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman'),
ProjectTemplate.new('gitpod_spring_petclinic', 'Gitpod/Spring Petclinic', _('A Gitpod configured Webapplication in Spring and Java'), 'https://gitlab.com/gitlab-org/project-templates/gitpod-spring-petclinic', 'illustrations/logos/gitpod.svg'),
ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'),
ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'),
ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'),
......
......@@ -1182,6 +1182,9 @@ msgstr ""
msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
msgstr ""
msgid "A Gitpod configured Webapplication in Spring and Java"
msgstr ""
msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
msgstr ""
......@@ -2165,7 +2168,7 @@ msgstr ""
msgid "AdminUsers|You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered."
msgstr ""
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered."
msgstr ""
msgid "AdminUsers|You cannot remove your own admin rights."
......@@ -15453,6 +15456,12 @@ msgstr ""
msgid "Mark as done"
msgstr ""
msgid "Mark as draft"
msgstr ""
msgid "Mark as ready"
msgstr ""
msgid "Mark as resolved"
msgstr ""
......@@ -20255,6 +20264,9 @@ msgstr ""
msgid "ProjectTemplates|GitLab Cluster Management"
msgstr ""
msgid "ProjectTemplates|Gitpod/Spring Petclinic"
msgstr ""
msgid "ProjectTemplates|Go Micro"
msgstr ""
......
......@@ -206,7 +206,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.5.0' do
before do
prepare_knative_stubs(knative_05_service(knative_stub_options))
prepare_knative_stubs(knative_05_service(**knative_stub_options))
end
include_examples 'GET #show with valid data'
......@@ -214,7 +214,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.6.0' do
before do
prepare_knative_stubs(knative_06_service(knative_stub_options))
prepare_knative_stubs(knative_06_service(**knative_stub_options))
end
include_examples 'GET #show with valid data'
......@@ -222,7 +222,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.7.0' do
before do
prepare_knative_stubs(knative_07_service(knative_stub_options))
prepare_knative_stubs(knative_07_service(**knative_stub_options))
end
include_examples 'GET #show with valid data'
......@@ -230,7 +230,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.9.0' do
before do
prepare_knative_stubs(knative_09_service(knative_stub_options))
prepare_knative_stubs(knative_09_service(**knative_stub_options))
end
include_examples 'GET #show with valid data'
......@@ -275,7 +275,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.5.0' do
before do
prepare_knative_stubs(knative_05_service(knative_stub_options))
prepare_knative_stubs(knative_05_service(**knative_stub_options))
end
include_examples 'GET #index with data'
......@@ -283,7 +283,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.6.0' do
before do
prepare_knative_stubs(knative_06_service(knative_stub_options))
prepare_knative_stubs(knative_06_service(**knative_stub_options))
end
include_examples 'GET #index with data'
......@@ -291,7 +291,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.7.0' do
before do
prepare_knative_stubs(knative_07_service(knative_stub_options))
prepare_knative_stubs(knative_07_service(**knative_stub_options))
end
include_examples 'GET #index with data'
......@@ -299,7 +299,7 @@ RSpec.describe Projects::Serverless::FunctionsController do
context 'on Knative 0.9.0' do
before do
prepare_knative_stubs(knative_09_service(knative_stub_options))
prepare_knative_stubs(knative_09_service(**knative_stub_options))
end
include_examples 'GET #index with data'
......
......@@ -23,7 +23,15 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
expect(container).to have_content("Close #{human_model_name}")
expect(container).to have_content('Report abuse')
expect(container).to have_content("Report #{human_model_name.pluralize} that are abusive, inappropriate or spam.")
if issuable.is_a?(MergeRequest)
page.within('.js-issuable-close-dropdown') do
expect(page).to have_link('Close merge request')
end
else
expect(container).to have_selector('.close-item.droplab-item-selected')
end
expect(container).to have_selector('.report-item')
expect(container).not_to have_selector('.report-item.droplab-item-selected')
expect(container).not_to have_selector('.reopen-item')
......@@ -123,7 +131,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
it 'shows only the `Edit` button' do
expect(page).to have_link('Edit')
expect(page).not_to have_link('Report abuse')
expect(page).to have_link('Report abuse')
expect(page).not_to have_button('Close merge request')
expect(page).not_to have_button('Reopen merge request')
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Merge request > User marks merge request as draft', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
before do
project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(project, merge_request)
end
it 'toggles draft status' do
click_link 'Mark as draft'
expect(page).to have_content("Draft: #{merge_request.title}")
page.within('.detail-page-header-actions') do
click_link 'Mark as ready'
end
expect(page).to have_content(merge_request.title)
end
end
......@@ -15,7 +15,11 @@ RSpec.describe 'User reopens a merge requests', :js do
end
it 'reopens a merge request' do
click_button('Reopen merge request', match: :first)
find('.js-issuable-close-dropdown .dropdown-toggle').click
click_link('Reopen merge request', match: :first)
wait_for_requests
page.within('.status-box') do
expect(page).to have_content('Open')
......
......@@ -35,7 +35,9 @@ RSpec.describe 'Merge request > User resolves Work in Progress', :js do
expect(page.find('.ci-widget-content')).to have_content("Pipeline ##{pipeline.id}")
expect(page).to have_content "This merge request is still a work in progress."
page.within('.mr-state-widget') do
click_button('Mark as ready')
end
wait_for_requests
......
{
"type": "object",
"properties" : {
"title": { "type": "string" },
"state": { "type": "string" },
"merge_status": { "type": "string" },
"source_branch_exists": { "type": "boolean" },
......
......@@ -6,11 +6,13 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
include ApiHelpers
include JavaScriptFixturesHelpers
let_it_be(:admin) { create(:admin) }
let_it_be(:project) { create(:project, :repository, path: 'releases-project') }
let_it_be(:admin) { create(:admin, username: 'administrator', email: 'admin@example.gitlab.com') }
let_it_be(:namespace) { create(:namespace, path: 'releases-namespace') }
let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'releases-project') }
let_it_be(:milestone_12_3) do
create(:milestone,
id: 123,
project: project,
title: '12.3',
description: 'The 12.3 milestone',
......@@ -20,6 +22,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
let_it_be(:milestone_12_4) do
create(:milestone,
id: 124,
project: project,
title: '12.4',
description: 'The 12.4 milestone',
......@@ -45,18 +48,25 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
let_it_be(:release) do
create(:release,
:with_evidence,
milestones: [milestone_12_3, milestone_12_4],
project: project,
tag: 'v1.1',
name: 'The first release',
author: admin,
description: 'Best. Release. **Ever.** :rocket:',
created_at: Time.zone.parse('2018-12-3'),
released_at: Time.zone.parse('2018-12-10'))
end
let_it_be(:evidence) do
create(:evidence,
release: release,
collected_at: Time.zone.parse('2018-12-03'))
end
let_it_be(:other_link) do
create(:release_link,
id: 10,
release: release,
name: 'linux-amd64 binaries',
filepath: '/binaries/linux-amd64',
......@@ -65,6 +75,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
let_it_be(:runbook_link) do
create(:release_link,
id: 11,
release: release,
name: 'Runbook',
url: "#{release.project.web_url}/runbook",
......@@ -73,6 +84,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
let_it_be(:package_link) do
create(:release_link,
id: 12,
release: release,
name: 'Package',
url: 'https://example.com/package',
......@@ -81,6 +93,7 @@ RSpec.describe 'Releases (JavaScript fixtures)' do
let_it_be(:image_link) do
create(:release_link,
id: 13,
release: release,
name: 'Image',
url: 'https://example.com/image',
......
......@@ -3,8 +3,6 @@ import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils';
import MergeRequest from '~/merge_request';
import CloseReopenReportToggle from '~/close_reopen_report_toggle';
import IssuablesHelper from '~/helpers/issuables_helper';
describe('MergeRequest', () => {
const test = {};
......@@ -112,66 +110,7 @@ describe('MergeRequest', () => {
});
});
describe('class constructor', () => {
beforeEach(() => {
jest.spyOn($, 'ajax').mockImplementation();
});
it('calls .initCloseReopenReport', () => {
jest.spyOn(IssuablesHelper, 'initCloseReopenReport').mockImplementation(() => {});
new MergeRequest(); // eslint-disable-line no-new
expect(IssuablesHelper.initCloseReopenReport).toHaveBeenCalled();
});
it('calls .initDroplab', () => {
const container = {
querySelector: jest.fn().mockName('container.querySelector'),
};
const dropdownTrigger = {};
const dropdownList = {};
const button = {};
jest.spyOn(CloseReopenReportToggle.prototype, 'initDroplab').mockImplementation(() => {});
jest.spyOn(document, 'querySelector').mockReturnValue(container);
container.querySelector
.mockReturnValueOnce(dropdownTrigger)
.mockReturnValueOnce(dropdownList)
.mockReturnValueOnce(button);
new MergeRequest(); // eslint-disable-line no-new
expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown');
expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle');
expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu');
expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button');
expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled();
});
});
describe('hideCloseButton', () => {
describe('merge request of another user', () => {
beforeEach(() => {
loadFixtures('merge_requests/merge_request_with_task_list.html');
test.el = document.querySelector('.js-issuable-actions');
new MergeRequest(); // eslint-disable-line no-new
MergeRequest.hideCloseButton();
});
it('hides the dropdown close item and selects the next item', () => {
const closeItem = test.el.querySelector('li.close-item');
const smallCloseItem = test.el.querySelector('.js-close-item');
const reportItem = test.el.querySelector('li.report-item');
expect(closeItem).toHaveClass('hidden');
expect(smallCloseItem).toHaveClass('hidden');
expect(reportItem).toHaveClass('droplab-item-selected');
expect(reportItem).not.toHaveClass('hidden');
});
});
describe('merge request of current_user', () => {
beforeEach(() => {
loadFixtures('merge_requests/merge_request_of_current_user.html');
......@@ -180,10 +119,8 @@ describe('MergeRequest', () => {
});
it('hides the close button', () => {
const closeButton = test.el.querySelector('.btn-close');
const smallCloseItem = test.el.querySelector('.js-close-item');
expect(closeButton).toHaveClass('hidden');
expect(smallCloseItem).toHaveClass('hidden');
});
});
......
......@@ -5,115 +5,123 @@ Object {
"data": Array [
Object {
"_links": Object {
"editUrl": "http://0.0.0.0:3000/root/release-test/-/releases/v5.10/edit",
"issuesUrl": null,
"mergeRequestsUrl": null,
"self": "http://0.0.0.0:3000/root/release-test/-/releases/v5.10",
"selfUrl": "http://0.0.0.0:3000/root/release-test/-/releases/v5.10",
"editUrl": "http://localhost/releases-namespace/releases-project/-/releases/v1.1/edit",
"issuesUrl": "http://localhost/releases-namespace/releases-project/-/issues?release_tag=v1.1&scope=all&state=opened",
"mergeRequestsUrl": "http://localhost/releases-namespace/releases-project/-/merge_requests?release_tag=v1.1&scope=all&state=opened",
"self": "http://localhost/releases-namespace/releases-project/-/releases/v1.1",
"selfUrl": "http://localhost/releases-namespace/releases-project/-/releases/v1.1",
},
"assets": Object {
"count": 7,
"count": 8,
"links": Array [
Object {
"directAssetUrl": "http://0.0.0.0:3000/root/release-test/-/releases/v5.32/permanent/path/to/runbook",
"directAssetUrl": "http://localhost/releases-namespace/releases-project/-/releases/v1.1/binaries/awesome-app-3",
"external": true,
"id": "gid://gitlab/Releases::Link/69",
"linkType": "other",
"name": "An example link",
"url": "https://example.com/link",
"id": "gid://gitlab/Releases::Link/13",
"linkType": "image",
"name": "Image",
"url": "https://example.com/image",
},
Object {
"directAssetUrl": "https://example.com/package",
"directAssetUrl": "http://localhost/releases-namespace/releases-project/-/releases/v1.1/binaries/awesome-app-2",
"external": true,
"id": "gid://gitlab/Releases::Link/68",
"id": "gid://gitlab/Releases::Link/12",
"linkType": "package",
"name": "An example package link",
"name": "Package",
"url": "https://example.com/package",
},
Object {
"directAssetUrl": "https://example.com/image",
"directAssetUrl": "http://localhost/releases-namespace/releases-project/-/releases/v1.1/binaries/awesome-app-1",
"external": false,
"id": "gid://gitlab/Releases::Link/11",
"linkType": "runbook",
"name": "Runbook",
"url": "http://localhost/releases-namespace/releases-project/runbook",
},
Object {
"directAssetUrl": "http://localhost/releases-namespace/releases-project/-/releases/v1.1/binaries/linux-amd64",
"external": true,
"id": "gid://gitlab/Releases::Link/67",
"linkType": "image",
"name": "An example image",
"url": "https://example.com/image",
"id": "gid://gitlab/Releases::Link/10",
"linkType": "other",
"name": "linux-amd64 binaries",
"url": "https://downloads.example.com/bin/gitlab-linux-amd64",
},
],
"sources": Array [
Object {
"format": "zip",
"url": "http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.zip",
"url": "http://localhost/releases-namespace/releases-project/-/archive/v1.1/releases-project-v1.1.zip",
},
Object {
"format": "tar.gz",
"url": "http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.tar.gz",
"url": "http://localhost/releases-namespace/releases-project/-/archive/v1.1/releases-project-v1.1.tar.gz",
},
Object {
"format": "tar.bz2",
"url": "http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.tar.bz2",
"url": "http://localhost/releases-namespace/releases-project/-/archive/v1.1/releases-project-v1.1.tar.bz2",
},
Object {
"format": "tar",
"url": "http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.tar",
"url": "http://localhost/releases-namespace/releases-project/-/archive/v1.1/releases-project-v1.1.tar",
},
],
},
"author": Object {
"avatarUrl": "/uploads/-/system/user/avatar/1/avatar.png",
"username": "root",
"webUrl": "http://0.0.0.0:3000/root",
"avatarUrl": "https://www.gravatar.com/avatar/16f8e2050ce10180ca571c2eb19cfce2?s=80&d=identicon",
"username": "administrator",
"webUrl": "http://localhost/administrator",
},
"commit": Object {
"shortId": "92e7ea2e",
"title": "Testing a change.",
"shortId": "b83d6e39",
"title": "Merge branch 'branch-merged' into 'master'",
},
"commitPath": "http://0.0.0.0:3000/root/release-test/-/commit/92e7ea2ee4496fe0d00ff69830ba0564d3d1e5a7",
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:24\\" dir=\\"auto\\">This is version <strong>1.0</strong>!</p>",
"commitPath": "http://localhost/releases-namespace/releases-project/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0",
"descriptionHtml": "<p data-sourcepos=\\"1:1-1:33\\" dir=\\"auto\\">Best. Release. <strong>Ever.</strong> <gl-emoji title=\\"rocket\\" data-name=\\"rocket\\" data-unicode-version=\\"6.0\\">🚀</gl-emoji></p>",
"evidences": Array [
Object {
"collectedAt": "2020-08-21T20:15:19Z",
"filepath": "http://0.0.0.0:3000/root/release-test/-/releases/v5.10/evidences/34.json",
"sha": "22bde8e8b93d870a29ddc339287a1fbb598f45d1396d",
"collectedAt": "2018-12-03T00:00:00Z",
"filepath": "http://localhost/releases-namespace/releases-project/-/releases/v1.1/evidences/1.json",
"sha": "760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d",
},
],
"milestones": Array [
Object {
"description": "",
"id": "gid://gitlab/Milestone/60",
"description": "The 12.4 milestone",
"id": "gid://gitlab/Milestone/124",
"issueStats": Object {
"closed": 0,
"total": 0,
"closed": 1,
"total": 4,
},
"stats": undefined,
"title": "12.4",
"webPath": undefined,
"webUrl": "/root/release-test/-/milestones/2",
"webUrl": "/releases-namespace/releases-project/-/milestones/2",
},
Object {
"description": "Milestone 12.3",
"id": "gid://gitlab/Milestone/59",
"description": "The 12.3 milestone",
"id": "gid://gitlab/Milestone/123",
"issueStats": Object {
"closed": 1,
"total": 2,
"closed": 3,
"total": 5,
},
"stats": undefined,
"title": "12.3",
"webPath": undefined,
"webUrl": "/root/release-test/-/milestones/1",
"webUrl": "/releases-namespace/releases-project/-/milestones/1",
},
],
"name": "Release 1.0",
"releasedAt": "2020-08-21T20:15:18Z",
"tagName": "v5.10",
"tagPath": "/root/release-test/-/tags/v5.10",
"upcomingRelease": false,
"name": "The first release",
"releasedAt": "2018-12-10T00:00:00Z",
"tagName": "v1.1",
"tagPath": "/releases-namespace/releases-project/-/tags/v1.1",
"upcomingRelease": true,
},
],
"paginationInfo": Object {
"endCursor": "eyJpZCI6IjMiLCJyZWxlYXNlZF9hdCI6IjIwMjAtMDctMDkgMjA6MTE6MzMuODA0OTYxMDAwIFVUQyJ9",
"hasNextPage": true,
"endCursor": "eyJpZCI6IjEiLCJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyJ9",
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": "eyJpZCI6IjQ0IiwicmVsZWFzZWRfYXQiOiIyMDMwLTAzLTE1IDA4OjAwOjAwLjAwMDAwMDAwMCBVVEMifQ",
"startCursor": "eyJpZCI6IjEiLCJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyJ9",
},
}
`;
......@@ -15,139 +15,3 @@ export const pageInfoHeadersWithPagination = {
'X-TOTAL': '21',
'X-TOTAL-PAGES': '2',
};
export const graphqlReleasesResponse = {
data: {
project: {
releases: {
count: 39,
nodes: [
{
name: 'Release 1.0',
tagName: 'v5.10',
tagPath: '/root/release-test/-/tags/v5.10',
descriptionHtml:
'<p data-sourcepos="1:1-1:24" dir="auto">This is version <strong>1.0</strong>!</p>',
releasedAt: '2020-08-21T20:15:18Z',
upcomingRelease: false,
assets: {
count: 7,
sources: {
nodes: [
{
format: 'zip',
url:
'http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.zip',
},
{
format: 'tar.gz',
url:
'http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.tar.gz',
},
{
format: 'tar.bz2',
url:
'http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.tar.bz2',
},
{
format: 'tar',
url:
'http://0.0.0.0:3000/root/release-test/-/archive/v5.10/release-test-v5.10.tar',
},
],
},
links: {
nodes: [
{
id: 'gid://gitlab/Releases::Link/69',
name: 'An example link',
url: 'https://example.com/link',
directAssetUrl:
'http://0.0.0.0:3000/root/release-test/-/releases/v5.32/permanent/path/to/runbook',
linkType: 'OTHER',
external: true,
},
{
id: 'gid://gitlab/Releases::Link/68',
name: 'An example package link',
url: 'https://example.com/package',
directAssetUrl: 'https://example.com/package',
linkType: 'PACKAGE',
external: true,
},
{
id: 'gid://gitlab/Releases::Link/67',
name: 'An example image',
url: 'https://example.com/image',
directAssetUrl: 'https://example.com/image',
linkType: 'IMAGE',
external: true,
},
],
},
},
evidences: {
nodes: [
{
filepath:
'http://0.0.0.0:3000/root/release-test/-/releases/v5.10/evidences/34.json',
collectedAt: '2020-08-21T20:15:19Z',
sha: '22bde8e8b93d870a29ddc339287a1fbb598f45d1396d',
},
],
},
links: {
editUrl: 'http://0.0.0.0:3000/root/release-test/-/releases/v5.10/edit',
issuesUrl: null,
mergeRequestsUrl: null,
selfUrl: 'http://0.0.0.0:3000/root/release-test/-/releases/v5.10',
},
commit: {
sha: '92e7ea2ee4496fe0d00ff69830ba0564d3d1e5a7',
webUrl:
'http://0.0.0.0:3000/root/release-test/-/commit/92e7ea2ee4496fe0d00ff69830ba0564d3d1e5a7',
title: 'Testing a change.',
},
author: {
webUrl: 'http://0.0.0.0:3000/root',
avatarUrl: '/uploads/-/system/user/avatar/1/avatar.png',
username: 'root',
},
milestones: {
nodes: [
{
id: 'gid://gitlab/Milestone/60',
title: '12.4',
description: '',
webPath: '/root/release-test/-/milestones/2',
stats: {
totalIssuesCount: 0,
closedIssuesCount: 0,
},
},
{
id: 'gid://gitlab/Milestone/59',
title: '12.3',
description: 'Milestone 12.3',
webPath: '/root/release-test/-/milestones/1',
stats: {
totalIssuesCount: 2,
closedIssuesCount: 1,
},
},
],
},
},
],
pageInfo: {
startCursor:
'eyJpZCI6IjQ0IiwicmVsZWFzZWRfYXQiOiIyMDMwLTAzLTE1IDA4OjAwOjAwLjAwMDAwMDAwMCBVVEMifQ',
hasPreviousPage: false,
hasNextPage: true,
endCursor:
'eyJpZCI6IjMiLCJyZWxlYXNlZF9hdCI6IjIwMjAtMDctMDkgMjA6MTE6MzMuODA0OTYxMDAwIFVUQyJ9',
},
},
},
},
};
......@@ -16,16 +16,17 @@ import {
parseIntPagination,
convertObjectPropsToCamelCase,
} from '~/lib/utils/common_utils';
import {
pageInfoHeadersWithoutPagination,
graphqlReleasesResponse as originalGraphqlReleasesResponse,
} from '../../../mock_data';
import { pageInfoHeadersWithoutPagination } from '../../../mock_data';
import allReleasesQuery from '~/releases/queries/all_releases.query.graphql';
import { PAGE_SIZE } from '~/releases/constants';
const originalRelease = getJSONFixture('api/releases/release.json');
const originalReleases = [originalRelease];
const originalGraphqlReleasesResponse = getJSONFixture(
'graphql/releases/queries/all_releases.query.graphql.json',
);
describe('Releases State actions', () => {
let mockedState;
let releases;
......
......@@ -3,12 +3,16 @@ import createState from '~/releases/stores/modules/list/state';
import mutations from '~/releases/stores/modules/list/mutations';
import * as types from '~/releases/stores/modules/list/mutation_types';
import { parseIntPagination, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { pageInfoHeadersWithoutPagination, graphqlReleasesResponse } from '../../../mock_data';
import { pageInfoHeadersWithoutPagination } from '../../../mock_data';
import { convertGraphQLResponse } from '~/releases/util';
const originalRelease = getJSONFixture('api/releases/release.json');
const originalReleases = [originalRelease];
const graphqlReleasesResponse = getJSONFixture(
'graphql/releases/queries/all_releases.query.graphql.json',
);
describe('Releases Store Mutations', () => {
let stateCopy;
let restPageInfo;
......
import { cloneDeep } from 'lodash';
import { getJSONFixture } from 'helpers/fixtures';
import { releaseToApiJson, apiJsonToRelease, convertGraphQLResponse } from '~/releases/util';
import { graphqlReleasesResponse as originalGraphqlReleasesResponse } from './mock_data';
const originalGraphqlReleasesResponse = getJSONFixture(
'graphql/releases/queries/all_releases.query.graphql.json',
);
describe('releases/util.js', () => {
describe('releaseToApiJson', () => {
......
......@@ -492,19 +492,6 @@ describe('ReadyToMerge', () => {
});
});
it('hides close button', done => {
jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged'));
jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {});
vm.handleMergePolling(() => {}, () => {});
setImmediate(() => {
expect(document.querySelector('.btn-close').classList.contains('hidden')).toBeTruthy();
done();
});
});
it('updates merge request count badge', done => {
jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged'));
jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {});
......
......@@ -120,7 +120,7 @@ RSpec.describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
described_class.new(merge_request.merge_request_diff,
batch_page,
batch_size,
collection_default_args)
**collection_default_args)
end
end
......
......@@ -8,6 +8,7 @@ RSpec.describe Gitlab::Lfs::Client do
let(:password) { 'password' }
let(:credentials) { { user: username, password: password, auth_method: 'password' } }
let(:git_lfs_content_type) { 'application/vnd.git-lfs+json' }
let(:git_lfs_user_agent) { "GitLab #{Gitlab::VERSION} LFS client" }
let(:basic_auth_headers) do
{ 'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}" }
......@@ -91,7 +92,8 @@ RSpec.describe Gitlab::Lfs::Client do
headers = {
'Accept' => git_lfs_content_type,
'Content-Type' => git_lfs_content_type
'Content-Type' => git_lfs_content_type,
'User-Agent' => git_lfs_user_agent
}.merge(headers)
stub_request(:post, base_url + '/info/lfs/objects/batch').with(body: body, headers: headers)
......@@ -156,7 +158,8 @@ RSpec.describe Gitlab::Lfs::Client do
def stub_upload(object:, headers:)
headers = {
'Content-Type' => 'application/octet-stream',
'Content-Length' => object.size.to_s
'Content-Length' => object.size.to_s,
'User-Agent' => git_lfs_user_agent
}.merge(headers)
stub_request(:put, upload_action['href']).with(
......@@ -209,7 +212,8 @@ RSpec.describe Gitlab::Lfs::Client do
def stub_verify(object:, headers:)
headers = {
'Accept' => git_lfs_content_type,
'Content-Type' => git_lfs_content_type
'Content-Type' => git_lfs_content_type,
'User-Agent' => git_lfs_user_agent
}.merge(headers)
stub_request(:post, verify_action['href']).with(
......
......@@ -8,9 +8,9 @@ RSpec.describe Gitlab::ProjectTemplate do
expected = %w[
rails spring express iosswift dotnetcore android
gomicro gatsby hugo jekyll plainhtml gitbook
hexo sse_middleman nfhugo nfjekyll nfplainhtml
nfgitbook nfhexo salesforcedx serverless_framework
jsonnet cluster_management
hexo sse_middleman gitpod_spring_petclinic nfhugo
nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx
serverless_framework jsonnet cluster_management
]
expect(described_class.all).to be_an(Array)
......
......@@ -84,11 +84,9 @@ RSpec.describe Clusters::Gcp::FinalizeCreationService, '#execute' do
before do
stub_cloud_platform_get_zone_cluster(
provider.gcp_project_id, provider.zone, cluster.name,
{
endpoint: endpoint,
username: username,
password: password
}
)
stub_kubeclient_discover(api_url)
......@@ -101,11 +99,9 @@ RSpec.describe Clusters::Gcp::FinalizeCreationService, '#execute' do
stub_kubeclient_get_secret(
api_url,
{
metadata_name: secret_name,
token: Base64.encode64(token),
namespace: 'default'
}
)
stub_kubeclient_put_cluster_role_binding(api_url, 'gitlab-admin')
......
......@@ -26,27 +26,21 @@ RSpec.describe Clusters::Kubernetes::ConfigureIstioIngressService, '#execute' do
stub_kubeclient_get_secret(
api_url,
{
metadata_name: "#{namespace}-token",
token: Base64.encode64('sample-token'),
namespace: namespace
}
)
stub_kubeclient_get_secret(
api_url,
{
metadata_name: 'istio-ingressgateway-ca-certs',
namespace: 'istio-system'
}
)
stub_kubeclient_get_secret(
api_url,
{
metadata_name: 'istio-ingressgateway-certs',
namespace: 'istio-system'
}
)
stub_kubeclient_put_secret(api_url, 'istio-ingressgateway-ca-certs', namespace: 'istio-system')
......
......@@ -41,11 +41,9 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute'
stub_kubeclient_get_secret(
api_url,
{
metadata_name: "#{namespace}-token",
token: Base64.encode64('sample-token'),
namespace: namespace
}
)
end
......
......@@ -31,11 +31,9 @@ RSpec.describe Clusters::Kubernetes::FetchKubernetesTokenService do
before do
stub_kubeclient_get_secret(
api_url,
{
metadata_name: service_account_token_name,
namespace: namespace,
token: token
}
)
end
......@@ -54,11 +52,9 @@ RSpec.describe Clusters::Kubernetes::FetchKubernetesTokenService do
before do
stub_kubeclient_get_secret_not_found_then_found(
api_url,
{
metadata_name: service_account_token_name,
namespace: namespace,
token: token
}
)
end
......@@ -96,11 +92,9 @@ RSpec.describe Clusters::Kubernetes::FetchKubernetesTokenService do
before do
stub_kubeclient_get_secret(
api_url,
{
metadata_name: service_account_token_name,
namespace: namespace,
token: nil
}
)
end
......
......@@ -24,7 +24,7 @@ module GoogleApi
def stub_cloud_platform_get_zone_cluster(project_id, zone, cluster_id, **options)
WebMock.stub_request(:get, cloud_platform_get_zone_cluster_url(project_id, zone, cluster_id))
.to_return(cloud_platform_response(cloud_platform_cluster_body(options)))
.to_return(cloud_platform_response(cloud_platform_cluster_body(**options)))
end
def stub_cloud_platform_get_zone_cluster_error(project_id, zone, cluster_id)
......
......@@ -153,7 +153,7 @@ module KubernetesHelpers
options[:name] ||= "kubetest"
options[:domain] ||= "example.com"
options[:response] ||= kube_response(kube_knative_services_body(options))
options[:response] ||= kube_response(kube_knative_services_body(**options))
stub_kubeclient_discover(service.api_url)
......@@ -167,7 +167,7 @@ module KubernetesHelpers
options[:namespace] ||= "default"
WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{options[:namespace]}/secrets/#{options[:metadata_name]}")
.to_return(kube_response(kube_v1_secret_body(options)))
.to_return(kube_response(kube_v1_secret_body(**options)))
end
def stub_kubeclient_get_secret_error(api_url, name, namespace: 'default', status: 404)
......@@ -517,7 +517,7 @@ module KubernetesHelpers
def kube_knative_services_body(**options)
{
"kind" => "List",
"items" => [knative_09_service(options)]
"items" => [knative_09_service(**options)]
}
end
......
# frozen_string_literal: true
RSpec.shared_examples 'diff statistics' do |test_include_stats_flag: true|
subject { described_class.new(diffable, collection_default_args) }
subject { described_class.new(diffable, **collection_default_args) }
def stub_stats_find_by_path(path, stats_mock)
expect_next_instance_of(Gitlab::Git::DiffStatsCollection) do |collection|
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment