2019-12-21 20:55:43 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-04-26 12:48:37 +05:30
|
|
|
require 'spec_helper'
|
2014-09-02 18:07:02 +05:30
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
RSpec.describe MarkupHelper do
|
2020-04-08 14:13:33 +05:30
|
|
|
let_it_be(:project) { create(:project, :repository) }
|
|
|
|
let_it_be(:user) do
|
2019-10-31 01:37:42 +05:30
|
|
|
user = create(:user, username: 'gfm')
|
2018-11-18 11:00:15 +05:30
|
|
|
project.add_maintainer(user)
|
2019-10-31 01:37:42 +05:30
|
|
|
user
|
|
|
|
end
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
let_it_be(:issue) { create(:issue, project: project) }
|
|
|
|
let_it_be(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
|
|
|
|
let_it_be(:snippet) { create(:project_snippet, project: project) }
|
2021-04-29 21:17:54 +05:30
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
let(:commit) { project.commit }
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
before do
|
2014-09-02 18:07:02 +05:30
|
|
|
# Helper expects a @project instance variable
|
2015-10-24 18:46:33 +05:30
|
|
|
helper.instance_variable_set(:@project, project)
|
|
|
|
|
|
|
|
# Stub the `current_user` helper
|
|
|
|
allow(helper).to receive(:current_user).and_return(user)
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2015-09-25 12:07:36 +05:30
|
|
|
describe "#markdown" do
|
2014-09-02 18:07:02 +05:30
|
|
|
describe "referencing multiple objects" do
|
2015-09-11 14:41:01 +05:30
|
|
|
let(:actual) { "#{merge_request.to_reference} -> #{commit.to_reference} -> #{issue.to_reference}" }
|
2014-09-02 18:07:02 +05:30
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
it "links to the merge request" do
|
2018-11-20 20:47:30 +05:30
|
|
|
expected = urls.project_merge_request_path(project, merge_request)
|
2015-10-24 18:46:33 +05:30
|
|
|
expect(helper.markdown(actual)).to match(expected)
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
it "links to the commit" do
|
2018-11-20 20:47:30 +05:30
|
|
|
expected = urls.project_commit_path(project, commit)
|
2015-10-24 18:46:33 +05:30
|
|
|
expect(helper.markdown(actual)).to match(expected)
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
it "links to the issue" do
|
2018-11-20 20:47:30 +05:30
|
|
|
expected = urls.project_issue_path(project, issue)
|
2015-10-24 18:46:33 +05:30
|
|
|
expect(helper.markdown(actual)).to match(expected)
|
2015-09-25 12:07:36 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "override default project" do
|
|
|
|
let(:actual) { issue.to_reference }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
let_it_be(:second_project) { create(:project, :public) }
|
|
|
|
let_it_be(:second_issue) { create(:issue, project: second_project) }
|
2015-09-25 12:07:36 +05:30
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
it 'links to the issue' do
|
2018-11-20 20:47:30 +05:30
|
|
|
expected = urls.project_issue_path(second_project, second_issue)
|
2015-09-25 12:07:36 +05:30
|
|
|
expect(markdown(actual, project: second_project)).to match(expected)
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
describe 'uploads' do
|
|
|
|
let(:text) { "![ImageTest](/uploads/test.png)" }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
let_it_be(:group) { create(:group) }
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
subject { helper.markdown(text) }
|
|
|
|
|
|
|
|
describe 'inside a project' do
|
|
|
|
it 'renders uploads relative to project' do
|
|
|
|
expect(subject).to include("#{project.full_path}/uploads/test.png")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'inside a group' do
|
|
|
|
before do
|
|
|
|
helper.instance_variable_set(:@group, group)
|
|
|
|
helper.instance_variable_set(:@project, nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'renders uploads relative to the group' do
|
|
|
|
expect(subject).to include("#{group.full_path}/-/uploads/test.png")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "with a group in the context" do
|
2020-04-08 14:13:33 +05:30
|
|
|
let_it_be(:project_in_group) { create(:project, group: group) }
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
before do
|
|
|
|
helper.instance_variable_set(:@group, group)
|
|
|
|
helper.instance_variable_set(:@project, project_in_group)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'renders uploads relative to project' do
|
|
|
|
expect(subject).to include("#{project_in_group.path_with_namespace}/uploads/test.png")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
context 'when text contains a relative link to an image in the repository' do
|
|
|
|
let(:image_file) { "logo-white.png" }
|
|
|
|
let(:text_with_relative_path) { "![](./#{image_file})\n" }
|
2020-05-24 23:13:21 +05:30
|
|
|
let(:generated_html) { helper.markdown(text_with_relative_path, requested_path: requested_path, ref: ref) }
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
subject { Nokogiri::HTML.parse(generated_html) }
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
context 'when requested_path is provided, but ref isn\'t' do
|
2019-12-26 22:10:19 +05:30
|
|
|
let(:requested_path) { 'files/images/README.md' }
|
2020-05-24 23:13:21 +05:30
|
|
|
let(:ref) { nil }
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
it 'returns the correct HTML for the image' do
|
2020-03-13 15:44:24 +05:30
|
|
|
expanded_path = "/#{project.full_path}/-/raw/master/files/images/#{image_file}"
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
expect(subject.css('a')[0].attr('href')).to eq(expanded_path)
|
|
|
|
expect(subject.css('img')[0].attr('data-src')).to eq(expanded_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
context 'when requested_path and ref parameters are both provided' do
|
|
|
|
let(:requested_path) { 'files/images/README.md' }
|
|
|
|
let(:ref) { 'other_branch' }
|
|
|
|
|
|
|
|
it 'returns the correct HTML for the image' do
|
|
|
|
project.repository.create_branch('other_branch')
|
|
|
|
|
|
|
|
expanded_path = "/#{project.full_path}/-/raw/#{ref}/files/images/#{image_file}"
|
|
|
|
|
|
|
|
expect(subject.css('a')[0].attr('href')).to eq(expanded_path)
|
|
|
|
expect(subject.css('img')[0].attr('data-src')).to eq(expanded_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when ref is provided, but requested_path isn\'t' do
|
|
|
|
let(:ref) { 'other_branch' }
|
|
|
|
let(:requested_path) { nil }
|
|
|
|
|
|
|
|
it 'returns the correct HTML for the image' do
|
|
|
|
project.repository.create_branch('other_branch')
|
|
|
|
|
|
|
|
expanded_path = "/#{project.full_path}/-/blob/#{ref}/./#{image_file}"
|
|
|
|
|
|
|
|
expect(subject.css('a')[0].attr('href')).to eq(expanded_path)
|
|
|
|
expect(subject.css('img')[0].attr('data-src')).to eq(expanded_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when neither requested_path, nor ref parameter is provided' do
|
|
|
|
let(:ref) { nil }
|
2019-12-26 22:10:19 +05:30
|
|
|
let(:requested_path) { nil }
|
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
it 'returns the correct HTML for the image' do
|
2020-04-08 14:13:33 +05:30
|
|
|
expanded_path = "/#{project.full_path}/-/blob/master/./#{image_file}"
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
expect(subject.css('a')[0].attr('href')).to eq(expanded_path)
|
2020-05-24 23:13:21 +05:30
|
|
|
expect(subject.css('img')[0].attr('data-src')).to eq(expanded_path)
|
2019-12-26 22:10:19 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
describe '#markdown_field' do
|
|
|
|
let(:attribute) { :title }
|
|
|
|
|
|
|
|
describe 'with already redacted attribute' do
|
|
|
|
it 'returns the redacted attribute' do
|
|
|
|
commit.redacted_title_html = 'commit title'
|
|
|
|
|
|
|
|
expect(Banzai).not_to receive(:render_field)
|
|
|
|
|
|
|
|
expect(helper.markdown_field(commit, attribute)).to eq('commit title')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'without redacted attribute' do
|
|
|
|
it 'renders the markdown value' do
|
|
|
|
expect(Banzai).to receive(:render_field).with(commit, attribute, {}).and_call_original
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(Banzai).to receive(:post_process)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
helper.markdown_field(commit, attribute)
|
|
|
|
end
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
context 'when post_process is false' do
|
|
|
|
it 'does not run Markdown post processing' do
|
|
|
|
expect(Banzai).to receive(:render_field).with(commit, attribute, {}).and_call_original
|
|
|
|
expect(Banzai).not_to receive(:post_process)
|
|
|
|
|
|
|
|
helper.markdown_field(commit, attribute, post_process: false)
|
|
|
|
end
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
describe '#link_to_markdown_field' do
|
2017-08-17 22:00:37 +05:30
|
|
|
let(:link) { '/commits/0a1b2c3d' }
|
|
|
|
let(:issues) { create_list(:issue, 2, project: project) }
|
2014-09-02 18:07:02 +05:30
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
# Clean the cache to make sure the title is re-rendered from the stubbed one
|
|
|
|
it 'handles references nested in links with all the text', :clean_gitlab_redis_cache do
|
2018-03-17 18:26:18 +05:30
|
|
|
allow(commit).to receive(:title).and_return("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real")
|
|
|
|
|
|
|
|
actual = helper.link_to_markdown_field(commit, :title, link)
|
|
|
|
doc = Nokogiri::HTML.parse(actual)
|
|
|
|
|
|
|
|
# Make sure we didn't create invalid markup
|
|
|
|
expect(doc.errors).to be_empty
|
|
|
|
|
|
|
|
# Leading commit link
|
|
|
|
expect(doc.css('a')[0].attr('href')).to eq link
|
|
|
|
expect(doc.css('a')[0].text).to eq 'This should finally fix '
|
|
|
|
|
|
|
|
# First issue link
|
|
|
|
expect(doc.css('a')[1].attr('href'))
|
2018-11-20 20:47:30 +05:30
|
|
|
.to eq urls.project_issue_path(project, issues[0])
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(doc.css('a')[1].text).to eq issues[0].to_reference
|
|
|
|
|
|
|
|
# Internal commit link
|
|
|
|
expect(doc.css('a')[2].attr('href')).to eq link
|
|
|
|
expect(doc.css('a')[2].text).to eq ' and '
|
|
|
|
|
|
|
|
# Second issue link
|
|
|
|
expect(doc.css('a')[3].attr('href'))
|
2018-11-20 20:47:30 +05:30
|
|
|
.to eq urls.project_issue_path(project, issues[1])
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(doc.css('a')[3].text).to eq issues[1].to_reference
|
|
|
|
|
|
|
|
# Trailing commit link
|
|
|
|
expect(doc.css('a')[4].attr('href')).to eq link
|
|
|
|
expect(doc.css('a')[4].text).to eq ' for real'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#link_to_markdown' do
|
|
|
|
let(:link) { '/commits/0a1b2c3d' }
|
|
|
|
let(:issues) { create_list(:issue, 2, project: project) }
|
|
|
|
|
|
|
|
it 'handles references nested in links with all the text' do
|
|
|
|
actual = helper.link_to_markdown("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", link)
|
2015-09-11 14:41:01 +05:30
|
|
|
doc = Nokogiri::HTML.parse(actual)
|
2014-09-02 18:07:02 +05:30
|
|
|
|
2015-09-11 14:41:01 +05:30
|
|
|
# Make sure we didn't create invalid markup
|
|
|
|
expect(doc.errors).to be_empty
|
2014-09-02 18:07:02 +05:30
|
|
|
|
|
|
|
# Leading commit link
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(doc.css('a')[0].attr('href')).to eq link
|
2015-09-11 14:41:01 +05:30
|
|
|
expect(doc.css('a')[0].text).to eq 'This should finally fix '
|
2014-09-02 18:07:02 +05:30
|
|
|
|
|
|
|
# First issue link
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(doc.css('a')[1].attr('href'))
|
2018-11-20 20:47:30 +05:30
|
|
|
.to eq urls.project_issue_path(project, issues[0])
|
2015-09-11 14:41:01 +05:30
|
|
|
expect(doc.css('a')[1].text).to eq issues[0].to_reference
|
2014-09-02 18:07:02 +05:30
|
|
|
|
|
|
|
# Internal commit link
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(doc.css('a')[2].attr('href')).to eq link
|
2015-09-11 14:41:01 +05:30
|
|
|
expect(doc.css('a')[2].text).to eq ' and '
|
2014-09-02 18:07:02 +05:30
|
|
|
|
|
|
|
# Second issue link
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(doc.css('a')[3].attr('href'))
|
2018-11-20 20:47:30 +05:30
|
|
|
.to eq urls.project_issue_path(project, issues[1])
|
2015-09-11 14:41:01 +05:30
|
|
|
expect(doc.css('a')[3].text).to eq issues[1].to_reference
|
2014-09-02 18:07:02 +05:30
|
|
|
|
|
|
|
# Trailing commit link
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(doc.css('a')[4].attr('href')).to eq link
|
2015-09-11 14:41:01 +05:30
|
|
|
expect(doc.css('a')[4].text).to eq ' for real'
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
it 'forwards HTML options' do
|
2018-03-17 18:26:18 +05:30
|
|
|
actual = helper.link_to_markdown("Fixed in #{commit.id}", link, class: 'foo')
|
2015-09-11 14:41:01 +05:30
|
|
|
doc = Nokogiri::HTML.parse(actual)
|
|
|
|
|
|
|
|
expect(doc.css('a')).to satisfy do |v|
|
|
|
|
# 'foo' gets added to all links
|
|
|
|
v.all? { |a| a.attr('class').match(/foo$/) }
|
|
|
|
end
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it "escapes HTML passed in as the body" do
|
2015-09-11 14:41:01 +05:30
|
|
|
actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}"
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(helper.link_to_markdown(actual, link))
|
2017-09-10 17:25:29 +05:30
|
|
|
.to match('<h1>test</h1>')
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2015-09-11 14:41:01 +05:30
|
|
|
it 'ignores reference links when they are the entire body' do
|
|
|
|
text = issues[0].to_reference
|
2018-03-17 18:26:18 +05:30
|
|
|
act = helper.link_to_markdown(text, '/foo')
|
2015-09-11 14:41:01 +05:30
|
|
|
expect(act).to eq %Q(<a href="/foo">#{issues[0].to_reference}</a>)
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
it 'replaces commit message with emoji to link' do
|
2018-03-17 18:26:18 +05:30
|
|
|
actual = link_to_markdown(':book: Book', '/foo')
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(actual)
|
2019-12-26 22:10:19 +05:30
|
|
|
.to eq '<a href="/foo"><gl-emoji title="open book" data-name="book" data-unicode-version="6.0">📖</gl-emoji></a><a href="/foo"> Book</a>'
|
2015-10-24 18:46:33 +05:30
|
|
|
end
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
describe '#link_to_html' do
|
|
|
|
it 'wraps the rendered content in a link' do
|
|
|
|
link = '/commits/0a1b2c3d'
|
|
|
|
issue = create(:issue, project: project)
|
|
|
|
|
|
|
|
rendered = helper.markdown("This should finally fix #{issue.to_reference} for real", pipeline: :single_line)
|
|
|
|
doc = Nokogiri::HTML.parse(rendered)
|
|
|
|
|
|
|
|
expect(doc.css('a')[0].attr('href'))
|
2018-11-20 20:47:30 +05:30
|
|
|
.to eq urls.project_issue_path(project, issue)
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(doc.css('a')[0].text).to eq issue.to_reference
|
|
|
|
|
|
|
|
wrapped = helper.link_to_html(rendered, link)
|
|
|
|
doc = Nokogiri::HTML.parse(wrapped)
|
|
|
|
|
|
|
|
expect(doc.css('a')[0].attr('href')).to eq link
|
|
|
|
expect(doc.css('a')[0].text).to eq 'This should finally fix '
|
|
|
|
end
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
it "escapes HTML passed as an emoji" do
|
|
|
|
rendered = '<gl-emoji><div class="test">test</div></gl-emoji>'
|
|
|
|
expect(helper.link_to_html(rendered, '/foo'))
|
|
|
|
.to eq '<a href="/foo"><gl-emoji><div class="test">test</div></gl-emoji></a>'
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2015-09-11 14:41:01 +05:30
|
|
|
describe '#render_wiki_content' do
|
2019-10-31 01:37:42 +05:30
|
|
|
let(:wiki) { double('WikiPage', path: "file.#{extension}") }
|
2020-03-13 15:44:24 +05:30
|
|
|
let(:wiki_repository) { double('Repository') }
|
2021-02-22 17:27:13 +05:30
|
|
|
let(:content) { 'wiki content' }
|
2019-10-31 01:37:42 +05:30
|
|
|
let(:context) do
|
|
|
|
{
|
2020-06-23 00:09:42 +05:30
|
|
|
pipeline: :wiki, project: project, wiki: wiki,
|
2022-01-26 12:08:38 +05:30
|
|
|
page_slug: 'nested/page', issuable_reference_expansion_enabled: true,
|
2020-03-13 15:44:24 +05:30
|
|
|
repository: wiki_repository
|
2019-10-31 01:37:42 +05:30
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2014-09-02 18:07:02 +05:30
|
|
|
before do
|
2021-02-22 17:27:13 +05:30
|
|
|
expect(wiki).to receive(:content).and_return(content)
|
2019-10-31 01:37:42 +05:30
|
|
|
expect(wiki).to receive(:slug).and_return('nested/page')
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(wiki).to receive(:repository).and_return(wiki_repository)
|
2021-02-22 17:27:13 +05:30
|
|
|
allow(wiki).to receive(:container).and_return(project)
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
helper.instance_variable_set(:@wiki, wiki)
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
context 'when file is Markdown' do
|
|
|
|
let(:extension) { 'md' }
|
2014-09-02 18:07:02 +05:30
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
it 'renders using #markdown_unsafe helper method' do
|
|
|
|
expect(helper).to receive(:markdown_unsafe).with('wiki content', context)
|
2018-11-20 20:47:30 +05:30
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
helper.render_wiki_content(wiki)
|
|
|
|
end
|
2021-02-22 17:27:13 +05:30
|
|
|
|
|
|
|
context 'when context has labels' do
|
|
|
|
let_it_be(:label) { create(:label, title: 'Bug', project: project) }
|
|
|
|
|
|
|
|
let(:content) { '~Bug' }
|
|
|
|
|
|
|
|
it 'renders label' do
|
|
|
|
result = helper.render_wiki_content(wiki)
|
|
|
|
doc = Nokogiri::HTML.parse(result)
|
|
|
|
|
|
|
|
expect(doc.css('.gl-label-link')).not_to be_empty
|
|
|
|
end
|
|
|
|
end
|
2021-03-08 18:12:59 +05:30
|
|
|
|
|
|
|
context 'when content has uploads' do
|
|
|
|
let(:upload_link) { '/uploads/test.png' }
|
|
|
|
let(:content) { "![ImageTest](#{upload_link})" }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(wiki).to receive(:wiki_base_path).and_return(project.wiki.wiki_base_path)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'renders uploads relative to project' do
|
|
|
|
result = helper.render_wiki_content(wiki)
|
|
|
|
|
|
|
|
expect(result).to include("#{project.full_path}#{upload_link}")
|
|
|
|
end
|
|
|
|
end
|
2018-11-20 20:47:30 +05:30
|
|
|
end
|
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
context 'when file is Asciidoc' do
|
|
|
|
let(:extension) { 'adoc' }
|
2015-09-11 14:41:01 +05:30
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
it 'renders using Gitlab::Asciidoc' do
|
|
|
|
expect(Gitlab::Asciidoc).to receive(:render)
|
2015-09-11 14:41:01 +05:30
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
helper.render_wiki_content(wiki)
|
|
|
|
end
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
|
2021-04-29 21:17:54 +05:30
|
|
|
context 'when file is Kramdown' do
|
|
|
|
let(:extension) { 'rmd' }
|
|
|
|
let(:content) do
|
|
|
|
<<-EOF
|
|
|
|
{::options parse_block_html="true" /}
|
|
|
|
|
|
|
|
<div>
|
|
|
|
FooBar
|
|
|
|
</div>
|
|
|
|
EOF
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'renders using #markdown_unsafe helper method' do
|
|
|
|
expect(helper).to receive(:markdown_unsafe).with(content, context)
|
|
|
|
|
|
|
|
result = helper.render_wiki_content(wiki)
|
|
|
|
|
|
|
|
expect(result).to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
context 'any other format' do
|
|
|
|
let(:extension) { 'foo' }
|
2014-09-02 18:07:02 +05:30
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
it 'renders all other formats using Gitlab::OtherMarkup' do
|
|
|
|
expect(Gitlab::OtherMarkup).to receive(:render)
|
|
|
|
|
|
|
|
helper.render_wiki_content(wiki)
|
|
|
|
end
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|
|
|
|
end
|
2015-04-26 12:48:37 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
describe '#markup' do
|
2017-08-17 22:00:37 +05:30
|
|
|
let(:content) { 'Noël' }
|
|
|
|
|
2021-06-02 17:11:27 +05:30
|
|
|
it 'sets the :text_source to :blob in the context' do
|
|
|
|
context = {}
|
|
|
|
helper.markup('foo.md', content, context)
|
|
|
|
|
|
|
|
expect(context).to include(text_source: :blob)
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
it 'preserves encoding' do
|
|
|
|
expect(content.encoding.name).to eq('UTF-8')
|
|
|
|
expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
|
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
it 'delegates to #markdown_unsafe when file name corresponds to Markdown' do
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true)
|
|
|
|
expect(helper).to receive(:markdown_unsafe).and_return('NOEL')
|
|
|
|
|
|
|
|
expect(helper.markup('foo.md', content)).to eq('NOEL')
|
|
|
|
end
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
it 'delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc' do
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true)
|
|
|
|
expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL')
|
|
|
|
|
|
|
|
expect(helper.markup('foo.adoc', content)).to eq('NOEL')
|
|
|
|
end
|
2018-11-08 19:23:39 +05:30
|
|
|
|
|
|
|
it 'uses passed in rendered content' do
|
|
|
|
expect(helper).not_to receive(:gitlab_markdown?)
|
|
|
|
expect(helper).not_to receive(:markdown_unsafe)
|
|
|
|
|
|
|
|
expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>')
|
|
|
|
end
|
|
|
|
|
2018-11-20 20:47:30 +05:30
|
|
|
it 'defaults to CommonMark' do
|
|
|
|
expect(helper.markup('foo.md', 'x^2')).to include('x^2')
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
describe '#markup_unsafe' do
|
|
|
|
subject { helper.markup_unsafe(file_name, text, context) }
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
let_it_be(:project_base) { create(:project, :repository) }
|
|
|
|
let_it_be(:context) { { project: project_base } }
|
2021-04-29 21:17:54 +05:30
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
let(:file_name) { 'foo.bar' }
|
|
|
|
let(:text) { 'Noël' }
|
|
|
|
|
|
|
|
context 'when text is missing' do
|
|
|
|
let(:text) { nil }
|
|
|
|
|
|
|
|
it 'returns an empty string' do
|
|
|
|
is_expected.to eq('')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when file is a markdown file' do
|
|
|
|
let(:file_name) { 'foo.md' }
|
|
|
|
|
|
|
|
it 'returns html (rendered by Banzai)' do
|
|
|
|
expected_html = '<p data-sourcepos="1:1-1:5" dir="auto">Noël</p>'
|
|
|
|
|
|
|
|
expect(Banzai).to receive(:render).with(text, context) { expected_html }
|
|
|
|
|
|
|
|
is_expected.to eq(expected_html)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when renderer returns an error' do
|
|
|
|
before do
|
2020-03-13 15:44:24 +05:30
|
|
|
allow(Banzai).to receive(:render).and_raise(StandardError, "An error")
|
2019-09-30 21:07:59 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns html (rendered by ActionView:TextHelper)' do
|
|
|
|
is_expected.to eq('<p>Noël</p>')
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
it 'logs the error' do
|
|
|
|
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
|
|
|
|
instance_of(StandardError),
|
|
|
|
project_id: project.id, file_name: 'foo.md'
|
|
|
|
)
|
|
|
|
|
|
|
|
subject
|
|
|
|
end
|
2019-09-30 21:07:59 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when file is asciidoc file' do
|
|
|
|
let(:file_name) { 'foo.adoc' }
|
|
|
|
|
|
|
|
it 'returns html (rendered by Gitlab::Asciidoc)' do
|
|
|
|
expected_html = "<div>\n<p>Noël</p>\n</div>"
|
|
|
|
|
|
|
|
expect(Gitlab::Asciidoc).to receive(:render).with(text, context) { expected_html }
|
|
|
|
|
|
|
|
is_expected.to eq(expected_html)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when file is a regular text file' do
|
|
|
|
let(:file_name) { 'foo.txt' }
|
|
|
|
|
|
|
|
it 'returns html (rendered by ActionView::TagHelper)' do
|
|
|
|
is_expected.to eq('<pre class="plain-readme">Noël</pre>')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when file has an unknown type' do
|
2020-03-13 15:44:24 +05:30
|
|
|
let(:file_name) { 'foo.tex' }
|
2019-09-30 21:07:59 +05:30
|
|
|
|
|
|
|
it 'returns html (rendered by Gitlab::OtherMarkup)' do
|
|
|
|
expected_html = 'Noël'
|
|
|
|
|
|
|
|
expect(Gitlab::OtherMarkup).to receive(:render).with(file_name, text, context) { expected_html }
|
|
|
|
|
|
|
|
is_expected.to eq(expected_html)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '#first_line_in_markdown' do
|
2018-03-17 18:26:18 +05:30
|
|
|
shared_examples_for 'common markdown examples' do
|
|
|
|
let(:project_base) { build(:project, :repository) }
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it 'displays inline code' do
|
|
|
|
object = create_object('Text with `inline code`')
|
|
|
|
expected = 'Text with <code>inline code</code>'
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(first_line_in_markdown(object, attribute, 100, project: project)).to match(expected)
|
|
|
|
end
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it 'truncates the text with multiple paragraphs' do
|
|
|
|
object = create_object("Paragraph 1\n\nParagraph 2")
|
|
|
|
expected = 'Paragraph 1...'
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(first_line_in_markdown(object, attribute, 100, project: project)).to match(expected)
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it 'displays the first line of a code block' do
|
|
|
|
object = create_object("```\nCode block\nwith two lines\n```")
|
|
|
|
expected = %r{<pre.+><code><span class="line">Code block\.\.\.</span>\n</code></pre>}
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(first_line_in_markdown(object, attribute, 100, project: project)).to match(expected)
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it 'truncates a single long line of text' do
|
|
|
|
text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
|
|
|
|
object = create_object(text * 4)
|
|
|
|
expected = (text * 2).sub(/.{3}/, '...')
|
|
|
|
|
|
|
|
expect(first_line_in_markdown(object, attribute, 150, project: project)).to match(expected)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'preserves a link href when link text is truncated' do
|
|
|
|
text = 'The quick brown fox jumped over the lazy dog' # 44 chars
|
|
|
|
link_url = 'http://example.com/foo/bar/baz' # 30 chars
|
2019-12-21 20:55:43 +05:30
|
|
|
input = "#{text}#{text}#{text} #{link_url}" # 163 chars
|
2018-03-17 18:26:18 +05:30
|
|
|
expected_link_text = 'http://example...</a>'
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
object = create_object(input)
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(first_line_in_markdown(object, attribute, 150, project: project)).to match(link_url)
|
|
|
|
expect(first_line_in_markdown(object, attribute, 150, project: project)).to match(expected_link_text)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'preserves code color scheme' do
|
|
|
|
object = create_object("```ruby\ndef test\n 'hello world'\nend\n```")
|
2022-01-26 12:08:38 +05:30
|
|
|
expected = "\n<pre class=\"code highlight js-syntax-highlight language-ruby\">" \
|
2018-03-17 18:26:18 +05:30
|
|
|
"<code><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \
|
2022-01-26 12:08:38 +05:30
|
|
|
"</code></pre>\n"
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
expect(first_line_in_markdown(object, attribute, 150, project: project)).to eq(expected)
|
|
|
|
end
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
context 'when images are allowed' do
|
|
|
|
it 'preserves data-src for lazy images' do
|
|
|
|
object = create_object("![ImageTest](/uploads/test.png)")
|
|
|
|
image_url = "data-src=\".*/uploads/test.png\""
|
|
|
|
text = first_line_in_markdown(object, attribute, 150, project: project, allow_images: true)
|
|
|
|
|
|
|
|
expect(text).to match(image_url)
|
|
|
|
expect(text).to match('<a')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when images are not allowed' do
|
|
|
|
it 'removes any images' do
|
|
|
|
object = create_object("![ImageTest](/uploads/test.png)")
|
|
|
|
text = first_line_in_markdown(object, attribute, 150, project: project)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
expect(text).not_to match('<img')
|
|
|
|
expect(text).not_to match('<a')
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
context 'labels formatting' do
|
|
|
|
let(:label_title) { 'this should be ~label_1' }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def create_and_format_label(project)
|
|
|
|
create(:label, title: 'label_1', project: project)
|
|
|
|
object = create_object(label_title, project: project)
|
|
|
|
|
|
|
|
first_line_in_markdown(object, attribute, 150, project: project)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'preserves style attribute for a label that can be accessed by current_user' do
|
|
|
|
project = create(:project, :public)
|
2020-04-08 14:13:33 +05:30
|
|
|
label = create_and_format_label(project)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
expect(label).to match(/span class=.*style=.*/)
|
|
|
|
expect(label).to include('data-html="true"')
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not style a label that can not be accessed by current_user' do
|
|
|
|
project = create(:project, :private)
|
2020-04-08 14:13:33 +05:30
|
|
|
label = create_and_format_label(project)
|
2018-03-17 18:26:18 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
expect(label).to include("~label_1")
|
|
|
|
expect(label).not_to match(/span class=.*style=.*/)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
it 'keeps whitelisted tags' do
|
|
|
|
html = '<a><i></i></a> <strong>strong</strong><em>em</em><b>b</b>'
|
|
|
|
|
|
|
|
object = create_object(html)
|
|
|
|
result = first_line_in_markdown(object, attribute, 100, project: project)
|
|
|
|
|
|
|
|
expect(result).to include(html)
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it 'truncates Markdown properly' do
|
|
|
|
object = create_object("@#{user.username}, can you look at this?\nHello world\n")
|
|
|
|
actual = first_line_in_markdown(object, attribute, 100, project: project)
|
|
|
|
|
|
|
|
doc = Nokogiri::HTML.parse(actual)
|
|
|
|
|
|
|
|
# Make sure we didn't create invalid markup
|
|
|
|
expect(doc.errors).to be_empty
|
|
|
|
|
|
|
|
# Leading user link
|
|
|
|
expect(doc.css('a').length).to eq(1)
|
|
|
|
expect(doc.css('a')[0].attr('href')).to eq user_path(user)
|
|
|
|
expect(doc.css('a')[0].text).to eq "@#{user.username}"
|
|
|
|
|
|
|
|
expect(doc.content).to eq "@#{user.username}, can you look at this?..."
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'truncates Markdown with emoji properly' do
|
|
|
|
object = create_object("foo :wink:\nbar :grinning:")
|
|
|
|
actual = first_line_in_markdown(object, attribute, 100, project: project)
|
|
|
|
|
|
|
|
doc = Nokogiri::HTML.parse(actual)
|
|
|
|
|
|
|
|
# Make sure we didn't create invalid markup
|
|
|
|
# But also account for the 2 errors caused by the unknown `gl-emoji` elements
|
|
|
|
expect(doc.errors.length).to eq(2)
|
|
|
|
|
|
|
|
expect(doc.css('gl-emoji').length).to eq(2)
|
|
|
|
expect(doc.css('gl-emoji')[0].attr('data-name')).to eq 'wink'
|
|
|
|
expect(doc.css('gl-emoji')[1].attr('data-name')).to eq 'grinning'
|
|
|
|
|
|
|
|
expect(doc.content).to eq "foo 😉\nbar 😀"
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
it 'does not post-process truncated text', :request_store do
|
|
|
|
object = create_object("hello \n\n [Test](README.md)")
|
|
|
|
|
|
|
|
expect do
|
|
|
|
first_line_in_markdown(object, attribute, nil, project: project)
|
|
|
|
end.not_to change { Gitlab::GitalyClient.get_request_count }
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the asked attribute can be redacted' do
|
|
|
|
include_examples 'common markdown examples' do
|
|
|
|
let(:attribute) { :note }
|
|
|
|
def create_object(title, project: project_base)
|
|
|
|
build(:note, note: title, project: project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the asked attribute can not be redacted' do
|
|
|
|
include_examples 'common markdown examples' do
|
|
|
|
let(:attribute) { :body }
|
|
|
|
def create_object(title, project: project_base)
|
|
|
|
issue = build(:issue, title: title)
|
|
|
|
build(:todo, :done, project: project_base, author: user, target: issue)
|
|
|
|
end
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#cross_project_reference' do
|
|
|
|
it 'shows the full MR reference' do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(helper.cross_project_reference(project, merge_request)).to include(project.full_path)
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'shows the full issue reference' do
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(helper.cross_project_reference(project, issue)).to include(project.full_path)
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2015-10-24 18:46:33 +05:30
|
|
|
end
|
2018-11-20 20:47:30 +05:30
|
|
|
|
|
|
|
def urls
|
|
|
|
Gitlab::Routing.url_helpers
|
|
|
|
end
|
2014-09-02 18:07:02 +05:30
|
|
|
end
|