Commit d4030a84 authored by Douwe Maan's avatar Douwe Maan

Pick up direct links to issues/MRs as references.

parent 1d6d757d
...@@ -60,17 +60,27 @@ module Gitlab ...@@ -60,17 +60,27 @@ module Gitlab
end end
def call def call
# `#123`
replace_text_nodes_matching(object_class.reference_pattern) do |content| replace_text_nodes_matching(object_class.reference_pattern) do |content|
object_link_filter(content, object_class.reference_pattern) object_link_filter(content, object_class.reference_pattern)
end end
# `[Issue](#123)`, which is turned into
# `<a href="#123">Issue</a>`
replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
object_link_filter(link, object_class.reference_pattern, link_text: text) object_link_filter(link, object_class.reference_pattern, link_text: text)
end end
# `http://gitlab.example.com/namespace/project/issues/123`, which is turned into
# `<a href="http://gitlab.example.com/namespace/project/issues/123">http://gitlab.example.com/namespace/project/issues/123</a>`
replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
object_link_filter(text, object_class.link_reference_pattern) object_link_filter(text, object_class.link_reference_pattern)
end end
# `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into
# `<a href="http://gitlab.example.com/namespace/project/issues/123">Issue</a>`
replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text|
object_link_filter(link, object_class.link_reference_pattern, link_text: text)
end end
end end
...@@ -88,7 +98,12 @@ module Gitlab ...@@ -88,7 +98,12 @@ module Gitlab
if project && object = find_object(project, id) if project && object = find_object(project, id)
title = escape_once(object_link_title(object)) title = escape_once(object_link_title(object))
klass = reference_class(object_sym) klass = reference_class(object_sym)
data = data_attribute(project: project.id, object_sym => object.id, original: match)
data = data_attribute(
original: link_text || match,
project: project.id,
object_sym => object.id
)
url = matches[:url] if matches.names.include?("url") url = matches[:url] if matches.names.include?("url")
url ||= url_for_object(object, project) url ||= url_for_object(object, project)
......
...@@ -143,7 +143,7 @@ module Gitlab::Markdown ...@@ -143,7 +143,7 @@ module Gitlab::Markdown
end end
end end
context 'URL cross-project reference' do context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, :public, namespace: namespace) } let(:project2) { create(:project, :public, namespace: namespace) }
let(:range) { CommitRange.new("#{commit1.id}...master", project) } let(:range) { CommitRange.new("#{commit1.id}...master", project) }
......
...@@ -132,7 +132,7 @@ module Gitlab::Markdown ...@@ -132,7 +132,7 @@ module Gitlab::Markdown
end end
end end
context 'URL cross-project reference' do context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, :public, namespace: namespace) } let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit } let(:commit) { project2.commit }
......
...@@ -136,30 +136,70 @@ module Gitlab::Markdown ...@@ -136,30 +136,70 @@ module Gitlab::Markdown
end end
end end
context 'URL cross-project reference' do context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) } let(:issue) { create(:issue, project: project2) }
let(:reference) { helper.url_for_issue(issue.iid, project2) + "#note_123" } let(:reference) { helper.url_for_issue(issue.iid, project2) + "#note_123" }
it 'ignores valid references when cross-reference project uses external tracker' do it 'links to a valid reference' do
expect_any_instance_of(Project).to receive(:get_issue). doc = reference_filter("See #{reference}")
with(issue.iid).and_return(nil)
exp = act = "Issue #{reference}" expect(doc.css('a').first.attr('href')).
expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(reference)}<\/a>/) to eq reference
end
it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/)
end
it 'adds to the results hash' do
result = reference_pipeline_result("Fixed #{reference}")
expect(result[:references][:issue]).to eq [issue]
end
end
context 'cross-project reference in link href' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
let(:reference) { %Q{<a href="#{issue.to_reference(project)}">Reference</a>} }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq helper.url_for_issue(issue.iid, project2)
end
it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
end end
it 'adds to the results hash' do
result = reference_pipeline_result("Fixed #{reference}")
expect(result[:references][:issue]).to eq [issue]
end
end
context 'cross-project URL in link href' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
let(:reference) { %Q{<a href="#{helper.url_for_issue(issue.iid, project2) + "#note_123"}">Reference</a>} }
it 'links to a valid reference' do it 'links to a valid reference' do
doc = reference_filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')). expect(doc.css('a').first.attr('href')).
to eq reference to eq helper.url_for_issue(issue.iid, project2) + "#note_123"
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
end end
it 'adds to the results hash' do it 'adds to the results hash' do
......
...@@ -16,17 +16,17 @@ module Gitlab::Markdown ...@@ -16,17 +16,17 @@ module Gitlab::Markdown
%w(pre code a style).each do |elem| %w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Label #{reference}</#{elem}>" exp = act = "<#{elem}>Label #{reference}</#{elem}>"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
it 'includes default classes' do it 'includes default classes' do
doc = filter("Label #{reference}") doc = reference_filter("Label #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label'
end end
it 'includes a data-project attribute' do it 'includes a data-project attribute' do
doc = filter("Label #{reference}") doc = reference_filter("Label #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-project') expect(link).to have_attribute('data-project')
...@@ -34,7 +34,7 @@ module Gitlab::Markdown ...@@ -34,7 +34,7 @@ module Gitlab::Markdown
end end
it 'includes a data-label attribute' do it 'includes a data-label attribute' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-label') expect(link).to have_attribute('data-label')
...@@ -42,7 +42,7 @@ module Gitlab::Markdown ...@@ -42,7 +42,7 @@ module Gitlab::Markdown
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
doc = filter("Label #{reference}", only_path: true) doc = reference_filter("Label #{reference}", only_path: true)
link = doc.css('a').first.attr('href') link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://) expect(link).not_to match %r(https?://)
...@@ -56,33 +56,33 @@ module Gitlab::Markdown ...@@ -56,33 +56,33 @@ module Gitlab::Markdown
describe 'label span element' do describe 'label span element' do
it 'includes default classes' do it 'includes default classes' do
doc = filter("Label #{reference}") doc = reference_filter("Label #{reference}")
expect(doc.css('a span').first.attr('class')).to eq 'label color-label' expect(doc.css('a span').first.attr('class')).to eq 'label color-label'
end end
it 'includes a style attribute' do it 'includes a style attribute' do
doc = filter("Label #{reference}") doc = reference_filter("Label #{reference}")
expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/) expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/)
end end
end end
context 'Integer-based references' do context 'Integer-based references' do
it 'links to a valid reference' do it 'links to a valid reference' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls. expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name) namespace_project_issues_url(project.namespace, project, label_name: label.name)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Label (#{reference}.)") doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\))) expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
end end
it 'ignores invalid label IDs' do it 'ignores invalid label IDs' do
exp = act = "Label #{invalidate_reference(reference)}" exp = act = "Label #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
...@@ -91,7 +91,7 @@ module Gitlab::Markdown ...@@ -91,7 +91,7 @@ module Gitlab::Markdown
let(:reference) { "#{Label.reference_prefix}#{label.name}" } let(:reference) { "#{Label.reference_prefix}#{label.name}" }
it 'links to a valid reference' do it 'links to a valid reference' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls. expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name) namespace_project_issues_url(project.namespace, project, label_name: label.name)
...@@ -99,14 +99,14 @@ module Gitlab::Markdown ...@@ -99,14 +99,14 @@ module Gitlab::Markdown
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Label (#{reference}.)") doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\))) expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
end end
it 'ignores invalid label names' do it 'ignores invalid label names' do
exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}" exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
...@@ -115,7 +115,7 @@ module Gitlab::Markdown ...@@ -115,7 +115,7 @@ module Gitlab::Markdown
let(:reference) { label.to_reference(:name) } let(:reference) { label.to_reference(:name) }
it 'links to a valid reference' do it 'links to a valid reference' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls. expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name) namespace_project_issues_url(project.namespace, project, label_name: label.name)
...@@ -123,21 +123,58 @@ module Gitlab::Markdown ...@@ -123,21 +123,58 @@ module Gitlab::Markdown
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Label (#{reference}.)") doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\))) expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
end end
it 'ignores invalid label names' do it 'ignores invalid label names' do
exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}")
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
describe 'edge cases' do describe 'edge cases' do
it 'gracefully handles non-references matching the pattern' do it 'gracefully handles non-references matching the pattern' do
exp = act = '(format nil "~0f" 3.0) ; 3.0' exp = act = '(format nil "~0f" 3.0) ; 3.0'
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end
end
describe 'referencing a label in a link href' do
let(:reference) { %Q{<a href="#{label.to_reference}">Label</a>} }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name)
end
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+>Label</a>\.\)))
end
it 'includes a data-project attribute' do
doc = reference_filter("Label #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
expect(link.attr('data-project')).to eq project.id.to_s
end
it 'includes a data-label attribute' do
doc = reference_filter("See #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-label')
expect(link.attr('data-label')).to eq label.id.to_s
end
it 'adds to the results hash' do
result = reference_pipeline_result("Label #{reference}")
expect(result[:references][:label]).to eq [label]
end end
end end
end end
......
...@@ -117,7 +117,7 @@ module Gitlab::Markdown ...@@ -117,7 +117,7 @@ module Gitlab::Markdown
end end
end end
context 'URL cross-project reference' do context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, :public, namespace: namespace) } let(:project2) { create(:project, :public, namespace: namespace) }
let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } let(:merge) { create(:merge_request, source_project: project2, target_project: project2) }
......
...@@ -115,7 +115,7 @@ module Gitlab::Markdown ...@@ -115,7 +115,7 @@ module Gitlab::Markdown
end end
end end
context 'URL cross-project reference' do context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') } let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:snippet) { create(:project_snippet, project: project2) } let(:snippet) { create(:project_snippet, project: project2) }
......
...@@ -14,13 +14,13 @@ module Gitlab::Markdown ...@@ -14,13 +14,13 @@ module Gitlab::Markdown
it 'ignores invalid users' do it 'ignores invalid users' do
exp = act = "Hey #{invalidate_reference(reference)}" exp = act = "Hey #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq(exp) expect(reference_filter(act).to_html).to eq(exp)
end end
%w(pre code a style).each do |elem| %w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Hey #{reference}</#{elem}>" exp = act = "<#{elem}>Hey #{reference}</#{elem}>"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
...@@ -32,7 +32,7 @@ module Gitlab::Markdown ...@@ -32,7 +32,7 @@ module Gitlab::Markdown
end end
it 'supports a special @all mention' do it 'supports a special @all mention' do
doc = filter("Hey #{reference}") doc = reference_filter("Hey #{reference}")
expect(doc.css('a').length).to eq 1 expect(doc.css('a').length).to eq 1
expect(doc.css('a').first.attr('href')) expect(doc.css('a').first.attr('href'))
.to eq urls.namespace_project_url(project.namespace, project) .to eq urls.namespace_project_url(project.namespace, project)
...@@ -46,26 +46,26 @@ module Gitlab::Markdown ...@@ -46,26 +46,26 @@ module Gitlab::Markdown
context 'mentioning a user' do context 'mentioning a user' do
it 'links to a User' do it 'links to a User' do
doc = filter("Hey #{reference}") doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
end end
it 'links to a User with a period' do it 'links to a User with a period' do
user = create(:user, name: 'alphA.Beta') user = create(:user, name: 'alphA.Beta')
doc = filter("Hey #{user.to_reference}") doc = reference_filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1 expect(doc.css('a').length).to eq 1
end end
it 'links to a User with an underscore' do it 'links to a User with an underscore' do
user = create(:user, name: 'ping_pong_king') user = create(:user, name: 'ping_pong_king')
doc = filter("Hey #{user.to_reference}") doc = reference_filter("Hey #{user.to_reference}")
expect(doc.css('a').length).to eq 1 expect(doc.css('a').length).to eq 1
end end
it 'includes a data-user attribute' do it 'includes a data-user attribute' do
doc = filter("Hey #{reference}") doc = reference_filter("Hey #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-user') expect(link).to have_attribute('data-user')
...@@ -83,12 +83,12 @@ module Gitlab::Markdown ...@@ -83,12 +83,12 @@ module Gitlab::Markdown
let(:reference) { group.to_reference } let(:reference) { group.to_reference }
it 'links to the Group' do it 'links to the Group' do
doc = filter("Hey #{reference}") doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
end end
it 'includes a data-group attribute' do it 'includes a data-group attribute' do
doc = filter("Hey #{reference}") doc = reference_filter("Hey #{reference}")
link = doc.css('a').first link = doc.css('a').first
expect(link).to have_attribute('data-group') expect(link).to have_attribute('data-group')
...@@ -102,21 +102,48 @@ module Gitlab::Markdown ...@@ -102,21 +102,48 @@ module Gitlab::Markdown
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Mention me (#{reference}.)") doc = reference_filter("Mention me (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{reference}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{reference}<\/a>\.\)/)
end end
it 'includes default classes' do it 'includes default classes' do
doc = filter("Hey #{reference}") doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member'
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
doc = filter("Hey #{reference}", only_path: true) doc = reference_filter("Hey #{reference}", only_path: true)
link = doc.css('a').first.attr('href') link = doc.css('a').first.attr('href')
expect(link).not_to match %r(https?://) expect(link).not_to match %r(https?://)
expect(link).to eq urls.user_path(user) expect(link).to eq urls.user_path(user)
end end
context 'referencing a user in a link href' do
let(:reference) { %Q{<a href="#{user.to_reference}">User</a>} }
it 'links to a User' do
doc = reference_filter("Hey #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
end
it 'links with adjacent text' do
doc = reference_filter("Mention me (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>User<\/a>\.\)/)
end
it 'includes a data-user attribute' do
doc = reference_filter("Hey #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-user')
expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
end
it 'adds to the results hash' do
result = reference_pipeline_result("Hey #{reference}")
expect(result[:references][:user]).to eq [user]
end
end
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