Commit 4431a25d authored by Jan Provaznik's avatar Jan Provaznik

Merge branch 'fix-file-tmpls-return-same-content-for-different-tmpls' into 'master'

Fix file templates return same content for different templates

See merge request gitlab-org/gitlab!52332
parents 963493f5 1f85284f
...@@ -66,6 +66,8 @@ export default class FileTemplateSelector { ...@@ -66,6 +66,8 @@ export default class FileTemplateSelector {
reportSelectionName(options) { reportSelectionName(options) {
const opts = options; const opts = options;
opts.query = options.selectedObj.name; opts.query = options.selectedObj.name;
opts.data = options.selectedObj;
opts.data.source_template_project_id = options.selectedObj.project_id;
this.reportSelection(opts); this.reportSelection(opts);
} }
......
...@@ -30,6 +30,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector { ...@@ -30,6 +30,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector {
const data = { const data = {
project: this.$dropdown.data('project'), project: this.$dropdown.data('project'),
fullname: this.$dropdown.data('fullname'), fullname: this.$dropdown.data('fullname'),
source_template_project_id: query.project_id,
}; };
this.reportSelection({ this.reportSelection({
......
...@@ -437,6 +437,7 @@ export class GitLabDropdown { ...@@ -437,6 +437,7 @@ export class GitLabDropdown {
groupName = el.data('group'); groupName = el.data('group');
if (groupName) { if (groupName) {
selectedIndex = el.data('index'); selectedIndex = el.data('index');
this.selectedIndex = selectedIndex;
selectedObject = this.renderedData[groupName][selectedIndex]; selectedObject = this.renderedData[groupName][selectedIndex];
} else { } else {
selectedIndex = el.closest('li').index(); selectedIndex = el.closest('li').index();
......
...@@ -132,6 +132,10 @@ export default { ...@@ -132,6 +132,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
projectId: {
type: Number,
required: true,
},
projectNamespace: { projectNamespace: {
type: String, type: String,
required: true, required: true,
...@@ -419,6 +423,7 @@ export default { ...@@ -419,6 +423,7 @@ export default {
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:project-path="projectPath" :project-path="projectPath"
:project-id="projectId"
:project-namespace="projectNamespace" :project-namespace="projectNamespace"
:show-delete-button="showDeleteButton" :show-delete-button="showDeleteButton"
:can-attach-file="canAttachFile" :can-attach-file="canAttachFile"
......
...@@ -21,6 +21,10 @@ export default { ...@@ -21,6 +21,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
projectId: {
type: Number,
required: true,
},
projectNamespace: { projectNamespace: {
type: String, type: String,
required: true, required: true,
...@@ -49,11 +53,12 @@ export default { ...@@ -49,11 +53,12 @@ export default {
</script> </script>
<template> <template>
<div class="dropdown js-issuable-selector-wrap" data-issuable-type="issue"> <div class="dropdown js-issuable-selector-wrap" data-issuable-type="issues">
<button <button
ref="toggle" ref="toggle"
:data-namespace-path="projectNamespace" :data-namespace-path="projectNamespace"
:data-project-path="projectPath" :data-project-path="projectPath"
:data-project-id="projectId"
:data-data="issuableTemplatesJson" :data-data="issuableTemplatesJson"
class="dropdown-menu-toggle js-issuable-selector" class="dropdown-menu-toggle js-issuable-selector"
type="button" type="button"
......
...@@ -46,6 +46,10 @@ export default { ...@@ -46,6 +46,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
projectId: {
type: Number,
required: true,
},
projectNamespace: { projectNamespace: {
type: String, type: String,
required: true, required: true,
...@@ -127,6 +131,7 @@ export default { ...@@ -127,6 +131,7 @@ export default {
:form-state="formState" :form-state="formState"
:issuable-templates="issuableTemplates" :issuable-templates="issuableTemplates"
:project-path="projectPath" :project-path="projectPath"
:project-id="projectId"
:project-namespace="projectNamespace" :project-namespace="projectNamespace"
/> />
</div> </div>
......
...@@ -54,6 +54,7 @@ export function initIssueHeaderActions(store) { ...@@ -54,6 +54,7 @@ export function initIssueHeaderActions(store) {
issueType: el.dataset.issueType, issueType: el.dataset.issueType,
newIssuePath: el.dataset.newIssuePath, newIssuePath: el.dataset.newIssuePath,
projectPath: el.dataset.projectPath, projectPath: el.dataset.projectPath,
projectId: el.dataset.projectId,
reportAbusePath: el.dataset.reportAbusePath, reportAbusePath: el.dataset.reportAbusePath,
submitAsSpamPath: el.dataset.submitAsSpamPath, submitAsSpamPath: el.dataset.submitAsSpamPath,
}, },
......
...@@ -9,8 +9,7 @@ export default class IssuableTemplateSelector extends TemplateSelector { ...@@ -9,8 +9,7 @@ export default class IssuableTemplateSelector extends TemplateSelector {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.projectPath = this.dropdown.data('projectPath'); this.projectId = this.dropdown.data('projectId');
this.namespacePath = this.dropdown.data('namespacePath');
this.issuableType = this.$dropdownContainer.data('issuableType'); this.issuableType = this.$dropdownContainer.data('issuableType');
this.titleInput = $(`#${this.issuableType}_title`); this.titleInput = $(`#${this.issuableType}_title`);
this.templateWarningEl = $('.js-issuable-template-warning'); this.templateWarningEl = $('.js-issuable-template-warning');
...@@ -81,21 +80,21 @@ export default class IssuableTemplateSelector extends TemplateSelector { ...@@ -81,21 +80,21 @@ export default class IssuableTemplateSelector extends TemplateSelector {
} }
requestFile(query) { requestFile(query) {
this.startLoadingSpinner(); const callback = (currentTemplate) => {
Api.issueTemplate(
this.namespacePath,
this.projectPath,
query.name,
this.issuableType,
(err, currentTemplate) => {
this.currentTemplate = currentTemplate; this.currentTemplate = currentTemplate;
this.stopLoadingSpinner(); this.stopLoadingSpinner();
if (err) return; // Error handled by global AJAX error handler
this.setInputValueToTemplateContent(); this.setInputValueToTemplateContent();
}, };
this.startLoadingSpinner();
Api.projectTemplate(
this.projectId,
this.issuableType,
query.name,
{ source_template_project_id: query.project_id },
callback,
); );
return;
} }
setInputValueToTemplateContent() { setInputValueToTemplateContent() {
......
...@@ -40,6 +40,7 @@ class LicenseTemplateFinder ...@@ -40,6 +40,7 @@ class LicenseTemplateFinder
LicenseTemplate.new( LicenseTemplate.new(
key: license.key, key: license.key,
name: license.name, name: license.name,
project: project,
nickname: license.nickname, nickname: license.nickname,
category: (license.featured? ? :Popular : :Other), category: (license.featured? ? :Popular : :Other),
content: license.content, content: license.content,
......
...@@ -16,9 +16,7 @@ module IssuablesDescriptionTemplatesHelper ...@@ -16,9 +16,7 @@ module IssuablesDescriptionTemplatesHelper
data: issuable_templates(ref_project, issuable.to_ability_name), data: issuable_templates(ref_project, issuable.to_ability_name),
field_name: 'issuable_template', field_name: 'issuable_template',
selected: selected_template(issuable), selected: selected_template(issuable),
project_id: ref_project.id, project_id: ref_project.id
project_path: ref_project.path,
namespace_path: ref_project.namespace.full_path
} }
} }
......
...@@ -12,11 +12,12 @@ class LicenseTemplate ...@@ -12,11 +12,12 @@ class LicenseTemplate
(fullname|name\sof\s(author|copyright\sowner)) (fullname|name\sof\s(author|copyright\sowner))
[\>\}\]]}xi.freeze [\>\}\]]}xi.freeze
attr_reader :key, :name, :category, :nickname, :url, :meta attr_reader :key, :name, :project, :category, :nickname, :url, :meta
def initialize(key:, name:, category:, content:, nickname: nil, url: nil, meta: {}) def initialize(key:, name:, project:, category:, content:, nickname: nil, url: nil, meta: {})
@key = key @key = key
@name = name @name = name
@project = project
@category = category @category = category
@content = content @content = content
@nickname = nickname @nickname = nickname
...@@ -24,6 +25,10 @@ class LicenseTemplate ...@@ -24,6 +25,10 @@ class LicenseTemplate
@meta = meta @meta = meta
end end
def project_id
project&.id
end
def popular? def popular?
category == :Popular category == :Popular
end end
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- return unless issuable && issuable_templates(ref_project, issuable.to_ability_name).any? - return unless issuable && issuable_templates(ref_project, issuable.to_ability_name).any?
.issuable-form-select-holder.selectbox.form-group .issuable-form-select-holder.selectbox.form-group
.js-issuable-selector-wrap{ data: { issuable_type: issuable.to_ability_name, qa_selector: 'template_dropdown' } } .js-issuable-selector-wrap{ data: { issuable_type: issuable.to_ability_name.pluralize, qa_selector: 'template_dropdown' } }
= template_dropdown_tag(issuable) do = template_dropdown_tag(issuable) do
%ul.dropdown-footer-list %ul.dropdown-footer-list
%li %li
......
...@@ -94,14 +94,15 @@ Example response (licenses): ...@@ -94,14 +94,15 @@ Example response (licenses):
## Get one template of a particular type ## Get one template of a particular type
```plaintext ```plaintext
GET /projects/:id/templates/:type/:key GET /projects/:id/templates/:type/:name
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- | | ---------- | ------ | -------- | ----------- |
| `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses|issues|merge_requests)` of the template | | `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses|issues|merge_requests)` of the template |
| `key` | string | yes | The key of the template, as obtained from the collection endpoint | | `name` | string | yes | The key of the template, as obtained from the collection endpoint |
| `source_template_project_id` | integer | no | The project ID where a given template is being stored. This is useful when multiple templates from different projects have the same name. If multiple templates have the same name, the match from `closest ancestor` is returned if `source_template_project_id` is not specified |
| `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses | | `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses |
| `fullname` | string | no | The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses | | `fullname` | string | no | The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses |
......
...@@ -45,6 +45,7 @@ export default { ...@@ -45,6 +45,7 @@ export default {
:endpoint="endpoint" :endpoint="endpoint"
:update-endpoint="updateEndpoint" :update-endpoint="updateEndpoint"
:project-path="groupPath" :project-path="groupPath"
:project-id="0"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:can-update="canUpdate" :can-update="canUpdate"
......
...@@ -19,7 +19,7 @@ module EE ...@@ -19,7 +19,7 @@ module EE
return super unless custom_templates? return super unless custom_templates?
if params[:name] if params[:name]
custom_templates.find(params[:name]) || super custom_templates.find(params[:name], params[:source_template_project_id]) || super
else else
custom_templates.all + super custom_templates.all + super
end end
......
...@@ -28,7 +28,7 @@ module EE ...@@ -28,7 +28,7 @@ module EE
return super if custom_templates.nil? || !custom_templates.enabled? return super if custom_templates.nil? || !custom_templates.enabled?
if params[:name] if params[:name]
custom_templates.find(params[:name]) || super custom_templates.find(params[:name], params[:source_template_project_id]) || super
else else
custom_templates.all + super custom_templates.all + super
end end
......
---
title: Fix file templates return same content for templates with same name but from
different projects
merge_request: 52332
author:
type: fixed
...@@ -43,12 +43,16 @@ module Gitlab ...@@ -43,12 +43,16 @@ module Gitlab
by_namespace + by_instance by_namespace + by_instance
end end
def find(name) def find(name, source_template_project_id = nil)
namespace_template_projects_hash.each do |namespace, project| namespace_template_projects_hash.each do |namespace, project|
next if source_template_project_id && project&.id != source_template_project_id.to_i
found = template_for(project, name, category_for(namespace)) found = template_for(project, name, category_for(namespace))
return found if found return found if found
end end
return if source_template_project_id && instance_template_project&.id != source_template_project_id.to_i
template_for(instance_template_project, name, _('Instance')) template_for(instance_template_project, name, _('Instance'))
end end
...@@ -78,7 +82,7 @@ module Gitlab ...@@ -78,7 +82,7 @@ module Gitlab
# ordered from most-specific to least-specific # ordered from most-specific to least-specific
def namespace_template_projects_hash def namespace_template_projects_hash
strong_memoize(:namespace_template_projects_hash) do strong_memoize(:namespace_template_projects_hash) do
next [] unless project.present? next {} unless project.present?
project project
.ancestors_upto(nil) .ancestors_upto(nil)
...@@ -98,18 +102,18 @@ module Gitlab ...@@ -98,18 +102,18 @@ module Gitlab
def templates_for(project, category) def templates_for(project, category)
return [] unless project return [] unless project
finder.all(project).map { |template| translate(template, category: category) } finder.all(project).map { |template| translate(template, project, category: category) }
end end
def template_for(project, name, category) def template_for(project, name, category)
return unless project return unless project
translate(finder.find(name, project), category: category) translate(finder.find(name, project), project, category: category)
rescue ::Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError rescue ::Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
nil nil
end end
def translate(template, category:) def translate(template, project, category:)
return unless template return unless template
template.category = category template.category = category
...@@ -122,6 +126,7 @@ module Gitlab ...@@ -122,6 +126,7 @@ module Gitlab
LicenseTemplate.new( LicenseTemplate.new(
key: template.key, key: template.key,
name: template.name, name: template.name,
project: project,
nickname: template.name, nickname: template.name,
category: template.category, category: template.category,
content: -> { template.content } content: -> { template.content }
......
...@@ -7,7 +7,7 @@ RSpec.describe LicenseTemplateFinder do ...@@ -7,7 +7,7 @@ RSpec.describe LicenseTemplateFinder do
let(:params) { {} } let(:params) { {} }
let(:fake_template_source) { double(::Gitlab::CustomFileTemplates) } let(:fake_template_source) { double(::Gitlab::CustomFileTemplates) }
let(:custom_template) { ::LicenseTemplate.new(key: 'foo', name: 'foo', category: nil, content: 'Template') } let(:custom_template) { ::LicenseTemplate.new(key: 'foo', name: 'foo', project: project, category: nil, content: 'Template') }
let(:custom_templates) { [custom_template] } let(:custom_templates) { [custom_template] }
subject(:finder) { described_class.new(project, params) } subject(:finder) { described_class.new(project, params) }
...@@ -23,7 +23,7 @@ RSpec.describe LicenseTemplateFinder do ...@@ -23,7 +23,7 @@ RSpec.describe LicenseTemplateFinder do
allow(fake_template_source) allow(fake_template_source)
.to receive(:find) .to receive(:find)
.with(custom_template.key) .with(custom_template.key, nil)
.and_return(custom_template) .and_return(custom_template)
allow(fake_template_source) allow(fake_template_source)
......
...@@ -33,7 +33,7 @@ RSpec.describe TemplateFinder do ...@@ -33,7 +33,7 @@ RSpec.describe TemplateFinder do
allow(fake_template_source) allow(fake_template_source)
.to receive(:find) .to receive(:find)
.with(custom_template.key) .with(custom_template.key, nil)
.and_return(custom_template) .and_return(custom_template)
allow(fake_template_source) allow(fake_template_source)
......
...@@ -29,10 +29,10 @@ RSpec.describe BlobHelper do ...@@ -29,10 +29,10 @@ RSpec.describe BlobHelper do
expect(Gitlab::Template::CustomLicenseTemplate) expect(Gitlab::Template::CustomLicenseTemplate)
.to receive(:all) .to receive(:all)
.with(project) .with(project)
.and_return([OpenStruct.new(key: 'name', name: 'Name')]) .and_return([OpenStruct.new(key: 'name', name: 'Name', project_id: project.id)])
expect(categories).to contain_exactly(:Popular, :Other, group_category) expect(categories).to contain_exactly(:Popular, :Other, group_category)
expect(by_group).to contain_exactly({ id: 'name', name: 'Name', key: 'name', project_id: nil }) expect(by_group).to contain_exactly({ id: 'name', name: 'Name', key: 'name', project_id: project.id })
expect(by_popular).to be_present expect(by_popular).to be_present
expect(by_other).to be_present expect(by_other).to be_present
end end
...@@ -43,10 +43,10 @@ RSpec.describe BlobHelper do ...@@ -43,10 +43,10 @@ RSpec.describe BlobHelper do
expect(Gitlab::Template::CustomLicenseTemplate) expect(Gitlab::Template::CustomLicenseTemplate)
.to receive(:all) .to receive(:all)
.with(project) .with(project)
.and_return([OpenStruct.new(key: 'name', name: 'Name')]) .and_return([OpenStruct.new(key: 'name', name: 'Name', project_id: project.id)])
expect(categories).to contain_exactly(:Popular, :Other, 'Instance') expect(categories).to contain_exactly(:Popular, :Other, 'Instance')
expect(by_instance).to contain_exactly({ id: 'name', name: 'Name', key: 'name', project_id: nil }) expect(by_instance).to contain_exactly({ id: 'name', name: 'Name', key: 'name', project_id: project.id })
expect(by_popular).to be_present expect(by_popular).to be_present
expect(by_other).to be_present expect(by_other).to be_present
end end
......
...@@ -180,6 +180,44 @@ RSpec.describe Gitlab::CustomFileTemplates do ...@@ -180,6 +180,44 @@ RSpec.describe Gitlab::CustomFileTemplates do
it 'returns nil for an unknown key' do it 'returns nil for an unknown key' do
expect(templates.find('unknown')).to be_nil expect(templates.find('unknown')).to be_nil
end end
context 'when subgroup template names overlap with ancestor' do
let(:subgroup_template_project) { create(:project, :custom_repo, namespace: subgroup, files: template_files('group')) }
before do
subgroup.update!(file_template_project: subgroup_template_project)
end
it 'returns a template from the subgroup' do
expect(templates.find(group_key)).to be_template(group_key, "Group #{subgroup.full_name}")
end
it 'finds a template from the parent group with specified project' do
expect(templates.find(group_key, group_template_project.id)).to be_template(group_key, "Group #{group.full_name}")
end
end
end
context 'when looking for template for a specific project' do
let(:target_project) { project }
let_it_be(:another_project) { create(:project, :custom_repo, namespace: group, files: template_files('group')) }
it 'finds a valid template when looking into group template project' do
templates_find = templates.find(group_key, group_template_project.id)
expect(templates_find).to be_template(group_key, "Group #{group.full_name}")
end
it 'finds a valid template when looking into instance template project' do
templates_find = templates.find(instance_key, instance_template_project.id)
expect(templates_find).to be_template(instance_key, "Instance")
end
it 'does not find a template when given project does not have the template' do
expect(templates.find(group_key, another_project.id)).to be_nil
end
end end
end end
end end
......
...@@ -30,7 +30,7 @@ RSpec.describe "Custom file template classes" do ...@@ -30,7 +30,7 @@ RSpec.describe "Custom file template classes" do
'LICENSE/category/baz.txt' => 'CustomLicenseTemplate category baz' 'LICENSE/category/baz.txt' => 'CustomLicenseTemplate category baz'
} }
let(:project) { create(:project, :custom_repo, files: files) } let_it_be(:project) { create(:project, :custom_repo, files: files) }
[ [
::Gitlab::Template::CustomDockerfileTemplate, ::Gitlab::Template::CustomDockerfileTemplate,
......
...@@ -36,16 +36,22 @@ module API ...@@ -36,16 +36,22 @@ module API
end end
params do params do
requires :name, type: String, desc: 'The name of the template' requires :name, type: String, desc: 'The name of the template'
optional :source_template_project_id, type: Integer,
desc: 'The project id where a given template is being stored. This is useful when multiple templates from different projects have the same name'
optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses' optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses'
optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses' optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses'
end end
get ':id/templates/:type/:name', requirements: TEMPLATE_NAMES_ENDPOINT_REQUIREMENTS do get ':id/templates/:type/:name', requirements: TEMPLATE_NAMES_ENDPOINT_REQUIREMENTS do
begin begin
template = TemplateFinder template = TemplateFinder.build(
.build(params[:type], user_project, name: params[:name]) params[:type],
.execute user_project,
{
name: params[:name],
source_template_project_id: params[:source_template_project_id]
}
).execute
rescue ::Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError rescue ::Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
not_found!('Template') not_found!('Template')
end end
......
...@@ -8,6 +8,7 @@ module Gitlab ...@@ -8,6 +8,7 @@ module Gitlab
def initialize(path, project = nil, category: nil) def initialize(path, project = nil, category: nil)
@path = path @path = path
@category = category @category = category
@project = project
@finder = self.class.finder(project) @finder = self.class.finder(project)
end end
...@@ -31,6 +32,10 @@ module Gitlab ...@@ -31,6 +32,10 @@ module Gitlab
# override with a comment to be placed at the top of the blob. # override with a comment to be placed at the top of the blob.
end end
def project_id
@project&.id
end
# Present for compatibility with license templates, which can replace text # Present for compatibility with license templates, which can replace text
# like `[fullname]` with a user-specified string. This is a no-op for # like `[fullname]` with a user-specified string. This is a no-op for
# other templates # other templates
...@@ -76,7 +81,7 @@ module Gitlab ...@@ -76,7 +81,7 @@ module Gitlab
end end
# Defines which strategy will be used to get templates files # Defines which strategy will be used to get templates files
# RepoTemplateFinder - Finds templates on project repository, templates are filtered perproject # RepoTemplateFinder - Finds templates on project repository, templates are filtered per project
# GlobalTemplateFinder - Finds templates on gitlab installation source, templates can be used in all projects # GlobalTemplateFinder - Finds templates on gitlab installation source, templates can be used in all projects
def finder(project = nil) def finder(project = nil)
raise NotImplementedError raise NotImplementedError
......
...@@ -423,7 +423,7 @@ describe('Issuable output', () => { ...@@ -423,7 +423,7 @@ describe('Issuable output', () => {
}); });
it('shows the form if template names request is successful', () => { it('shows the form if template names request is successful', () => {
const mockData = [{ name: 'Bug' }]; const mockData = [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }];
mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData])); mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData]));
return wrapper.vm.requestTemplatesAndShowForm().then(() => { return wrapper.vm.requestTemplatesAndShowForm().then(() => {
......
...@@ -14,8 +14,10 @@ describe('Issue description template component', () => { ...@@ -14,8 +14,10 @@ describe('Issue description template component', () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
formState, formState,
issuableTemplates: [{ name: 'test' }], issuableTemplates: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
projectId: 1,
projectPath: '/', projectPath: '/',
namespacePath: '/',
projectNamespace: '/', projectNamespace: '/',
}, },
}).$mount(); }).$mount();
...@@ -23,7 +25,7 @@ describe('Issue description template component', () => { ...@@ -23,7 +25,7 @@ describe('Issue description template component', () => {
it('renders templates as JSON array in data attribute', () => { it('renders templates as JSON array in data attribute', () => {
expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe( expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe(
'[{"name":"test"}]', '[{"name":"test","id":"test","project_path":"/","namespace_path":"/"}]',
); );
}); });
......
...@@ -19,6 +19,7 @@ describe('Inline edit form component', () => { ...@@ -19,6 +19,7 @@ describe('Inline edit form component', () => {
markdownPreviewPath: '/', markdownPreviewPath: '/',
markdownDocsPath: '/', markdownDocsPath: '/',
projectPath: '/', projectPath: '/',
projectId: 1,
projectNamespace: '/', projectNamespace: '/',
}; };
...@@ -42,7 +43,11 @@ describe('Inline edit form component', () => { ...@@ -42,7 +43,11 @@ describe('Inline edit form component', () => {
}); });
it('renders template selector when templates exists', () => { it('renders template selector when templates exists', () => {
createComponent({ issuableTemplates: ['test'] }); createComponent({
issuableTemplates: [
{ name: 'test', id: 'test', project_path: 'test', namespace_path: 'test' },
],
});
expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull(); expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull();
}); });
......
...@@ -52,6 +52,7 @@ export const appProps = { ...@@ -52,6 +52,7 @@ export const appProps = {
markdownDocsPath: '/', markdownDocsPath: '/',
projectNamespace: '/', projectNamespace: '/',
projectPath: '/', projectPath: '/',
projectId: 1,
issuableTemplateNamesPath: '/issuable-templates-path', issuableTemplateNamesPath: '/issuable-templates-path',
zoomMeetingUrl, zoomMeetingUrl,
publishedIncidentUrl, publishedIncidentUrl,
......
...@@ -209,6 +209,7 @@ RSpec.describe IssuablesHelper do ...@@ -209,6 +209,7 @@ RSpec.describe IssuablesHelper do
markdownDocsPath: '/help/user/markdown', markdownDocsPath: '/help/user/markdown',
lockVersion: issue.lock_version, lockVersion: issue.lock_version,
projectPath: @project.path, projectPath: @project.path,
projectId: @project.id,
projectNamespace: @project.namespace.path, projectNamespace: @project.namespace.path,
initialTitleHtml: issue.title, initialTitleHtml: issue.title,
initialTitleText: issue.title, initialTitleText: issue.title,
......
...@@ -57,6 +57,6 @@ RSpec.describe LicenseTemplate do ...@@ -57,6 +57,6 @@ RSpec.describe LicenseTemplate do
end end
def build_template(content) def build_template(content)
described_class.new(key: 'foo', name: 'foo', category: :Other, content: content) described_class.new(key: 'foo', name: 'foo', project: nil, category: :Other, content: content)
end end
end end
...@@ -131,7 +131,7 @@ RSpec.describe API::ProjectTemplates do ...@@ -131,7 +131,7 @@ RSpec.describe API::ProjectTemplates do
end end
end end
describe 'GET /projects/:id/templates/:type/:key' do describe 'GET /projects/:id/templates/:type/:name' do
it 'returns a specific dockerfile' do it 'returns a specific dockerfile' do
get api("/projects/#{public_project.id}/templates/dockerfiles/Binary") get api("/projects/#{public_project.id}/templates/dockerfiles/Binary")
......
...@@ -23,11 +23,11 @@ RSpec.shared_examples 'project issuable templates' do ...@@ -23,11 +23,11 @@ RSpec.shared_examples 'project issuable templates' do
end end
it 'returns only md files as issue templates' do it 'returns only md files as issue templates' do
expect(helper.issuable_templates(project, 'issue')).to eq(templates('issue', nil)) expect(helper.issuable_templates(project, 'issue')).to eq(templates('issue', project))
end end
it 'returns only md files as merge_request templates' do it 'returns only md files as merge_request templates' do
expect(helper.issuable_templates(project, 'merge_request')).to eq(templates('merge_request', nil)) expect(helper.issuable_templates(project, 'merge_request')).to eq(templates('merge_request', project))
end end
end end
......
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