Read about our upcoming Code of Conduct on this issue

Commit f272ee6e authored by Oswaldo Ferreira's avatar Oswaldo Ferreira
Browse files

Add shorthand support to gitlab markdown references

parent 7706b77848d1
......@@ -5,7 +5,7 @@
describe '#references_per_project' do
it 'returns a Hash containing references grouped per project paths' do
doc = Nokogiri::HTML.fragment("#1 #{project.to_reference}#2")
doc = Nokogiri::HTML.fragment("#1 #{project.path_with_namespace}#2")
filter = described_class.new(doc, project: project)
expect(filter).to receive(:object_class).exactly(4).times.and_return(Issue)
......@@ -14,7 +14,7 @@
refs = filter.references_per_project
expect(refs).to be_an_instance_of(Hash)
expect(refs[project.to_reference]).to eq(Set.new(%w[1 2]))
expect(refs[project.path_with_namespace]).to eq(Set.new(%w[1 2]))
end
end
......
......@@ -59,9 +59,7 @@
it 'ignores invalid commit IDs' do
exp = act = "See #{commit1.id.reverse}...#{commit2.id}"
expect(project).to receive(:valid_repo?).and_return(true)
expect(project.repository).to receive(:commit).with(commit1.id.reverse)
expect(project.repository).to receive(:commit).with(commit2.id)
allow(project.repository).to receive(:commit).with(commit1.id.reverse)
expect(reference_filter(act).to_html).to eq exp
end
......@@ -100,15 +98,45 @@
end
end
context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:reference) { range.to_reference(project) }
context 'cross-project / cross-namespace complete reference' do
let(:project2) { create(:project, :public) }
let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" }
before do
range.project = project2
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.css('a').first.text).
to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.text).to eql("Fixed (#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp
exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project / same-namespace complete reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:project, :public, namespace: namespace) }
let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
......@@ -116,24 +144,65 @@
to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'links with adjacent text' do
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}")
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
expect(doc.css('a').first.text).
to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.text).to eql("Fixed (#{project2.path}@#{commit1.short_id}...#{commit2.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
exp = act = "Fixed #{project2.path}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp
exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
exp = act = "Fixed #{project2.path}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project shorthand reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:project, :public, namespace: namespace) }
let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.css('a').first.text).
to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.text).to eql("Fixed (#{project2.path}@#{commit1.short_id}...#{commit2.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Fixed #{project2.path}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp
exp = act = "Fixed #{project2.path}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:range) { CommitRange.new("#{commit1.id}...master", project) }
let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') }
......
......@@ -41,6 +41,7 @@
it 'links with adjacent text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/)
end
......@@ -48,8 +49,6 @@
invalid = invalidate_reference(reference)
exp = act = "See #{invalid}"
expect(project).to receive(:valid_repo?).and_return(true)
expect(project.repository).to receive(:commit).with(invalid)
expect(reference_filter(act).to_html).to eq exp
end
......@@ -95,34 +94,85 @@
end
end
context 'cross-project reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
context 'cross-project / cross-namespace complete reference' do
let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { commit.to_reference(project) }
let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}@#{commit.short_id}")
end
it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)")
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.text).to eql("See (#{project2.path_with_namespace}@#{commit.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project / same-namespace complete reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, namespace: namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
exp = Regexp.escape(project2.to_reference)
expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
expect(doc.css('a').first.text).to eql("#{project2.path}@#{commit.short_id}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.text).to eql("See (#{project2.path}@#{commit.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project shorthand reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, namespace: namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.css('a').first.text).to eql("#{project2.path}@#{commit.short_id}")
end
it 'has valid text' do
doc = reference_filter("See (#{reference}.)")
expect(doc.text).to eql("See (#{project2.path}@#{commit.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project URL reference' do
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) }
......
......@@ -24,7 +24,7 @@ def helper
context 'internal reference' do
it_behaves_like 'a reference containing an element node'
let(:reference) { issue.to_reference }
let(:reference) { "##{issue.iid}" }
it 'ignores valid references when using non-default tracker' do
allow(project).to receive(:default_issues_tracker?).and_return(false)
......@@ -42,7 +42,7 @@ def helper
it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
expect(doc.text).to eql("Fixed (#{reference}.)")
end
it 'ignores invalid issue IDs' do
......@@ -116,13 +116,56 @@ def helper
end
end
context 'cross-project reference' do
context 'cross-project / cross-namespace complete reference' do
it_behaves_like 'a reference containing an element node'
let(:namespace) { create(:namespace, name: 'cross-reference') }
let(:project2) { create(:empty_project, :public) }
let(:issue) { create(:issue, project: project2) }
let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(described_class).to receive(:find_object).
with(project2, issue.iid).
and_return(nil)
exp = act = "Issue #{reference}"
expect(reference_filter(act).to_html).to eq exp
end
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 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}##{issue.iid}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.text).to eq("Fixed (#{project2.path_with_namespace}##{issue.iid}.)")
end
it 'ignores invalid issue IDs on the referenced project' do
exp = act = "Fixed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project / same-namespace complete reference' do
it_behaves_like 'a reference containing an element node'
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
let(:reference) { issue.to_reference(project) }
let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(described_class).to receive(:find_object).
......@@ -140,9 +183,16 @@ def helper
to eq helper.url_for_issue(issue.iid, project2)
end
it 'links with adjacent text' do
it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
expect(doc.css('a').first.text).to eql("#{project2.path}##{issue.iid}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.text).to eq("Fixed (#{project2.path}##{issue.iid}.)")
end
it 'ignores invalid issue IDs on the referenced project' do
......@@ -150,9 +200,47 @@ def helper
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project shorthand reference' do
it_behaves_like 'a reference containing an element node'
it 'ignores out-of-bounds issue IDs on the referenced project' do
exp = act = "Fixed ##{Gitlab::Database::MAX_INT_VALUE + 1}"
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
let(:reference) { "#{project2.path}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(described_class).to receive(:find_object).
with(project2, issue.iid).
and_return(nil)
exp = act = "Issue #{reference}"
expect(reference_filter(act).to_html).to eq exp
end
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 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.css('a').first.text).to eql("#{project2.path}##{issue.iid}")
end
it 'has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
expect(doc.text).to eq("Fixed (#{project2.path}##{issue.iid}.)")
end
it 'ignores invalid issue IDs on the referenced project' do
exp = act = "Fixed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
......
......@@ -4,7 +4,7 @@
describe Banzai::Filter::LabelReferenceFilter, lib: true do
include FilterSpecHelper
let(:project) { create(:empty_project, :public) }
let(:project) { create(:empty_project, :public, name: 'sample-project') }
let(:label) { create(:label, project: project) }
let(:reference) { label.to_reference }
......@@ -48,6 +48,14 @@
expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name)
end
context 'project that does not exist referenced' do
let(:result) { reference_filter('aaa/bbb~ccc') }
it 'does not link reference' do
expect(result.to_html).to eq 'aaa/bbb~ccc'
end
end
describe 'label span element' do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
......@@ -334,14 +342,14 @@
end
context 'with project reference' do
let(:reference) { project.to_reference + group_label.to_reference(format: :name) }
let(:reference) { "#{project.to_reference}#{group_label.to_reference(format: :name)}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}", project: project)
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
expect(doc.text).to eq 'See gfm references'
expect(doc.text).to eq "See gfm references"
end
it 'links with adjacent text' do
......@@ -357,51 +365,114 @@
end
end
describe 'cross project label references' do
context 'valid project referenced' do
let(:another_project) { create(:empty_project, :public) }
let(:project_name) { another_project.name_with_namespace }
let(:label) { create(:label, project: another_project, color: '#00ff00') }
let(:reference) { label.to_reference(project) }
describe 'cross-project / cross-namespace complete reference' do
let(:project2) { create(:empty_project) }
let(:label) { create(:label, project: project2, color: '#00ff00') }
let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
let!(:result) { reference_filter("See #{reference}") }
it 'links to a valid reference' do
expect(result.css('a').first.attr('href'))
.to eq urls.namespace_project_issues_url(project2.namespace,
project2,
label_name: label.name)
end
it 'has valid color' do
expect(result.css('a span').first.attr('style')).to match /background-color: #00ff00/
end
it 'has valid link text' do
expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name_with_namespace}"
end
it 'has valid text' do
expect(result.text).to eq "See #{label.name} in #{project2.name_with_namespace}"
end
it 'ignores invalid IDs on the referenced label' do
exp = act = "See #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
end
describe 'cross-project / same-namespace complete reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, namespace: namespace) }
let(:project2) { create(:empty_project, namespace: namespace) }
let(:label) { create(:label, project: project2, color: '#00ff00') }
let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project issues page' do
it 'links to a valid reference' do
expect(result.css('a').first.attr('href'))
.to eq urls.namespace_project_issues_url(another_project.namespace,
another_project,
.to eq urls.namespace_project_issues_url(project2.namespace,
project2,
label_name: label.name)
end
it 'has valid color' do
expect(result.css('a span').first.attr('style'))
.to match /background-color: #00ff00/
expect(result.css('a span').first.attr('style')).to match /background-color: #00ff00/
end
it 'contains cross project content' do
expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
it 'has valid link text' do
expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name}"
end
it 'has valid text' do
expect(result.text).to eq "See #{label.name} in #{project2.name}"
end
context 'project that does not exist referenced' do
let(:result) { reference_filter('aaa/bbb~ccc') }
it 'ignores invalid IDs on the referenced label' do
exp = act = "See #{invalidate_reference(reference)}"
it 'does not link reference' do
expect(result.to_html).to eq 'aaa/bbb~ccc'
expect(reference_filter(act).to_html).to eq exp
end
end
describe 'cross-project shorthand reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:empty_project, namespace: namespace) }
let(:project2) { create(:empty_project, namespace: namespace) }
let(:label) { create(:label, project: project2, color: '#00ff00') }
let(:reference) { "#{project2.path}~#{label.name}" }
let!(:result) { reference_filter("See #{reference}") }
it 'links to a valid reference' do
expect(result.css('a').first.attr('href'))
.to eq urls.namespace_project_issues_url(project2.namespace,
project2,
label_name: label.name)
end
it 'has valid color' do
expect(result.css('a span').first.attr('style')).
to match /background-color: #00ff00/
end
it 'has valid link text' do
expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name}"
end