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,14 +98,44 @@
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) }
......
......@@ -8,7 +8,7 @@ def helper
end
let(:project) { create(:empty_project, :public) }
let(:issue) { create(:issue, project: project) }
let(:issue) { create(:issue, project: project) }
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
......@@ -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,68 +365,247 @@
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}") }
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 'points to referenced project issues page' do
expect(result.css('a').first.attr('href'))
.to eq urls.namespace_project_issues_url(another_project.namespace,
another_project,
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 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 'contains cross project content' do
expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
end
it 'has valid text' do
expect(result.text).to eq "See #{label.name} in #{project2.name_with_namespace}"
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'
end
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 '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
it 'has valid text' do
expect(result.text).to eq "See #{label.name} in #{project2.name}"
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 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'))