Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c17064b66b
commit
2763994b8f
14 changed files with 282 additions and 93 deletions
|
@ -17,7 +17,15 @@ export default class IssuableTemplateSelector extends TemplateSelector {
|
|||
name: this.dropdown.data('selected'),
|
||||
};
|
||||
|
||||
if (initialQuery.name) this.requestFile(initialQuery);
|
||||
// Only use the default template if we don't have description data from autosave
|
||||
if (!initialQuery.name && this.dropdown.data('default') && !this.editor.getValue().length) {
|
||||
initialQuery.name = this.dropdown.data('default');
|
||||
}
|
||||
|
||||
if (initialQuery.name) {
|
||||
this.requestFile(initialQuery);
|
||||
this.setToggleText(initialQuery.name);
|
||||
}
|
||||
|
||||
$('.reset-template', this.dropdown.parent()).on('click', () => {
|
||||
this.setInputValueToTemplateContent();
|
||||
|
@ -53,10 +61,14 @@ export default class IssuableTemplateSelector extends TemplateSelector {
|
|||
}
|
||||
|
||||
this.setInputValueToTemplateContent();
|
||||
$('.dropdown-toggle-text', this.dropdown).text(__('Choose a template'));
|
||||
this.setToggleText(__('Choose a template'));
|
||||
this.previousSelectedIndex = null;
|
||||
}
|
||||
|
||||
setToggleText(text) {
|
||||
$('.dropdown-toggle-text', this.dropdown).text(text);
|
||||
}
|
||||
|
||||
setSelectedIndex() {
|
||||
this.previousSelectedIndex = this.dropdown.data('deprecatedJQueryDropdown').selectedIndex;
|
||||
}
|
||||
|
|
|
@ -5,8 +5,12 @@ module IssuablesDescriptionTemplatesHelper
|
|||
include GitlabRoutingHelper
|
||||
|
||||
def template_dropdown_tag(issuable, &block)
|
||||
selected_template = selected_template(issuable)
|
||||
title = selected_template || _('Choose a template')
|
||||
template_names = template_names(issuable)
|
||||
|
||||
selected_template = selected_template_name(template_names)
|
||||
default_template = default_template_name(template_names, issuable)
|
||||
title = _('Choose a template')
|
||||
|
||||
options = {
|
||||
toggle_class: 'js-issuable-selector',
|
||||
title: title,
|
||||
|
@ -17,6 +21,7 @@ module IssuablesDescriptionTemplatesHelper
|
|||
data: issuable_templates(ref_project, issuable.to_ability_name),
|
||||
field_name: 'issuable_template',
|
||||
selected: selected_template,
|
||||
default: default_template,
|
||||
project_id: ref_project.id
|
||||
}
|
||||
}
|
||||
|
@ -32,19 +37,19 @@ module IssuablesDescriptionTemplatesHelper
|
|||
@template_types[project.id][issuable_type] ||= TemplateFinder.all_template_names(project, issuable_type.pluralize)
|
||||
end
|
||||
|
||||
def selected_template(issuable)
|
||||
all_templates = issuable_templates(ref_project, issuable.to_ability_name)
|
||||
|
||||
# Only local templates will be listed if licenses for inherited templates are not present
|
||||
all_templates = all_templates.values.flatten.map { |tpl| tpl[:name] }.compact.uniq
|
||||
|
||||
template = all_templates.find { |tmpl_name| tmpl_name == params[:issuable_template] }
|
||||
|
||||
unless issuable.description.present?
|
||||
template ||= all_templates.find { |tmpl_name| tmpl_name.casecmp?('default') }
|
||||
def selected_template_name(template_names)
|
||||
template_names.find { |tmpl_name| tmpl_name == params[:issuable_template] }
|
||||
end
|
||||
|
||||
template
|
||||
def default_template_name(template_names, issuable)
|
||||
return if issuable.description.present? || issuable.persisted?
|
||||
|
||||
template_names.find { |tmpl_name| tmpl_name.casecmp?('default') }
|
||||
end
|
||||
|
||||
def template_names(issuable)
|
||||
# Only local templates will be listed if licenses for inherited templates are not present
|
||||
issuable_templates(ref_project, issuable.to_ability_name).values.flatten.map { |tpl| tpl[:name] }.compact.uniq
|
||||
end
|
||||
|
||||
def available_service_desk_templates_for(project)
|
||||
|
|
|
@ -5,9 +5,14 @@ module Todos
|
|||
class DestroyedIssuableService
|
||||
BATCH_SIZE = 100
|
||||
|
||||
# Since we are moving towards work items, in some instances we create todos with
|
||||
# `target_type: WorkItem` in other instances we still create todos with `target_type: Issue`
|
||||
# So when an issue/work item is deleted, we just make sure to delete todos for both target types
|
||||
BOUND_TARGET_TYPES = %w(Issue WorkItem).freeze
|
||||
|
||||
def initialize(target_id, target_type)
|
||||
@target_id = target_id
|
||||
@target_type = target_type
|
||||
@target_type = BOUND_TARGET_TYPES.include?(target_type) ? BOUND_TARGET_TYPES : target_type
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
|
@ -7556,16 +7556,35 @@
|
|||
wysiwyg: |-
|
||||
<p>Multiple spaces</p>
|
||||
07_01__gitlab_specific_markdown__footnotes__001:
|
||||
canonical: ""
|
||||
canonical: |
|
||||
<p>
|
||||
footnote reference tag
|
||||
<sup>
|
||||
<a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref>
|
||||
1
|
||||
</a>
|
||||
</sup>
|
||||
</p>
|
||||
<section data-footnotes>
|
||||
<ol>
|
||||
<li id="fn-fortytwo-42">
|
||||
<p>
|
||||
footnote text
|
||||
<a href="#fnref-fortytwo-42" data-footnote-backref>
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:27" dir="auto">footnote reference tag <sup class="footnote-ref"><a href="#fn-1-9034" id="fnref-1-9034" data-footnote-ref>1</a></sup></p>
|
||||
<p data-sourcepos="1:1-1:34" dir="auto">footnote reference tag <sup class="footnote-ref"><a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref>1</a></sup></p>
|
||||
<section data-footnotes class="footnotes">
|
||||
<ol>
|
||||
<li id="fn-1-9034">
|
||||
<p data-sourcepos="3:7-3:19">footnote text <a href="#fnref-1-9034" data-footnote-backref aria-label="Back to content" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
|
||||
<li id="fn-fortytwo-42">
|
||||
<p data-sourcepos="3:14-3:26">footnote text <a href="#fnref-fortytwo-42" data-footnote-backref aria-label="Back to content" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
wysiwyg: |-
|
||||
<p>footnote reference tag <sup identifier="1">1</sup></p>
|
||||
<p>footnote reference tag <sup identifier="fortytwo">fortytwo</sup></p>
|
||||
<div node="footnoteDefinition(paragraph("footnote text"))" htmlattributes="[object Object]"><p>footnote text</p></div>
|
||||
|
|
|
@ -2190,6 +2190,6 @@
|
|||
06_15__inlines__textual_content__003: |
|
||||
Multiple spaces
|
||||
07_01__gitlab_specific_markdown__footnotes__001: |
|
||||
footnote reference tag [^1]
|
||||
footnote reference tag [^fortytwo]
|
||||
|
||||
[^1]: footnote text
|
||||
[^fortytwo]: footnote text
|
||||
|
|
|
@ -19218,8 +19218,8 @@
|
|||
{
|
||||
"type": "footnoteReference",
|
||||
"attrs": {
|
||||
"identifier": "1",
|
||||
"label": "1"
|
||||
"identifier": "fortytwo",
|
||||
"label": "fortytwo"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -19227,8 +19227,8 @@
|
|||
{
|
||||
"type": "footnoteDefinition",
|
||||
"attrs": {
|
||||
"identifier": "1",
|
||||
"label": "1"
|
||||
"identifier": "fortytwo",
|
||||
"label": "fortytwo"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
|
|
|
@ -14,7 +14,27 @@ See
|
|||
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
|
||||
|
||||
```````````````````````````````` example gitlab footnote
|
||||
footnote reference tag [^1]
|
||||
footnote reference tag [^fortytwo]
|
||||
|
||||
[^1]: footnote text
|
||||
[^fortytwo]: footnote text
|
||||
.
|
||||
<p>
|
||||
footnote reference tag
|
||||
<sup>
|
||||
<a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref>
|
||||
1
|
||||
</a>
|
||||
</sup>
|
||||
</p>
|
||||
<section data-footnotes>
|
||||
<ol>
|
||||
<li id="fn-fortytwo-42">
|
||||
<p>
|
||||
footnote text
|
||||
<a href="#fnref-fortytwo-42" data-footnote-backref>
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
````````````````````````````````
|
||||
|
|
|
@ -9616,9 +9616,29 @@ See
|
|||
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
|
||||
|
||||
```````````````````````````````` example gitlab footnote
|
||||
footnote reference tag [^1]
|
||||
footnote reference tag [^fortytwo]
|
||||
|
||||
[^1]: footnote text
|
||||
[^fortytwo]: footnote text
|
||||
.
|
||||
<p>
|
||||
footnote reference tag
|
||||
<sup>
|
||||
<a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref>
|
||||
1
|
||||
</a>
|
||||
</sup>
|
||||
</p>
|
||||
<section data-footnotes>
|
||||
<ol>
|
||||
<li id="fn-fortytwo-42">
|
||||
<p>
|
||||
footnote text
|
||||
<a href="#fnref-fortytwo-42" data-footnote-backref>
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
````````````````````````````````
|
||||
|
||||
<!-- END TESTS -->
|
||||
|
|
|
@ -29,6 +29,8 @@ module Glfm
|
|||
def process(skip_static_and_wysiwyg: false)
|
||||
output('Updating example snapshots...')
|
||||
|
||||
setup_environment
|
||||
|
||||
output('(Skipping static HTML generation)') if skip_static_and_wysiwyg
|
||||
|
||||
output("Reading #{GLFM_SPEC_TXT_PATH}...")
|
||||
|
@ -47,6 +49,14 @@ module Glfm
|
|||
|
||||
private
|
||||
|
||||
def setup_environment
|
||||
# Set 'GITLAB_TEST_FOOTNOTE_ID' in order to override random number generation in
|
||||
# Banzai::Filter::FootnoteFilter#random_number, and thus avoid the need to
|
||||
# perform normalization on the value. See:
|
||||
# https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#normalization
|
||||
ENV['GITLAB_TEST_FOOTNOTE_ID'] = '42'
|
||||
end
|
||||
|
||||
def add_example_names(all_examples)
|
||||
# NOTE: This method and the parse_examples method assume:
|
||||
# 1. Section 2 is the first section which contains examples
|
||||
|
|
|
@ -90,6 +90,34 @@ RSpec.describe 'issuable templates', :js do
|
|||
end
|
||||
end
|
||||
|
||||
context 'user creates an issue with a default template from the repo' do
|
||||
let(:template_content) { 'this is the default template' }
|
||||
|
||||
before do
|
||||
project.repository.create_file(
|
||||
user,
|
||||
'.gitlab/issue_templates/default.md',
|
||||
template_content,
|
||||
message: 'added default issue template',
|
||||
branch_name: 'master'
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not overwrite autosaved description' do
|
||||
visit new_project_issue_path project
|
||||
wait_for_requests
|
||||
|
||||
assert_template # default template is loaded the first time
|
||||
|
||||
fill_in 'issue_description', with: 'my own description', fill_options: { clear: :backspace }
|
||||
|
||||
visit new_project_issue_path project
|
||||
wait_for_requests
|
||||
|
||||
assert_template(expected_content: 'my own description')
|
||||
end
|
||||
end
|
||||
|
||||
context 'user creates a merge request using templates' do
|
||||
let(:template_content) { 'this is a test "feature-proposal" template' }
|
||||
let(:bug_template_content) { 'this is merge request bug template' }
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do
|
||||
describe '#issuable_templates' do
|
||||
include_context 'project issuable templates context'
|
||||
|
||||
describe '#issuable_templates' do
|
||||
let_it_be(:inherited_from) { nil }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:parent_group, reload: true) { create(:group) }
|
||||
|
@ -44,7 +44,7 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#selected_template' do
|
||||
describe '#available_service_desk_templates_for' do
|
||||
let_it_be(:project) { build(:project) }
|
||||
|
||||
before do
|
||||
|
@ -72,40 +72,9 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do
|
|||
].to_json
|
||||
expect(helper.available_service_desk_templates_for(@project)).to eq(value)
|
||||
end
|
||||
|
||||
context 'when no issuable_template parameter or default template is present' do
|
||||
it 'does not select a template' do
|
||||
expect(helper.selected_template(project)).to be(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an issuable_template parameter has been provided' do
|
||||
before do
|
||||
allow(helper).to receive(:params).and_return({ issuable_template: 'another_issue_template' })
|
||||
end
|
||||
|
||||
it 'selects the issuable template' do
|
||||
expect(helper.selected_template(project)).to eq('another_issue_template')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a default template' do
|
||||
let(:templates) do
|
||||
{
|
||||
"" => [
|
||||
{ name: "another_issue_template", id: "another_issue_template", project_id: project.id },
|
||||
{ name: "default", id: "default", project_id: project.id }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
it 'selects the default template' do
|
||||
expect(helper.selected_template(project)).to eq('default')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are not templates in the project' do
|
||||
context 'when there are no templates in the project' do
|
||||
let(:templates) { {} }
|
||||
|
||||
it 'returns empty array' do
|
||||
|
@ -114,4 +83,92 @@ RSpec.describe IssuablesDescriptionTemplatesHelper, :clean_gitlab_redis_cache do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#selected_template_name' do
|
||||
let(:template_names) { %w(another_issue_template custom_issue_template) }
|
||||
|
||||
context 'when no issuable_template parameter is provided' do
|
||||
it 'does not select a template' do
|
||||
expect(helper.selected_template_name(template_names)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an issuable_template parameter has been provided' do
|
||||
before do
|
||||
allow(helper).to receive(:params).and_return({ issuable_template: template_param_value })
|
||||
end
|
||||
|
||||
context 'when param matches existing templates' do
|
||||
let(:template_param_value) { 'another_issue_template' }
|
||||
|
||||
it 'returns the matching issuable template' do
|
||||
expect(helper.selected_template_name(template_names)).to eq('another_issue_template')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when param does not match any templates' do
|
||||
let(:template_param_value) { 'non_matching_issue_template' }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(helper.selected_template_name(template_names)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default_template_name' do
|
||||
context 'when a default template is available' do
|
||||
let(:template_names) { %w(another_issue_template deFault) }
|
||||
|
||||
it 'returns the default template' do
|
||||
issue = build(:issue)
|
||||
|
||||
expect(helper.default_template_name(template_names, issue)).to be('deFault')
|
||||
end
|
||||
|
||||
it 'returns nil when issuable has a description set' do
|
||||
issue = build(:issue, description: 'from template in project settings')
|
||||
|
||||
expect(helper.default_template_name(template_names, issue)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when issuable is persisted' do
|
||||
issue = create(:issue)
|
||||
|
||||
expect(helper.default_template_name(template_names, issue)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no default template' do
|
||||
let(:template_names) { %w(another_issue_template) }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(helper.default_template_name(template_names, build(:issue))).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#template_names' do
|
||||
let(:project) { build(:project) }
|
||||
let(:templates) do
|
||||
{
|
||||
"Project templates" => [
|
||||
{ name: "another_issue_template", id: "another_issue_template", project_id: project.id },
|
||||
{ name: "custom_issue_template", id: "custom_issue_template", project_id: project.id }
|
||||
],
|
||||
"Group templates" => [
|
||||
{ name: "another_issue_template", id: "another_issue_template", project_id: project.id }
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:ref_project).and_return(project)
|
||||
allow(helper).to receive(:issuable_templates).and_return(templates)
|
||||
end
|
||||
|
||||
it 'returns unique list of template names' do
|
||||
expect(helper.template_names(build(:issue))).to contain_exactly('another_issue_template', 'custom_issue_template')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,16 +2,14 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Admin::BroadcastMessagesController do
|
||||
RSpec.describe Admin::BroadcastMessagesController, :enable_admin_mode do
|
||||
before do
|
||||
sign_in(create(:admin))
|
||||
end
|
||||
|
||||
describe 'GET /preview' do
|
||||
render_views
|
||||
|
||||
describe 'POST /preview' do
|
||||
it 'renders preview partial' do
|
||||
get :preview, params: { broadcast_message: { message: "Hello, world!" } }
|
||||
post preview_admin_broadcast_messages_path, params: { broadcast_message: { message: "Hello, world!" } }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.body).to render_template(:_preview)
|
|
@ -4,31 +4,46 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Todos::Destroy::DestroyedIssuableService do
|
||||
describe '#execute' do
|
||||
let_it_be(:target) { create(:merge_request) }
|
||||
let_it_be(:pending_todo) { create(:todo, :pending, project: target.project, target: target, user: create(:user)) }
|
||||
let_it_be(:done_todo) { create(:todo, :done, project: target.project, target: target, user: create(:user)) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
def execute
|
||||
described_class.new(target.id, target.class.name).execute
|
||||
end
|
||||
subject { described_class.new(target.id, target.class.name).execute }
|
||||
|
||||
context 'when target is merge request' do
|
||||
let_it_be(:target) { create(:merge_request) }
|
||||
let_it_be(:pending_todo) { create(:todo, :pending, project: target.project, target: target, user: user) }
|
||||
let_it_be(:done_todo) { create(:todo, :done, project: target.project, target: target, user: user) }
|
||||
|
||||
it 'deletes todos for specified target ID and type' do
|
||||
control_count = ActiveRecord::QueryRecorder.new { execute }.count
|
||||
control_count = ActiveRecord::QueryRecorder.new { subject }.count
|
||||
|
||||
# Create more todos for the target
|
||||
create(:todo, :pending, project: target.project, target: target, user: create(:user))
|
||||
create(:todo, :pending, project: target.project, target: target, user: create(:user))
|
||||
create(:todo, :done, project: target.project, target: target, user: create(:user))
|
||||
create(:todo, :done, project: target.project, target: target, user: create(:user))
|
||||
create(:todo, :pending, project: target.project, target: target, user: user)
|
||||
create(:todo, :pending, project: target.project, target: target, user: user)
|
||||
create(:todo, :done, project: target.project, target: target, user: user)
|
||||
create(:todo, :done, project: target.project, target: target, user: user)
|
||||
|
||||
expect { execute }.not_to exceed_query_limit(control_count)
|
||||
expect(target.reload.todos.count).to eq(0)
|
||||
expect { subject }.not_to exceed_query_limit(control_count)
|
||||
end
|
||||
|
||||
it 'invalidates todos cache counts of todo users', :use_clean_rails_redis_caching do
|
||||
expect { execute }
|
||||
expect { subject }
|
||||
.to change { pending_todo.user.todos_pending_count }.from(1).to(0)
|
||||
.and change { done_todo.user.todos_done_count }.from(1).to(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when target is an work item' do
|
||||
let_it_be(:target) { create(:work_item) }
|
||||
let_it_be(:todo1) { create(:todo, :pending, project: target.project, target: target, user: user) }
|
||||
let_it_be(:todo2) { create(:todo, :done, project: target.project, target: target, user: user) }
|
||||
# rubocop: disable Cop/AvoidBecomes
|
||||
let_it_be(:todo3) { create(:todo, :pending, project: target.project, target: target.becomes(Issue), user: user) }
|
||||
let_it_be(:todo4) { create(:todo, :done, project: target.project, target: target.becomes(Issue), user: user) }
|
||||
# rubocop: enable Cop/AvoidBecomes
|
||||
|
||||
it 'deletes todos' do
|
||||
expect { subject }.to change(Todo, :count).by(-4)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue