Autolink first so we don't pick up numeric anchors as issue references.

This commit is contained in:
Douwe Maan 2015-12-01 15:51:27 +01:00
parent 62c14ba2ed
commit f3ea06eb7f
21 changed files with 171 additions and 140 deletions

View file

@ -73,11 +73,8 @@ class Commit
# This pattern supports cross-project references. # This pattern supports cross-project references.
def self.reference_pattern def self.reference_pattern
%r{ %r{
#{link_reference_pattern} |
(?:
(?:#{Project.reference_pattern}#{reference_prefix})? (?:#{Project.reference_pattern}#{reference_prefix})?
(?<commit>\h{6,40}) (?<commit>\h{6,40})
)
}x }x
end end

View file

@ -44,11 +44,8 @@ class CommitRange
# This pattern supports cross-project references. # This pattern supports cross-project references.
def self.reference_pattern def self.reference_pattern
%r{ %r{
#{link_reference_pattern} |
(?:
(?:#{Project.reference_pattern}#{reference_prefix})? (?:#{Project.reference_pattern}#{reference_prefix})?
(?<commit_range>#{STRICT_PATTERN}) (?<commit_range>#{STRICT_PATTERN})
)
}x }x
end end

View file

@ -64,11 +64,8 @@ class Issue < ActiveRecord::Base
# This pattern supports cross-project references. # This pattern supports cross-project references.
def self.reference_pattern def self.reference_pattern
%r{ %r{
#{link_reference_pattern} |
(?:
(#{Project.reference_pattern})? (#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<issue>\d+) #{Regexp.escape(reference_prefix)}(?<issue>\d+)
)
}x }x
end end

View file

@ -146,11 +146,8 @@ class MergeRequest < ActiveRecord::Base
# This pattern supports cross-project references. # This pattern supports cross-project references.
def self.reference_pattern def self.reference_pattern
%r{ %r{
#{link_reference_pattern} |
(?:
(#{Project.reference_pattern})? (#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<merge_request>\d+) #{Regexp.escape(reference_prefix)}(?<merge_request>\d+)
)
}x }x
end end

View file

@ -60,11 +60,8 @@ class Snippet < ActiveRecord::Base
# This pattern supports cross-project references. # This pattern supports cross-project references.
def self.reference_pattern def self.reference_pattern
%r{ %r{
#{link_reference_pattern} |
(?:
(#{Project.reference_pattern})? (#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<snippet>\d+) #{Regexp.escape(reference_prefix)}(?<snippet>\d+)
)
}x }x
end end

View file

@ -2,7 +2,7 @@ module Gitlab
class ClosingIssueExtractor class ClosingIssueExtractor
ISSUE_CLOSING_REGEX = begin ISSUE_CLOSING_REGEX = begin
pattern = Gitlab.config.gitlab.issue_closing_pattern pattern = Gitlab.config.gitlab.issue_closing_pattern
pattern = pattern.sub('%{issue_ref}', "(?:#{Issue.reference_pattern})") pattern = pattern.sub('%{issue_ref}', "(?:(?:#{Issue.link_reference_pattern})|(?:#{Issue.reference_pattern}))")
Regexp.new(pattern).freeze Regexp.new(pattern).freeze
end end

View file

@ -181,6 +181,8 @@ module Gitlab
Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::RelativeLinkFilter,
Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::EmojiFilter,
Gitlab::Markdown::TableOfContentsFilter, Gitlab::Markdown::TableOfContentsFilter,
Gitlab::Markdown::AutolinkFilter,
Gitlab::Markdown::ExternalLinkFilter,
Gitlab::Markdown::UserReferenceFilter, Gitlab::Markdown::UserReferenceFilter,
Gitlab::Markdown::IssueReferenceFilter, Gitlab::Markdown::IssueReferenceFilter,
@ -191,9 +193,6 @@ module Gitlab
Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::CommitReferenceFilter,
Gitlab::Markdown::LabelReferenceFilter, Gitlab::Markdown::LabelReferenceFilter,
Gitlab::Markdown::AutolinkFilter,
Gitlab::Markdown::ExternalLinkFilter,
Gitlab::Markdown::TaskListFilter Gitlab::Markdown::TaskListFilter
] ]
end end

View file

@ -37,8 +37,8 @@ module Gitlab
# of the external project reference, and all of the matchdata. # of the external project reference, and all of the matchdata.
# #
# Returns a String replaced with the return of the block. # Returns a String replaced with the return of the block.
def self.references_in(text) def self.references_in(text, pattern = object_class.reference_pattern)
text.gsub(object_class.reference_pattern) do |match| text.gsub(pattern) do |match|
yield match, $~[object_sym].to_i, $~[:project], $~ yield match, $~[object_sym].to_i, $~[:project], $~
end end
end end
@ -61,7 +61,11 @@ module Gitlab
def call def call
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_link_filter(content, object_class.reference_pattern)
end
replace_link_nodes_matching(object_class.link_reference_pattern) do |content|
object_link_filter(content, object_class.link_reference_pattern)
end end
end end
@ -72,15 +76,17 @@ module Gitlab
# #
# Returns a String with references replaced with links. All links # Returns a String with references replaced with links. All links
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def object_link_filter(text) def object_link_filter(text, pattern)
references_in(text) do |match, id, project_ref, matches| references_in(text, pattern) do |match, id, project_ref, matches|
project = project_from_ref(project_ref) project = project_from_ref(project_ref)
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(project: project.id, object_sym => object.id, original: match)
url = matches[:url] || url_for_object(object, project)
url = matches[:url] if matches.names.include?("url")
url ||= url_for_object(object, project)
text = object.reference_link_text(context[:project]) text = object.reference_link_text(context[:project])
@ -99,7 +105,7 @@ module Gitlab
def object_link_text_extras(object, matches) def object_link_text_extras(object, matches)
extras = [] extras = []
if matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ if matches.names.include?("anchor") && matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/
extras << "comment #{$1}" extras << "comment #{$1}"
end end

View file

@ -10,8 +10,8 @@ module Gitlab
CommitRange CommitRange
end end
def self.references_in(text) def self.references_in(text, pattern = CommitRange.reference_pattern)
text.gsub(CommitRange.reference_pattern) do |match| text.gsub(pattern) do |match|
yield match, $~[:commit_range], $~[:project], $~ yield match, $~[:commit_range], $~[:project], $~
end end
end end

View file

@ -10,8 +10,8 @@ module Gitlab
Commit Commit
end end
def self.references_in(text) def self.references_in(text, pattern = Commit.reference_pattern)
text.gsub(Commit.reference_pattern) do |match| text.gsub(pattern) do |match|
yield match, $~[:commit], $~[:project], $~ yield match, $~[:commit], $~[:project], $~
end end
end end

View file

@ -8,12 +8,9 @@ module Gitlab
class ExternalLinkFilter < HTML::Pipeline::Filter class ExternalLinkFilter < HTML::Pipeline::Filter
def call def call
doc.search('a').each do |node| doc.search('a').each do |node|
next unless node.has_attribute?('href') link = node.attr('href')
klass = node.attribute('class') next unless link
next if klass && klass.include?('gfm')
link = node.attribute('href').value
# Skip non-HTTP(S) links # Skip non-HTTP(S) links
next unless link.start_with?('http') next unless link.start_with?('http')

View file

@ -24,7 +24,7 @@ module Gitlab
def object_link_text_extras(object, matches) def object_link_text_extras(object, matches)
extras = super extras = super
if matches[:path] && matches[:path] == '/diffs' if matches.names.include?("path") && matches[:path] && matches[:path] == '/diffs'
extras.unshift "diffs" extras.unshift "diffs"
end end

View file

@ -14,7 +14,7 @@ module Gitlab
unless user_can_reference?(node) unless user_can_reference?(node)
# The reference should be replaced by the original text, # The reference should be replaced by the original text,
# which is not always the same as the rendered text. # which is not always the same as the rendered text.
text = node.attribute('data-original') || node.text text = node.attr('data-original') || node.text
node.replace(text) node.replace(text)
end end
end end

View file

@ -122,6 +122,29 @@ module Gitlab
doc doc
end end
def replace_link_nodes_matching(pattern)
return doc if project.nil?
doc.search('a').each do |node|
klass = node.attr('class')
next if klass && klass.include?('gfm')
link = node.attr('href')
text = node.text
# Ignore ending punctionation like periods or commas
next unless link == text && text =~ /\A#{pattern}/
html = yield text
next if html == text
node.replace(html)
end
doc
end
# Ensure that a :project key exists in context # Ensure that a :project key exists in context
# #
# Note that while the key might exist, its value could be nil! # Note that while the key might exist, its value could be nil!

View file

@ -58,7 +58,15 @@ module Gitlab
reference_filter: filter reference_filter: filter
} }
pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context) # We need to autolink first to finds links to referables, and to prevent
# numeric anchors to be parsed as issue references.
filters = [
Gitlab::Markdown::AutolinkFilter,
filter,
Gitlab::Markdown::ReferenceGathererFilter
]
pipeline = HTML::Pipeline.new(filters, context)
result = pipeline.call(@text) result = pipeline.call(@text)
values = result[:references][filter_type].uniq values = result[:references][filter_type].uniq

View file

@ -18,7 +18,7 @@ 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}>Commit Range #{range.to_reference}</#{elem}>" exp = act = "<#{elem}>Commit Range #{range.to_reference}</#{elem}>"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
@ -27,14 +27,14 @@ module Gitlab::Markdown
let(:reference2) { range2.to_reference } let(:reference2) { range2.to_reference }
it 'links to a valid two-dot reference' do it 'links to a valid two-dot reference' do
doc = filter("See #{reference2}") doc = reference_filter("See #{reference2}")
expect(doc.css('a').first.attr('href')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param) to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param)
end end
it 'links to a valid three-dot reference' do it 'links to a valid three-dot reference' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param) to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param)
@ -46,12 +46,12 @@ module Gitlab::Markdown
exp = commit1.short_id + '...' + commit2.short_id exp = commit1.short_id + '...' + commit2.short_id
expect(filter("See #{reference}").css('a').first.text).to eq exp expect(reference_filter("See #{reference}").css('a').first.text).to eq exp
expect(filter("See #{reference2}").css('a').first.text).to eq exp expect(reference_filter("See #{reference2}").css('a').first.text).to eq exp
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("See (#{reference}.)") doc = reference_filter("See (#{reference}.)")
exp = Regexp.escape(range.reference_link_text) exp = Regexp.escape(range.reference_link_text)
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
@ -63,21 +63,21 @@ module Gitlab::Markdown
expect(project).to receive(:valid_repo?).and_return(true) 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(commit1.id.reverse)
expect(project.repository).to receive(:commit).with(commit2.id) expect(project.repository).to receive(:commit).with(commit2.id)
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'includes a title attribute' do it 'includes a title attribute' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('title')).to eq range.reference_title expect(doc.css('a').first.attr('title')).to eq range.reference_title
end end
it 'includes default classes' do it 'includes default classes' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range'
end end
it 'includes a data-project attribute' do it 'includes a data-project 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-project') expect(link).to have_attribute('data-project')
@ -85,7 +85,7 @@ module Gitlab::Markdown
end end
it 'includes a data-commit-range attribute' do it 'includes a data-commit-range 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-commit-range') expect(link).to have_attribute('data-commit-range')
@ -93,7 +93,7 @@ module Gitlab::Markdown
end end
it 'supports an :only_path option' do it 'supports an :only_path option' do
doc = filter("See #{reference}", only_path: true) doc = reference_filter("See #{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?://)
@ -116,14 +116,14 @@ module Gitlab::Markdown
end end
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')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}") exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}")
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
@ -131,10 +131,10 @@ module Gitlab::Markdown
it 'ignores invalid commit IDs on the referenced project' do 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.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'adds to the results hash' do it 'adds to the results hash' do
@ -154,14 +154,14 @@ module Gitlab::Markdown
end end
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')). expect(doc.css('a').first.attr('href')).
to eq reference to eq reference
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
exp = Regexp.escape(range.reference_link_text(project)) exp = Regexp.escape(range.reference_link_text(project))
expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
@ -169,10 +169,10 @@ module Gitlab::Markdown
it 'ignores invalid commit IDs on the referenced project' do 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.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'adds to the results hash' do it 'adds to the results hash' do

View file

@ -14,7 +14,7 @@ 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}>Commit #{commit.id}</#{elem}>" exp = act = "<#{elem}>Commit #{commit.id}</#{elem}>"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
@ -24,7 +24,7 @@ module Gitlab::Markdown
# Let's test a variety of commit SHA sizes just to be paranoid # Let's test a variety of commit SHA sizes just to be paranoid
[6, 8, 12, 18, 20, 32, 40].each do |size| [6, 8, 12, 18, 20, 32, 40].each do |size|
it "links to a valid reference of #{size} characters" do it "links to a valid reference of #{size} characters" do
doc = filter("See #{reference[0...size]}") doc = reference_filter("See #{reference[0...size]}")
expect(doc.css('a').first.text).to eq commit.short_id expect(doc.css('a').first.text).to eq commit.short_id
expect(doc.css('a').first.attr('href')). expect(doc.css('a').first.attr('href')).
@ -33,15 +33,15 @@ module Gitlab::Markdown
end end
it 'always uses the short ID as the link text' do it 'always uses the short ID as the link text' do
doc = filter("See #{commit.id}") doc = reference_filter("See #{commit.id}")
expect(doc.text).to eq "See #{commit.short_id}" expect(doc.text).to eq "See #{commit.short_id}"
doc = filter("See #{commit.id[0...6]}") doc = reference_filter("See #{commit.id[0...6]}")
expect(doc.text).to eq "See #{commit.short_id}" expect(doc.text).to eq "See #{commit.short_id}"
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("See (#{reference}.)") doc = reference_filter("See (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/)
end end
@ -51,28 +51,28 @@ module Gitlab::Markdown
expect(project).to receive(:valid_repo?).and_return(true) expect(project).to receive(:valid_repo?).and_return(true)
expect(project.repository).to receive(:commit).with(invalid) expect(project.repository).to receive(:commit).with(invalid)
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'includes a title attribute' do it 'includes a title attribute' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('title')).to eq commit.link_title expect(doc.css('a').first.attr('title')).to eq commit.link_title
end end
it 'escapes the title attribute' do it 'escapes the title attribute' do
allow_any_instance_of(Commit).to receive(:title).and_return(%{"></a>whatever<a title="}) allow_any_instance_of(Commit).to receive(:title).and_return(%{"></a>whatever<a title="})
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.text).to eq "See #{commit.short_id}" expect(doc.text).to eq "See #{commit.short_id}"
end end
it 'includes default classes' do it 'includes default classes' do
doc = filter("See #{reference}") doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit'
end end
it 'includes a data-project attribute' do it 'includes a data-project 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-project') expect(link).to have_attribute('data-project')
@ -80,7 +80,7 @@ module Gitlab::Markdown
end end
it 'includes a data-commit attribute' do it 'includes a data-commit 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-commit') expect(link).to have_attribute('data-commit')
@ -88,7 +88,7 @@ module Gitlab::Markdown
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
doc = filter("See #{reference}", only_path: true) doc = reference_filter("See #{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?://)
@ -108,14 +108,14 @@ module Gitlab::Markdown
let(:reference) { commit.to_reference(project) } let(:reference) { commit.to_reference(project) }
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')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
exp = Regexp.escape(project2.to_reference) exp = Regexp.escape(project2.to_reference)
expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
@ -123,7 +123,7 @@ module Gitlab::Markdown
it 'ignores invalid commit IDs on the referenced project' do it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}" exp = act = "Committed #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'adds to the results hash' do it 'adds to the results hash' do
@ -139,21 +139,21 @@ module Gitlab::Markdown
let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) } let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) }
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')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{commit.reference_link_text(project)}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{commit.reference_link_text(project)}<\/a>\.\)/)
end end
it 'ignores invalid commit IDs on the referenced project' do it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}" exp = act = "Committed #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/)
end end
it 'adds to the results hash' do it 'adds to the results hash' do

