Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5b20366d04
commit
224d2fe167
|
@ -77,7 +77,7 @@ export default {
|
|||
return this.formState.title.trim() !== '';
|
||||
},
|
||||
shouldShowDeleteButton() {
|
||||
return this.canDestroy && this.showDeleteButton;
|
||||
return this.canDestroy && this.showDeleteButton && this.typeToShow;
|
||||
},
|
||||
typeToShow() {
|
||||
const { issueState, issuableType } = this;
|
||||
|
|
|
@ -31,7 +31,7 @@ export default {
|
|||
<url-sync>
|
||||
<template #default="{ updateQuery }">
|
||||
<registry-search
|
||||
:filter="filter"
|
||||
:filters="filter"
|
||||
:sorting="sorting"
|
||||
:tokens="[] /* eslint-disable-line @gitlab/vue-no-new-non-primitive-in-template */"
|
||||
:sortable-fields="sortableFields"
|
||||
|
|
|
@ -105,7 +105,7 @@ export default {
|
|||
<template #default="{ updateQuery }">
|
||||
<registry-search
|
||||
v-if="mountRegistrySearch"
|
||||
:filter="filters"
|
||||
:filters="filters"
|
||||
:sorting="sorting"
|
||||
:tokens="$options.tokens"
|
||||
:sortable-fields="sortableFields"
|
||||
|
|
|
@ -66,7 +66,7 @@ export default {
|
|||
<template #default="{ updateQuery }">
|
||||
<registry-search
|
||||
v-if="mountRegistrySearch"
|
||||
:filter="filters"
|
||||
:filters="filters"
|
||||
:sorting="sorting"
|
||||
:tokens="$options.tokens"
|
||||
:sortable-fields="sortableFields"
|
||||
|
|
|
@ -6,9 +6,9 @@ import BlobHeader from '~/blob/components/blob_header.vue';
|
|||
import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constants';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { isLoggedIn } from '~/lib/utils/common_utils';
|
||||
import { isLoggedIn, handleLocationHash } from '~/lib/utils/common_utils';
|
||||
import { __ } from '~/locale';
|
||||
import { redirectTo, getLocationHash } from '~/lib/utils/url_utility';
|
||||
import { redirectTo } from '~/lib/utils/url_utility';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
|
||||
import CodeIntelligence from '~/code_navigation/components/app.vue';
|
||||
|
@ -183,7 +183,7 @@ export default {
|
|||
this.isLoadingLegacyViewer = true;
|
||||
axios
|
||||
.get(`${this.blobInfo.webPath}?format=json&viewer=${type}`)
|
||||
.then(({ data: { html, binary } }) => {
|
||||
.then(async ({ data: { html, binary } }) => {
|
||||
if (type === SIMPLE_BLOB_VIEWER) {
|
||||
this.isRenderingLegacyTextViewer = true;
|
||||
|
||||
|
@ -197,20 +197,14 @@ export default {
|
|||
this.legacyRichViewer = html;
|
||||
}
|
||||
|
||||
this.scrollToHash();
|
||||
this.isBinary = binary;
|
||||
this.isLoadingLegacyViewer = false;
|
||||
|
||||
await this.$nextTick();
|
||||
handleLocationHash(); // Ensures that we scroll to the hash when async content is loaded
|
||||
})
|
||||
.catch(() => this.displayError());
|
||||
},
|
||||
scrollToHash() {
|
||||
const hash = getLocationHash();
|
||||
if (hash) {
|
||||
// Ensures the browser's native scroll to hash is triggered for async content
|
||||
window.location.hash = '';
|
||||
window.location.hash = hash;
|
||||
}
|
||||
},
|
||||
displayError() {
|
||||
createFlash({ message: __('An error occurred while loading the file. Please try again.') });
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ export default {
|
|||
GlFilteredSearch,
|
||||
},
|
||||
props: {
|
||||
filter: {
|
||||
filters: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
@ -33,7 +33,7 @@ export default {
|
|||
computed: {
|
||||
internalFilter: {
|
||||
get() {
|
||||
return this.filter;
|
||||
return this.filters;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('filter:changed', value);
|
||||
|
@ -71,7 +71,7 @@ export default {
|
|||
const sort = this.isSortAscending ? DESCENDING_ORDER : ASCENDING_ORDER;
|
||||
const newQueryString = this.generateQueryData({
|
||||
sorting: { ...this.sorting, sort },
|
||||
filter: this.filter,
|
||||
filter: this.filters,
|
||||
});
|
||||
this.$emit('sorting:changed', { sort });
|
||||
this.$emit('query:changed', newQueryString);
|
||||
|
@ -79,7 +79,7 @@ export default {
|
|||
onSortItemClick(item) {
|
||||
const newQueryString = this.generateQueryData({
|
||||
sorting: { ...this.sorting, orderBy: item },
|
||||
filter: this.filter,
|
||||
filter: this.filters,
|
||||
});
|
||||
this.$emit('sorting:changed', { orderBy: item });
|
||||
this.$emit('query:changed', newQueryString);
|
||||
|
@ -87,7 +87,7 @@ export default {
|
|||
submitSearch() {
|
||||
const newQueryString = this.generateQueryData({
|
||||
sorting: this.sorting,
|
||||
filter: this.filter,
|
||||
filter: this.filters,
|
||||
});
|
||||
this.$emit('filter:submit');
|
||||
this.$emit('query:changed', newQueryString);
|
||||
|
|
|
@ -14,6 +14,10 @@ module Mutations
|
|||
required: true, as: :tag,
|
||||
description: 'Name of the tag to associate with the release.'
|
||||
|
||||
argument :tag_message, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Message to use if creating a new annotated tag.'
|
||||
|
||||
argument :ref, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Commit SHA or branch name to use if creating a new tag.'
|
||||
|
|
|
@ -670,8 +670,6 @@ class Namespace < ApplicationRecord
|
|||
end
|
||||
|
||||
def first_auto_devops_config_cache_key_for(group_id)
|
||||
return "namespaces:{first_auto_devops_config}:#{group_id}" unless sync_traversal_ids?
|
||||
|
||||
# Use SHA2 of `traversal_ids` to account for moving a namespace within the same root ancestor hierarchy.
|
||||
"namespaces:{#{traversal_ids.first}}:first_auto_devops_config:#{group_id}:#{Digest::SHA2.hexdigest(traversal_ids.join(' '))}"
|
||||
end
|
||||
|
|
|
@ -42,11 +42,11 @@ module Namespaces
|
|||
UnboundedSearch = Class.new(StandardError)
|
||||
|
||||
included do
|
||||
before_update :lock_both_roots, if: -> { sync_traversal_ids? && parent_id_changed? }
|
||||
after_update :sync_traversal_ids, if: -> { sync_traversal_ids? && saved_change_to_parent_id? }
|
||||
before_update :lock_both_roots, if: -> { parent_id_changed? }
|
||||
after_update :sync_traversal_ids, if: -> { saved_change_to_parent_id? }
|
||||
# This uses rails internal before_commit API to sync traversal_ids on namespace create, right before transaction is committed.
|
||||
# This helps reduce the time during which the root namespace record is locked to ensure updated traversal_ids are valid
|
||||
before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? }
|
||||
before_commit :sync_traversal_ids, on: [:create]
|
||||
end
|
||||
|
||||
class_methods do
|
||||
|
@ -76,10 +76,6 @@ module Namespaces
|
|||
end
|
||||
end
|
||||
|
||||
def sync_traversal_ids?
|
||||
Feature.enabled?(:sync_traversal_ids, root_ancestor)
|
||||
end
|
||||
|
||||
def use_traversal_ids?
|
||||
return false unless Feature.enabled?(:use_traversal_ids)
|
||||
|
||||
|
|
|
@ -223,6 +223,7 @@ module MergeRequests
|
|||
# more than one commit in the MR
|
||||
#
|
||||
def assign_title_and_description
|
||||
assign_description_from_repository_template if Feature.enabled?(:mr_default_description_from_repo, target_project)
|
||||
assign_title_and_description_from_commits
|
||||
merge_request.title ||= title_from_issue if target_project.issues_enabled? || target_project.external_issue_tracker
|
||||
merge_request.title ||= source_branch.titleize.humanize
|
||||
|
@ -286,6 +287,37 @@ module MergeRequests
|
|||
title_parts.join(' ')
|
||||
end
|
||||
|
||||
def assign_description_from_repository_template
|
||||
return unless merge_request.description.blank?
|
||||
|
||||
# Use TemplateFinder to load the default template. We need this mainly for
|
||||
# the project_id, in case it differs from the target project. Conveniently,
|
||||
# since the underlying merge_request_template_names_hash is cached, this
|
||||
# should also be relatively cheap and allows us to bail early if the project
|
||||
# does not have a default template.
|
||||
templates = TemplateFinder.all_template_names(target_project, :merge_requests)
|
||||
template = templates.values.flatten.find { |tmpl| tmpl[:name].casecmp?('default') }
|
||||
|
||||
return unless template
|
||||
|
||||
begin
|
||||
repository_template = TemplateFinder.build(
|
||||
:merge_requests,
|
||||
target_project,
|
||||
{
|
||||
name: template[:name],
|
||||
source_template_project_id: template[:project_id]
|
||||
}
|
||||
).execute
|
||||
rescue ::Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
|
||||
return
|
||||
end
|
||||
|
||||
return unless repository_template.present?
|
||||
|
||||
merge_request.description = repository_template.content
|
||||
end
|
||||
|
||||
def issue_iid
|
||||
strong_memoize(:issue_iid) do
|
||||
@params_issue_iid || begin
|
||||
|
|
|
@ -19,6 +19,10 @@ module Releases
|
|||
params[:tag]
|
||||
end
|
||||
|
||||
def tag_message
|
||||
params[:tag_message]
|
||||
end
|
||||
|
||||
def ref
|
||||
params[:ref]
|
||||
end
|
||||
|
|
|
@ -34,7 +34,7 @@ module Releases
|
|||
|
||||
result = Tags::CreateService
|
||||
.new(project, current_user)
|
||||
.execute(tag_name, ref, nil)
|
||||
.execute(tag_name, ref, tag_message)
|
||||
|
||||
return result unless result[:status] == :success
|
||||
|
||||
|
|
|
@ -14,5 +14,5 @@
|
|||
= f.text_field :extern_uid, class: 'form-control', required: true
|
||||
|
||||
.form-actions
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-success"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: sync_traversal_ids
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52854
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321947
|
||||
group: group::access
|
||||
name: mr_default_description_from_repo
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82398
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/361753
|
||||
milestone: '15.0'
|
||||
type: development
|
||||
default_enabled: true
|
||||
milestone: '13.11'
|
||||
group: group::code review
|
||||
default_enabled: false
|
|
@ -4144,6 +4144,7 @@ Input type: `ReleaseCreateInput`
|
|||
| <a id="mutationreleasecreateprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project the release is associated with. |
|
||||
| <a id="mutationreleasecreateref"></a>`ref` | [`String`](#string) | Commit SHA or branch name to use if creating a new tag. |
|
||||
| <a id="mutationreleasecreatereleasedat"></a>`releasedAt` | [`Time`](#time) | Date and time for the release. Defaults to the current date and time. |
|
||||
| <a id="mutationreleasecreatetagmessage"></a>`tagMessage` | [`String`](#string) | Message to use if creating a new annotated tag. |
|
||||
| <a id="mutationreleasecreatetagname"></a>`tagName` | [`String!`](#string) | Name of the tag to associate with the release. |
|
||||
|
||||
#### Fields
|
||||
|
|
|
@ -85,10 +85,10 @@ GitLab can display the results of one or more reports in:
|
|||
## `artifacts:reports:cobertura` (removed)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3708) in GitLab 12.9.
|
||||
> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78132) in GitLab 14.9.
|
||||
> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78132) in GitLab 14.7.
|
||||
> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/348980) in GitLab 15.0.
|
||||
|
||||
This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78132) in GitLab 14.9 and
|
||||
This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78132) in GitLab 14.7 and
|
||||
[removed](https://gitlab.com/gitlab-org/gitlab/-/issues/348980) in GitLab 15.0. Use `artifacts:reports:coverage_report`
|
||||
instead.
|
||||
|
||||
|
|
|
@ -26337,6 +26337,9 @@ msgstr ""
|
|||
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Edit %{scannerType} profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Edit on-demand DAST scan"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -5,13 +5,15 @@ module Gitlab
|
|||
module Group
|
||||
module Settings
|
||||
class UsageQuotas < Chemlab::Page
|
||||
# TODO: Supplant with data-qa-selectors
|
||||
link :pipeline_tab, id: 'pipelines-quota'
|
||||
link :storage_tab, id: 'storage-quota'
|
||||
link :buy_ci_minutes, text: 'Buy additional minutes'
|
||||
link :buy_storage, text: /Purchase more storage/
|
||||
link :buy_storage, text: /Buy storage/
|
||||
div :plan_ci_minutes
|
||||
div :additional_ci_minutes
|
||||
span :purchased_usage_total
|
||||
div :purchased_usage_total_free, 'data-testid': 'purchased-usage-card' # Different UI for free namespace
|
||||
div :ci_purchase_successful_alert, text: /You have successfully purchased CI minutes/
|
||||
div :storage_purchase_successful_alert, text: /You have successfully purchased a storage/
|
||||
h2 :storage_available_alert, text: /purchased storage is available/
|
||||
|
@ -36,9 +38,14 @@ module Gitlab
|
|||
# Returns total purchased storage value once it's ready on page
|
||||
#
|
||||
# @return [Float] Total purchased storage value in GiB
|
||||
def total_purchased_storage
|
||||
def total_purchased_storage(free_name_space = true)
|
||||
storage_available_alert_element.wait_until(&:present?)
|
||||
purchased_usage_total.to_f
|
||||
|
||||
if free_name_space
|
||||
purchased_usage_total_free.split('/').last.match(/\d+\.\d+/)[0].to_f
|
||||
else
|
||||
purchased_usage_total.to_f
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,6 +48,9 @@ module QA
|
|||
usage_quota.buy_storage
|
||||
end
|
||||
|
||||
# Purchase checkout opens a new tab
|
||||
Chemlab.configuration.browser.session.engine.switch_window
|
||||
|
||||
Gitlab::Page::Subscriptions::New.perform do |storage|
|
||||
storage.quantity = quantity
|
||||
storage.continue_to_billing
|
||||
|
|
|
@ -65,7 +65,7 @@ describe('Infrastructure Search', () => {
|
|||
|
||||
expect(findRegistrySearch().exists()).toBe(true);
|
||||
expect(findRegistrySearch().props()).toMatchObject({
|
||||
filter: store.state.filter,
|
||||
filters: store.state.filter,
|
||||
sorting: store.state.sorting,
|
||||
tokens: [],
|
||||
sortableFields: sortableFields(),
|
||||
|
@ -80,7 +80,7 @@ describe('Infrastructure Search', () => {
|
|||
mountComponent(isGroupPage);
|
||||
|
||||
expect(findRegistrySearch().props()).toMatchObject({
|
||||
filter: store.state.filter,
|
||||
filters: store.state.filter,
|
||||
sorting: store.state.sorting,
|
||||
tokens: [],
|
||||
sortableFields: fields,
|
||||
|
|
|
@ -134,7 +134,7 @@ describe('Package Search', () => {
|
|||
|
||||
await nextTick();
|
||||
|
||||
expect(findRegistrySearch().props('filter')).toEqual(['foo']);
|
||||
expect(findRegistrySearch().props('filters')).toEqual(['foo']);
|
||||
});
|
||||
|
||||
it('on filter:submit emits update event', async () => {
|
||||
|
@ -175,7 +175,7 @@ describe('Package Search', () => {
|
|||
expect(getQueryParams).toHaveBeenCalled();
|
||||
|
||||
expect(findRegistrySearch().props()).toMatchObject({
|
||||
filter: defaultQueryParamsMock.filters,
|
||||
filters: defaultQueryParamsMock.filters,
|
||||
sorting: defaultQueryParamsMock.sorting,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -100,7 +100,7 @@ describe('Persisted Search', () => {
|
|||
|
||||
await nextTick();
|
||||
|
||||
expect(findRegistrySearch().props('filter')).toEqual(['foo']);
|
||||
expect(findRegistrySearch().props('filters')).toEqual(['foo']);
|
||||
});
|
||||
|
||||
it('on filter:submit emits update event', async () => {
|
||||
|
@ -138,7 +138,7 @@ describe('Persisted Search', () => {
|
|||
expect(getQueryParams).toHaveBeenCalled();
|
||||
|
||||
expect(findRegistrySearch().props()).toMatchObject({
|
||||
filter: defaultQueryParamsMock.filters,
|
||||
filters: defaultQueryParamsMock.filters,
|
||||
sorting: defaultQueryParamsMock.sorting,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ import userInfoQuery from '~/repository/queries/user_info.query.graphql';
|
|||
import applicationInfoQuery from '~/repository/queries/application_info.query.graphql';
|
||||
import CodeIntelligence from '~/code_navigation/components/app.vue';
|
||||
import { redirectTo } from '~/lib/utils/url_utility';
|
||||
import { isLoggedIn } from '~/lib/utils/common_utils';
|
||||
import { isLoggedIn, handleLocationHash } from '~/lib/utils/common_utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import httpStatusCodes from '~/lib/utils/http_status';
|
||||
import LineHighlighter from '~/blob/line_highlighter';
|
||||
|
@ -209,6 +209,12 @@ describe('Blob content viewer component', () => {
|
|||
await createComponent({ blob: { ...simpleViewerMock, fileType, highlightJs } });
|
||||
expect(LineHighlighter).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('scrolls to the hash', async () => {
|
||||
mockAxios.onGet(legacyViewerUrl).replyOnce(httpStatusCodes.OK, 'test');
|
||||
await createComponent({ blob: { ...simpleViewerMock, fileType, highlightJs } });
|
||||
expect(handleLocationHash).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ describe('Registry Search', () => {
|
|||
const findFilteredSearch = () => wrapper.find(GlFilteredSearch);
|
||||
|
||||
const defaultProps = {
|
||||
filter: [],
|
||||
filters: [],
|
||||
sorting: { sort: 'asc', orderBy: 'name' },
|
||||
tokens: [{ type: 'foo' }],
|
||||
sortableFields: [
|
||||
|
@ -123,7 +123,7 @@ describe('Registry Search', () => {
|
|||
});
|
||||
|
||||
describe('query string calculation', () => {
|
||||
const filter = [
|
||||
const filters = [
|
||||
{ type: FILTERED_SEARCH_TERM, value: { data: 'one' } },
|
||||
{ type: FILTERED_SEARCH_TERM, value: { data: 'two' } },
|
||||
{ type: 'typeOne', value: { data: 'value_one' } },
|
||||
|
@ -131,7 +131,7 @@ describe('Registry Search', () => {
|
|||
];
|
||||
|
||||
it('aggregates the filter in the correct object', () => {
|
||||
mountComponent({ ...defaultProps, filter });
|
||||
mountComponent({ ...defaultProps, filters });
|
||||
|
||||
findFilteredSearch().vm.$emit('submit');
|
||||
|
||||
|
|
|
@ -151,10 +151,9 @@ RSpec.describe Ci::NamespaceMirror do
|
|||
|
||||
it_behaves_like 'changing the middle namespace'
|
||||
|
||||
context 'when the FFs sync_traversal_ids, use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do
|
||||
context 'when the FFs use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do
|
||||
before do
|
||||
stub_feature_flags(sync_traversal_ids: false,
|
||||
use_traversal_ids: false,
|
||||
stub_feature_flags(use_traversal_ids: false,
|
||||
use_traversal_ids_for_ancestors: false)
|
||||
end
|
||||
|
||||
|
|
|
@ -2393,19 +2393,6 @@ RSpec.describe Group do
|
|||
|
||||
fetch_config
|
||||
end
|
||||
|
||||
context 'when traversal ID feature flags are disabled' do
|
||||
before do
|
||||
stub_feature_flags(sync_traversal_ids: false)
|
||||
end
|
||||
|
||||
it 'caches the parent config when group auto_devops_enabled is nil' do
|
||||
cache_key = "namespaces:{first_auto_devops_config}:#{group.id}"
|
||||
define_cache_expectations(cache_key)
|
||||
|
||||
fetch_config
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'cache expiration' do
|
||||
|
|
|
@ -17,6 +17,7 @@ RSpec.describe 'Creation of a new release' do
|
|||
let(:mutation_name) { :release_create }
|
||||
|
||||
let(:tag_name) { 'v7.12.5'}
|
||||
let(:tag_message) { nil }
|
||||
let(:ref) { 'master'}
|
||||
let(:name) { 'Version 7.12.5'}
|
||||
let(:description) { 'Release 7.12.5 :rocket:' }
|
||||
|
@ -29,6 +30,7 @@ RSpec.describe 'Creation of a new release' do
|
|||
{
|
||||
projectPath: project.full_path,
|
||||
tagName: tag_name,
|
||||
tagMessage: tag_message,
|
||||
ref: ref,
|
||||
name: name,
|
||||
description: description,
|
||||
|
@ -191,10 +193,26 @@ RSpec.describe 'Creation of a new release' do
|
|||
context 'when the provided tag does not already exist' do
|
||||
let(:tag_name) { 'v7.12.5-alpha' }
|
||||
|
||||
after do
|
||||
project.repository.rm_tag(developer, tag_name)
|
||||
end
|
||||
|
||||
it_behaves_like 'no errors'
|
||||
|
||||
it 'creates a new tag' do
|
||||
it 'creates a new lightweight tag' do
|
||||
expect { create_release }.to change { Project.find_by_id(project.id).repository.tag_count }.by(1)
|
||||
expect(project.repository.find_tag(tag_name).message).to be_blank
|
||||
end
|
||||
|
||||
context 'and tag_message is provided' do
|
||||
let(:tag_message) { 'Annotated tag message' }
|
||||
|
||||
it_behaves_like 'no errors'
|
||||
|
||||
it 'creates a new annotated tag with the message' do
|
||||
expect { create_release }.to change { Project.find_by_id(project.id).repository.tag_count }.by(1)
|
||||
expect(project.repository.find_tag(tag_name).message).to eq(tag_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1177,7 +1177,6 @@ RSpec.describe API::Groups do
|
|||
|
||||
it "only looks up root ancestor once and returns projects including those in subgroups" do
|
||||
expect(Namespace).to receive(:find_by).with(id: group1.id.to_s).once.and_call_original # For the group sent in the API call
|
||||
expect(Namespace).to receive(:find_by).with(id: group1.traversal_ids.first).once.and_call_original # root_ancestor direct lookup
|
||||
expect(Namespace).to receive(:joins).with(start_with('INNER JOIN (SELECT id, traversal_ids[1]')).once.and_call_original # All-in-one root_ancestor query
|
||||
|
||||
get api("/groups/#{group1.id}/projects", user1), params: { include_subgroups: true }
|
||||
|
|
|
@ -154,57 +154,58 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
|
||||
describe 'writing examples_index.yml' do
|
||||
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
|
||||
let(:expected_examples_index_yml_contents) do
|
||||
<<~ES_EXAMPLES_INDEX_YML_CONTENTS
|
||||
---
|
||||
02_01__inlines__strong__01:
|
||||
spec_txt_example_position: 1
|
||||
source_specification: commonmark
|
||||
02_01__inlines__strong__02:
|
||||
spec_txt_example_position: 2
|
||||
source_specification: github
|
||||
02_02__inlines__strikethrough_extension__01:
|
||||
spec_txt_example_position: 3
|
||||
source_specification: github
|
||||
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01:
|
||||
spec_txt_example_position: 4
|
||||
source_specification: gitlab
|
||||
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01:
|
||||
spec_txt_example_position: 5
|
||||
source_specification: gitlab
|
||||
ES_EXAMPLES_INDEX_YML_CONTENTS
|
||||
end
|
||||
|
||||
it 'writes the correct content' do
|
||||
subject.process(skip_static_and_wysiwyg: true)
|
||||
|
||||
expected =
|
||||
<<~ES_EXAMPLES_INDEX_YML_CONTENTS
|
||||
---
|
||||
02_01__inlines__strong__01:
|
||||
spec_txt_example_position: 1
|
||||
source_specification: commonmark
|
||||
02_01__inlines__strong__02:
|
||||
spec_txt_example_position: 2
|
||||
source_specification: github
|
||||
02_02__inlines__strikethrough_extension__01:
|
||||
spec_txt_example_position: 3
|
||||
source_specification: github
|
||||
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01:
|
||||
spec_txt_example_position: 4
|
||||
source_specification: gitlab
|
||||
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01:
|
||||
spec_txt_example_position: 5
|
||||
source_specification: gitlab
|
||||
ES_EXAMPLES_INDEX_YML_CONTENTS
|
||||
expect(es_examples_index_yml_contents).to eq(expected)
|
||||
expect(es_examples_index_yml_contents).to eq(expected_examples_index_yml_contents)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'writing markdown.yml' do
|
||||
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
|
||||
let(:expected_markdown_yml_contents) do
|
||||
<<~ES_MARKDOWN_YML_CONTENTS
|
||||
---
|
||||
02_01__inlines__strong__01: |
|
||||
__bold__
|
||||
02_01__inlines__strong__02: |
|
||||
__bold with more text__
|
||||
02_02__inlines__strikethrough_extension__01: |
|
||||
~~Hi~~ Hello, world!
|
||||
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: |
|
||||
**bold**
|
||||
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01: |
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
ES_MARKDOWN_YML_CONTENTS
|
||||
end
|
||||
|
||||
it 'writes the correct content' do
|
||||
subject.process(skip_static_and_wysiwyg: true)
|
||||
|
||||
expected =
|
||||
<<~ES_MARKDOWN_YML_CONTENTS
|
||||
---
|
||||
02_01__inlines__strong__01: |
|
||||
__bold__
|
||||
02_01__inlines__strong__02: |
|
||||
__bold with more text__
|
||||
02_02__inlines__strikethrough_extension__01: |
|
||||
~~Hi~~ Hello, world!
|
||||
03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: |
|
||||
**bold**
|
||||
04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__01: |
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
ES_MARKDOWN_YML_CONTENTS
|
||||
|
||||
expect(es_markdown_yml_contents).to eq(expected)
|
||||
expect(es_markdown_yml_contents).to eq(expected_markdown_yml_contents)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -250,6 +251,45 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
GLFM_SPEC_TXT_CONTENTS
|
||||
end
|
||||
|
||||
let(:expected_html_yml_contents) do
|
||||
<<~ES_HTML_YML_CONTENTS
|
||||
---
|
||||
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01:
|
||||
canonical: |
|
||||
<p><strong>bold</strong></p>
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
|
||||
wysiwyg: |-
|
||||
<p><strong>bold</strong></p>
|
||||
ES_HTML_YML_CONTENTS
|
||||
end
|
||||
|
||||
let(:expected_prosemirror_json_contents) do
|
||||
<<~ES_PROSEMIRROR_JSON_YML_CONTENTS
|
||||
---
|
||||
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: |-
|
||||
{
|
||||
"type": "doc",
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"marks": [
|
||||
{
|
||||
"type": "bold"
|
||||
}
|
||||
],
|
||||
"text": "bold"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
ES_PROSEMIRROR_JSON_YML_CONTENTS
|
||||
end
|
||||
|
||||
before do
|
||||
# NOTE: This is a necessary to avoid an `error Couldn't find an integrity file` error
|
||||
# when invoking `yarn jest ...` on CI from within an RSpec job. It could be solved by
|
||||
|
@ -266,45 +306,8 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
it 'writes the correct content' do
|
||||
subject.process
|
||||
|
||||
expected_html =
|
||||
<<~ES_HTML_YML_CONTENTS
|
||||
---
|
||||
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01:
|
||||
canonical: |
|
||||
<p><strong>bold</strong></p>
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
|
||||
wysiwyg: |-
|
||||
<p><strong>bold</strong></p>
|
||||
ES_HTML_YML_CONTENTS
|
||||
|
||||
expected_prosemirror_json =
|
||||
<<~ES_PROSEMIRROR_JSON_YML_CONTENTS
|
||||
---
|
||||
02_01__gitlab_specific_section_with_examples__strong_but_with_two_asterisks__01: |-
|
||||
{
|
||||
"type": "doc",
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"marks": [
|
||||
{
|
||||
"type": "bold"
|
||||
}
|
||||
],
|
||||
"text": "bold"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
ES_PROSEMIRROR_JSON_YML_CONTENTS
|
||||
|
||||
expect(es_html_yml_contents).to eq(expected_html)
|
||||
expect(es_prosemirror_json_yml_contents).to eq(expected_prosemirror_json)
|
||||
expect(es_html_yml_contents).to eq(expected_html_yml_contents)
|
||||
expect(es_prosemirror_json_yml_contents).to eq(expected_prosemirror_json_contents)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -137,10 +137,9 @@ RSpec.describe Ci::ProcessSyncEventsService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the FFs sync_traversal_ids, use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do
|
||||
context 'when the FFs use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do
|
||||
before do
|
||||
stub_feature_flags(sync_traversal_ids: false,
|
||||
use_traversal_ids: false,
|
||||
stub_feature_flags(use_traversal_ids: false,
|
||||
use_traversal_ids_for_ancestors: false)
|
||||
end
|
||||
|
||||
|
|
|
@ -79,6 +79,31 @@ RSpec.describe MergeRequests::BuildService do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'with a Default.md template' do
|
||||
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
||||
let(:project) { create(:project, :custom_repo, files: files ) }
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: project)
|
||||
end
|
||||
|
||||
it 'the template description is preferred' do
|
||||
expect(merge_request.description).to eq('Default template contents')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: false)
|
||||
end
|
||||
|
||||
it 'the template description is not preferred' do
|
||||
expect(merge_request.description).not_to eq('Default template contents')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
it 'calls the compare service with the correct arguments' do
|
||||
allow_any_instance_of(described_class).to receive(:projects_and_branches_valid?).and_return(true)
|
||||
|
@ -221,6 +246,7 @@ RSpec.describe MergeRequests::BuildService do
|
|||
end
|
||||
|
||||
it_behaves_like 'allows the merge request to be created'
|
||||
it_behaves_like 'with a Default.md template'
|
||||
|
||||
it 'uses the title of the commit as the title of the merge request' do
|
||||
expect(merge_request.title).to eq(commit_2.safe_message.split("\n").first)
|
||||
|
@ -241,6 +267,8 @@ RSpec.describe MergeRequests::BuildService do
|
|||
context 'commit has no description' do
|
||||
let(:commits) { Commit.decorate([commit_3], project) }
|
||||
|
||||
it_behaves_like 'with a Default.md template'
|
||||
|
||||
it 'uses the title of the commit as the title of the merge request' do
|
||||
expect(merge_request.title).to eq(commit_3.safe_message)
|
||||
end
|
||||
|
@ -279,6 +307,35 @@ RSpec.describe MergeRequests::BuildService do
|
|||
|
||||
expect(merge_request.description).to eq(expected_description)
|
||||
end
|
||||
|
||||
context 'a Default.md template is defined' do
|
||||
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
||||
let(:project) { create(:project, :custom_repo, files: files ) }
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: project)
|
||||
end
|
||||
|
||||
it 'appends the closing description to a Default.md template' do
|
||||
expected_description = ['Default template contents', closing_message].compact.join("\n\n")
|
||||
|
||||
expect(merge_request.description).to eq(expected_description)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: false)
|
||||
end
|
||||
|
||||
it 'appends the closing description to the commit description' do
|
||||
expected_description = ['Create the app', closing_message].compact.join("\n\n")
|
||||
|
||||
expect(merge_request.description).to eq(expected_description)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the source branch matches an internal issue' do
|
||||
|
@ -332,6 +389,7 @@ RSpec.describe MergeRequests::BuildService do
|
|||
end
|
||||
|
||||
it_behaves_like 'allows the merge request to be created'
|
||||
it_behaves_like 'with a Default.md template'
|
||||
|
||||
it 'uses the title of the branch as the merge request title' do
|
||||
expect(merge_request.title).to eq('Feature branch')
|
||||
|
@ -347,6 +405,31 @@ RSpec.describe MergeRequests::BuildService do
|
|||
it 'keeps the description from the initial params' do
|
||||
expect(merge_request.description).to eq(description)
|
||||
end
|
||||
|
||||
context 'a Default.md template is defined' do
|
||||
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
||||
let(:project) { create(:project, :custom_repo, files: files ) }
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: project)
|
||||
end
|
||||
|
||||
it 'keeps the description from the initial params' do
|
||||
expect(merge_request.description).to eq(description)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: false)
|
||||
end
|
||||
|
||||
it 'keeps the description from the initial params' do
|
||||
expect(merge_request.description).to eq(description)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the source branch matches an issue' do
|
||||
|
@ -377,6 +460,33 @@ RSpec.describe MergeRequests::BuildService do
|
|||
it 'sets the closing description' do
|
||||
expect(merge_request.description).to eq(closing_message)
|
||||
end
|
||||
|
||||
context 'a Default.md template is defined' do
|
||||
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
||||
let(:project) { create(:project, :custom_repo, files: files ) }
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: project)
|
||||
end
|
||||
|
||||
it 'appends the closing description to a Default.md template' do
|
||||
expected_description = ['Default template contents', closing_message].compact.join("\n\n")
|
||||
|
||||
expect(merge_request.description).to eq(expected_description)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: false)
|
||||
end
|
||||
|
||||
it 'sets the closing description' do
|
||||
expect(merge_request.description).to eq(closing_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -389,6 +499,7 @@ RSpec.describe MergeRequests::BuildService do
|
|||
end
|
||||
|
||||
it_behaves_like 'allows the merge request to be created'
|
||||
it_behaves_like 'with a Default.md template'
|
||||
|
||||
it 'uses the first line of the first multi-line commit message as the title' do
|
||||
expect(merge_request.title).to eq('Closes #1234 Second commit')
|
||||
|
@ -426,6 +537,35 @@ RSpec.describe MergeRequests::BuildService do
|
|||
it 'sets the closing description' do
|
||||
expect(merge_request.description).to eq("Create the app#{closing_message ? "\n\n" + closing_message : ''}")
|
||||
end
|
||||
|
||||
context 'a Default.md template is defined' do
|
||||
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
||||
let(:project) { create(:project, :custom_repo, files: files ) }
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: project)
|
||||
end
|
||||
|
||||
it 'appends the closing description to a Default.md template' do
|
||||
expected_description = ['Default template contents', closing_message].compact.join("\n\n")
|
||||
|
||||
expect(merge_request.description).to eq(expected_description)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when mr_default_description_from_repo feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(mr_default_description_from_repo: false)
|
||||
end
|
||||
|
||||
it 'appends the closing description to the commit description' do
|
||||
expected_description = ['Create the app', closing_message].compact.join("\n\n")
|
||||
|
||||
expect(merge_request.description).to eq(expected_description)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -626,4 +766,52 @@ RSpec.describe MergeRequests::BuildService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#assign_description_from_repository_template' do
|
||||
subject { service.send(:assign_description_from_repository_template) }
|
||||
|
||||
it 'performs no action if the merge request description is not blank' do
|
||||
merge_request.description = 'foo'
|
||||
subject
|
||||
expect(merge_request.description).to eq 'foo'
|
||||
end
|
||||
|
||||
context 'when a Default template is not found' do
|
||||
it 'does not modify the merge request description' do
|
||||
merge_request.description = nil
|
||||
subject
|
||||
expect(merge_request.description).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a Default template is found' do
|
||||
context 'when its contents cannot be retrieved' do
|
||||
let(:files) { { '.gitlab/merge_request_templates/OtherTemplate.md' => 'Other template contents' } }
|
||||
let(:project) { create(:project, :custom_repo, files: files ) }
|
||||
|
||||
it 'does not modify the merge request description' do
|
||||
allow(TemplateFinder).to receive(:all_template_names).and_return({
|
||||
merge_requests: [
|
||||
{ name: 'Default', id: 'default', key: 'default', project_id: project.id }
|
||||
]
|
||||
})
|
||||
|
||||
merge_request.description = nil
|
||||
subject
|
||||
expect(merge_request.description).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when its contents can be retrieved' do
|
||||
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
||||
let(:project) { create(:project, :custom_repo, files: files ) }
|
||||
|
||||
it 'modifies the merge request description' do
|
||||
merge_request.description = nil
|
||||
subject
|
||||
expect(merge_request.description).to eq 'Default template contents'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue