Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-19 06:08:48 +00:00
parent 5b20366d04
commit 224d2fe167
31 changed files with 392 additions and 151 deletions

View File

@ -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;

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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.') });
},

View File

@ -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);

View File

@ -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.'

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -19,6 +19,10 @@ module Releases
params[:tag]
end
def tag_message
params[:tag_message]
end
def ref
params[:ref]
end

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,
});
});

View File

@ -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,
});
});

View File

@ -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();
});
});
});

View File

@ -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');

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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