View file

@ -18,7 +18,7 @@ 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}>Issue #{issue.to_reference}</#{elem}>" exp = act = "<#{elem}>Issue #{issue.to_reference}</#{elem}>"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
@ -29,18 +29,18 @@ module Gitlab::Markdown
expect(project).to receive(:get_issue).with(issue.iid).and_return(nil) expect(project).to receive(:get_issue).with(issue.iid).and_return(nil)
exp = act = "Issue #{reference}" exp = act = "Issue #{reference}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'links to a valid reference' do it 'links to a valid reference' do
doc = filter("Fixed #{reference}") doc = reference_filter("Fixed #{reference}")
expect(doc.css('a').first.attr('href')). expect(doc.css('a').first.attr('href')).
to eq helper.url_for_issue(issue.iid, project) to eq helper.url_for_issue(issue.iid, project)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end end
@ -48,28 +48,28 @@ module Gitlab::Markdown
invalid = invalidate_reference(reference) invalid = invalidate_reference(reference)
exp = act = "Fixed #{invalid}" exp = act = "Fixed #{invalid}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'includes a title attribute' do it 'includes a title attribute' do
doc = filter("Issue #{reference}") doc = reference_filter("Issue #{reference}")
expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}" expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}"
end end
it 'escapes the title attribute' do it 'escapes the title attribute' do
issue.update_attribute(:title, %{"></a>whatever<a title="}) issue.update_attribute(:title, %{"></a>whatever<a title="})
doc = filter("Issue #{reference}") doc = reference_filter("Issue #{reference}")
expect(doc.text).to eq "Issue #{reference}" expect(doc.text).to eq "Issue #{reference}"
end end
it 'includes default classes' do it 'includes default classes' do
doc = filter("Issue #{reference}") doc = reference_filter("Issue #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue'
end end
it 'includes a data-project attribute' do it 'includes a data-project attribute' do
doc = filter("Issue #{reference}") doc = reference_filter("Issue #{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')
@ -77,7 +77,7 @@ module Gitlab::Markdown
end end
it 'includes a data-issue attribute' do it 'includes a data-issue 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-issue') expect(link).to have_attribute('data-issue')
@ -85,7 +85,7 @@ module Gitlab::Markdown
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
doc = filter("Issue #{reference}", only_path: true) doc = reference_filter("Issue #{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?://)
@ -109,25 +109,25 @@ module Gitlab::Markdown
with(issue.iid).and_return(nil) with(issue.iid).and_return(nil)
exp = act = "Issue #{reference}" exp = act = "Issue #{reference}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
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')). expect(doc.css('a').first.attr('href')).
to eq helper.url_for_issue(issue.iid, project2) to eq helper.url_for_issue(issue.iid, project2)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Fixed (#{reference}.)") doc = reference_filter("Fixed (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end end
it 'ignores invalid issue IDs on the referenced project' do it 'ignores invalid issue IDs on the referenced project' do
exp = act = "Fixed #{invalidate_reference(reference)}" exp = act = "Fixed #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'adds to the results hash' do it 'adds to the results hash' do
@ -147,18 +147,18 @@ module Gitlab::Markdown
with(issue.iid).and_return(nil) with(issue.iid).and_return(nil)
exp = act = "Issue #{reference}" exp = act = "Issue #{reference}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(reference)}<\/a>/)
end end
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')). expect(doc.css('a').first.attr('href')).
to eq reference to eq reference
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = 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.+>#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/)
end end

View file

@ -14,7 +14,7 @@ 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}>Merge #{merge.to_reference}</#{elem}>" exp = act = "<#{elem}>Merge #{merge.to_reference}</#{elem}>"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
@ -22,42 +22,42 @@ module Gitlab::Markdown
let(:reference) { merge.to_reference } let(:reference) { merge.to_reference }
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_merge_request_url(project.namespace, project, merge) namespace_project_merge_request_url(project.namespace, project, merge)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Merge (#{reference}.)") doc = reference_filter("Merge (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end end
it 'ignores invalid merge IDs' do it 'ignores invalid merge IDs' do
exp = act = "Merge #{invalidate_reference(reference)}" exp = act = "Merge #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'includes a title attribute' do it 'includes a title attribute' do
doc = filter("Merge #{reference}") doc = reference_filter("Merge #{reference}")
expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}" expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}"
end end
it 'escapes the title attribute' do it 'escapes the title attribute' do
merge.update_attribute(:title, %{"></a>whatever<a title="}) merge.update_attribute(:title, %{"></a>whatever<a title="})
doc = filter("Merge #{reference}") doc = reference_filter("Merge #{reference}")
expect(doc.text).to eq "Merge #{reference}" expect(doc.text).to eq "Merge #{reference}"
end end
it 'includes default classes' do it 'includes default classes' do
doc = filter("Merge #{reference}") doc = reference_filter("Merge #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request'
end end
it 'includes a data-project attribute' do it 'includes a data-project attribute' do
doc = filter("Merge #{reference}") doc = reference_filter("Merge #{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')
@ -65,7 +65,7 @@ module Gitlab::Markdown
end end
it 'includes a data-merge-request attribute' do it 'includes a data-merge-request 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-merge-request') expect(link).to have_attribute('data-merge-request')
@ -73,7 +73,7 @@ module Gitlab::Markdown
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
doc = filter("Merge #{reference}", only_path: true) doc = reference_filter("Merge #{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?://)
@ -93,7 +93,7 @@ module Gitlab::Markdown
let(:reference) { merge.to_reference(project) } let(:reference) { merge.to_reference(project) }
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')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_merge_request_url(project2.namespace, to eq urls.namespace_project_merge_request_url(project2.namespace,
@ -101,14 +101,14 @@ module Gitlab::Markdown
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Merge (#{reference}.)") doc = reference_filter("Merge (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end end
it 'ignores invalid merge IDs on the referenced project' do it 'ignores invalid merge IDs on the referenced project' do
exp = act = "Merge #{invalidate_reference(reference)}" exp = act = "Merge #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'adds to the results hash' do it 'adds to the results hash' do
@ -124,14 +124,14 @@ module Gitlab::Markdown
let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' } let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' }
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')). expect(doc.css('a').first.attr('href')).
to eq reference to eq reference
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Merge (#{reference}.)") doc = reference_filter("Merge (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/)
end end

View file

@ -15,48 +15,48 @@ 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}>Snippet #{reference}</#{elem}>" exp = act = "<#{elem}>Snippet #{reference}</#{elem}>"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
context 'internal reference' do context 'internal reference' 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_snippet_url(project.namespace, project, snippet) namespace_project_snippet_url(project.namespace, project, snippet)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("Snippet (#{reference}.)") doc = reference_filter("Snippet (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end end
it 'ignores invalid snippet IDs' do it 'ignores invalid snippet IDs' do
exp = act = "Snippet #{invalidate_reference(reference)}" exp = act = "Snippet #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'includes a title attribute' do it 'includes a title attribute' do
doc = filter("Snippet #{reference}") doc = reference_filter("Snippet #{reference}")
expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}" expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}"
end end
it 'escapes the title attribute' do it 'escapes the title attribute' do
snippet.update_attribute(:title, %{"></a>whatever<a title="}) snippet.update_attribute(:title, %{"></a>whatever<a title="})
doc = filter("Snippet #{reference}") doc = reference_filter("Snippet #{reference}")
expect(doc.text).to eq "Snippet #{reference}" expect(doc.text).to eq "Snippet #{reference}"
end end
it 'includes default classes' do it 'includes default classes' do
doc = filter("Snippet #{reference}") doc = reference_filter("Snippet #{reference}")
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet' expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet'
end end
it 'includes a data-project attribute' do it 'includes a data-project attribute' do
doc = filter("Snippet #{reference}") doc = reference_filter("Snippet #{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')
@ -64,7 +64,7 @@ module Gitlab::Markdown
end end
it 'includes a data-snippet attribute' do it 'includes a data-snippet 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-snippet') expect(link).to have_attribute('data-snippet')
@ -72,7 +72,7 @@ module Gitlab::Markdown
end end
it 'supports an :only_path context' do it 'supports an :only_path context' do
doc = filter("Snippet #{reference}", only_path: true) doc = reference_filter("Snippet #{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?://)
@ -92,21 +92,21 @@ module Gitlab::Markdown
let(:reference) { snippet.to_reference(project) } let(:reference) { snippet.to_reference(project) }
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')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("See (#{reference}.)") doc = reference_filter("See (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
end end
it 'ignores invalid snippet IDs on the referenced project' do it 'ignores invalid snippet IDs on the referenced project' do
exp = act = "See #{invalidate_reference(reference)}" exp = act = "See #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
it 'adds to the results hash' do it 'adds to the results hash' do
@ -122,21 +122,21 @@ module Gitlab::Markdown
let(:reference) { urls.namespace_project_snippet_url(project2.namespace, project2, snippet) } let(:reference) { urls.namespace_project_snippet_url(project2.namespace, project2, snippet) }
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')). expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = filter("See (#{reference}.)") doc = reference_filter("See (#{reference}.)")
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(snippet.to_reference(project))}<\/a>\.\)/) expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(snippet.to_reference(project))}<\/a>\.\)/)
end end
it 'ignores invalid snippet IDs on the referenced project' do it 'ignores invalid snippet IDs on the referenced project' do
exp = act = "See #{invalidate_reference(reference)}" exp = act = "See #{invalidate_reference(reference)}"
expect(filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/)
end end
it 'adds to the results hash' do it 'adds to the results hash' do

View file

@ -35,11 +35,24 @@ module FilterSpecHelper
pipeline.call(body) pipeline.call(body)
end end
def reference_pipeline_result(body, contexts = {}) def reference_pipeline(contexts = {})
contexts.reverse_merge!(project: project) if defined?(project) contexts.reverse_merge!(project: project) if defined?(project)
pipeline = HTML::Pipeline.new([described_class, Gitlab::Markdown::ReferenceGathererFilter], contexts) filters = [
pipeline.call(body) Gitlab::Markdown::AutolinkFilter,
described_class,
Gitlab::Markdown::ReferenceGathererFilter
]
HTML::Pipeline.new(filters, contexts)
end
def reference_pipeline_result(body, contexts = {})
reference_pipeline(contexts).call(body)
end
def reference_filter(html, contexts = {})
reference_pipeline(contexts).to_document(html)
end end
# Modify a String reference to make it invalid # Modify a String reference to make it invalid