diff options
Diffstat (limited to 'spec/lib/banzai/filter')
5 files changed, 231 insertions, 88 deletions
diff --git a/spec/lib/banzai/filter/inline_observability_filter_spec.rb b/spec/lib/banzai/filter/inline_observability_filter_spec.rb index 341ada6d2b5..fb1ba46e76c 100644 --- a/spec/lib/banzai/filter/inline_observability_filter_spec.rb +++ b/spec/lib/banzai/filter/inline_observability_filter_spec.rb @@ -7,27 +7,49 @@ RSpec.describe Banzai::Filter::InlineObservabilityFilter do let(:input) { %(<a href="#{url}">example</a>) } let(:doc) { filter(input) } + let(:group) { create(:group) } + let(:user) { create(:user) } - context 'when the document has an external link' do - let(:url) { 'https://foo.com' } + describe '#filter?' do + context 'when the document has an external link' do + let(:url) { 'https://foo.com' } - it 'leaves regular non-observability links unchanged' do - expect(doc.to_s).to eq(input) + it 'leaves regular non-observability links unchanged' do + expect(doc.to_s).to eq(input) + end end - end - context 'when the document contains an embeddable observability link' do - let(:url) { 'https://observe.gitlab.com/12345' } + context 'when the document contains an embeddable observability link' do + let(:url) { 'https://observe.gitlab.com/12345' } + + it 'leaves the original link unchanged' do + expect(doc.at_css('a').to_s).to eq(input) + end + + it 'appends an observability charts placeholder' do + node = doc.at_css('.js-render-observability') - it 'leaves the original link unchanged' do - expect(doc.at_css('a').to_s).to eq(input) + expect(node).to be_present + expect(node.attribute('data-frame-url').to_s).to eq(url) + end end - it 'appends a observability charts placeholder' do - node = doc.at_css('.js-render-observability') + context 'when feature flag is disabled' do + let(:url) { 'https://observe.gitlab.com/12345' } + + before do + stub_feature_flags(observability_group_tab: false) + end + + it 'leaves the original link unchanged' do + expect(doc.at_css('a').to_s).to eq(input) + end + + it 'does not append an observability charts placeholder' do + node = doc.at_css('.js-render-observability') - expect(node).to be_present - expect(node.attribute('data-frame-url').to_s).to eq(url) + expect(node).not_to be_present + end end end end diff --git a/spec/lib/banzai/filter/math_filter_spec.rb b/spec/lib/banzai/filter/math_filter_spec.rb index c5d2bcd5363..374983e40a1 100644 --- a/spec/lib/banzai/filter/math_filter_spec.rb +++ b/spec/lib/banzai/filter/math_filter_spec.rb @@ -2,14 +2,15 @@ require 'spec_helper' -RSpec.describe Banzai::Filter::MathFilter do +RSpec.describe Banzai::Filter::MathFilter, feature_category: :team_planning do using RSpec::Parameterized::TableSyntax include FilterSpecHelper shared_examples 'inline math' do it 'removes surrounding dollar signs and adds class code, math and js-render-math' do - doc = filter(text) - expected = result_template.gsub('<math>', '<code class="code math js-render-math" data-math-style="inline">') + doc = pipeline_filter(text) + + expected = result_template.gsub('<math>', '<code data-math-style="inline" class="code math js-render-math">') expected.gsub!('</math>', '</code>') expect(doc.to_s).to eq expected @@ -17,12 +18,12 @@ RSpec.describe Banzai::Filter::MathFilter do end shared_examples 'display math' do - let_it_be(:template_prefix_with_pre) { '<pre class="code math js-render-math" data-math-style="display"><code>' } - let_it_be(:template_prefix_with_code) { '<code class="code math js-render-math" data-math-style="display">' } + let_it_be(:template_prefix_with_pre) { '<pre lang="math" data-math-style="display" class="js-render-math"><code>' } + let_it_be(:template_prefix_with_code) { '<code data-math-style="display" class="code math js-render-math">' } let(:use_pre_tags) { false } it 'removes surrounding dollar signs and adds class code, math and js-render-math' do - doc = filter(text) + doc = pipeline_filter(text) template_prefix = use_pre_tags ? template_prefix_with_pre : template_prefix_with_code template_suffix = "</code>#{'</pre>' if use_pre_tags}" @@ -36,36 +37,38 @@ RSpec.describe Banzai::Filter::MathFilter do describe 'inline math using $...$ syntax' do context 'with valid syntax' do where(:text, :result_template) do - '$2+2$' | '<math>2+2</math>' - '$22+1$ and $22 + a^2$' | '<math>22+1</math> and <math>22 + a^2</math>' - '$22 and $2+2$' | '$22 and <math>2+2</math>' - '$2+2$ $22 and flightjs/Flight$22 $2+2$' | '<math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math>' - '$1/2$ <b>test</b>' | '<math>1/2</math> <b>test</b>' - '$a!$' | '<math>a!</math>' - '$x$' | '<math>x</math>' + '$2+2$' | '<p><math>2+2</math></p>' + '$22+1$ and $22 + a^2$' | '<p><math>22+1</math> and <math>22 + a^2</math></p>' + '$22 and $2+2$' | '<p>$22 and <math>2+2</math></p>' + '$2+2$ $22 and flightjs/Flight$22 $2+2$' | '<p><math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math></p>' + '$1/2$ <b>test</b>' | '<p><math>1/2</math> <b>test</b></p>' + '$a!$' | '<p><math>a!</math></p>' + '$x$' | '<p><math>x</math></p>' + '$1+2\$$' | '<p><math>1+2\$</math></p>' + '$1+\$2$' | '<p><math>1+\$2</math></p>' + '$1+\%2$' | '<p><math>1+\%2</math></p>' + '$1+\#2$' | '<p><math>1+\#2</math></p>' + '$1+\&2$' | '<p><math>1+\&2</math></p>' + '$1+\{2$' | '<p><math>1+\{2</math></p>' + '$1+\}2$' | '<p><math>1+\}2</math></p>' + '$1+\_2$' | '<p><math>1+\_2</math></p>' end with_them do it_behaves_like 'inline math' end end - - it 'does not handle dollar literals properly' do - doc = filter('$20+30\$$') - expected = '<code class="code math js-render-math" data-math-style="inline">20+30\\</code>$' - - expect(doc.to_s).to eq expected - end end describe 'inline math using $`...`$ syntax' do context 'with valid syntax' do where(:text, :result_template) do - '$<code>2+2</code>$' | '<math>2+2</math>' - '$<code>22+1</code>$ and $<code>22 + a^2</code>$' | '<math>22+1</math> and <math>22 + a^2</math>' - '$22 and $<code>2+2</code>$' | '$22 and <math>2+2</math>' - '$<code>2+2</code>$ $22 and flightjs/Flight$22 $<code>2+2</code>$' | '<math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math>' - 'test $$<code>2+2</code>$$ test' | 'test $<math>2+2</math>$ test' + '$`2+2`$' | '<p><math>2+2</math></p>' + '$`22+1`$ and $`22 + a^2`$' | '<p><math>22+1</math> and <math>22 + a^2</math></p>' + '$22 and $`2+2`$' | '<p>$22 and <math>2+2</math></p>' + '$`2+2`$ $22 and flightjs/Flight$22 $`2+2`$' | '<p><math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math></p>' + 'test $$`2+2`$$ test' | '<p>test $<math>2+2</math>$ test</p>' + '$`1+\$2`$' | '<p><math>1+\$2</math></p>' end with_them do @@ -77,15 +80,15 @@ RSpec.describe Banzai::Filter::MathFilter do describe 'inline display math using $$...$$ syntax' do context 'with valid syntax' do where(:text, :result_template) do - '$$2+2$$' | '<math>2+2</math>' - '$$ 2+2 $$' | '<math>2+2</math>' - '$$22+1$$ and $$22 + a^2$$' | '<math>22+1</math> and <math>22 + a^2</math>' - '$22 and $$2+2$$' | '$22 and <math>2+2</math>' - '$$2+2$$ $22 and flightjs/Flight$22 $$2+2$$' | '<math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math>' - 'flightjs/Flight$22 and $$a^2 + b^2 = c^2$$' | 'flightjs/Flight$22 and <math>a^2 + b^2 = c^2</math>' - '$$a!$$' | '<math>a!</math>' - '$$x$$' | '<math>x</math>' - '$$20,000 and $$30,000' | '<math>20,000 and</math>30,000' + '$$2+2$$' | '<p><math>2+2</math></p>' + '$$ 2+2 $$' | '<p><math>2+2</math></p>' + '$$22+1$$ and $$22 + a^2$$' | '<p><math>22+1</math> and <math>22 + a^2</math></p>' + '$22 and $$2+2$$' | '<p>$22 and <math>2+2</math></p>' + '$$2+2$$ $22 and flightjs/Flight$22 $$2+2$$' | '<p><math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math></p>' + 'flightjs/Flight$22 and $$a^2 + b^2 = c^2$$' | '<p>flightjs/Flight$22 and <math>a^2 + b^2 = c^2</math></p>' + '$$a!$$' | '<p><math>a!</math></p>' + '$$x$$' | '<p><math>x</math></p>' + '$$20,000 and $$30,000' | '<p><math>20,000 and</math>30,000</p>' end with_them do @@ -97,8 +100,8 @@ RSpec.describe Banzai::Filter::MathFilter do describe 'block display math using $$\n...\n$$ syntax' do context 'with valid syntax' do where(:text, :result_template) do - "$$\n2+2\n$$" | "<math>2+2</math>" - "$$\n2+2\n3+4\n$$" | "<math>2+2\n3+4</math>" + "$$\n2+2\n$$" | "<math>2+2\n</math>" + "$$\n2+2\n3+4\n$$" | "<math>2+2\n3+4\n</math>" end with_them do @@ -107,72 +110,96 @@ RSpec.describe Banzai::Filter::MathFilter do end end end + + context 'when it spans multiple lines' do + let(:math) do + <<~MATH + \\begin{align*} + \\Delta t \\frac{d(b_i, a_i)}{c} + \\Delta t_{b_i} + \\end{align*} + MATH + end + + let(:text) { "$$\n#{math}$$" } + let(:result_template) { "<math>#{math}</math>" } + + it_behaves_like 'display math' do + let(:use_pre_tags) { true } + end + end + + context 'when it contains \\' do + let(:math) do + <<~MATH + E = mc^2 \\\\ + E = \\$mc^2 + MATH + end + + let(:text) { "$$\n#{math}$$" } + let(:result_template) { "<math>#{math}</math>" } + + it_behaves_like 'display math' do + let(:use_pre_tags) { true } + end + end end describe 'display math using ```math...``` syntax' do it 'adds data-math-style display attribute to display math' do - doc = filter('<pre lang="math"><code>2+2</code></pre>') + doc = pipeline_filter("```math\n2+2\n```") pre = doc.xpath('descendant-or-self::pre').first expect(pre['data-math-style']).to eq 'display' end it 'adds js-render-math class to display math' do - doc = filter('<pre lang="math"><code>2+2</code></pre>') + doc = pipeline_filter("```math\n2+2\n```") pre = doc.xpath('descendant-or-self::pre').first expect(pre[:class]).to include("js-render-math") end it 'ignores code blocks that are not math' do - input = '<pre lang="plaintext"><code>2+2</code></pre>' - doc = filter(input) + input = "```plaintext\n2+2\n```" + doc = pipeline_filter(input) - expect(doc.to_s).to eq input + expect(doc.to_s).to eq "<pre lang=\"plaintext\"><code>2+2\n</code></pre>" end it 'requires the pre to contain both code and math' do input = '<pre lang="math">something</pre>' - doc = filter(input) + doc = pipeline_filter(input) expect(doc.to_s).to eq input end - - it 'dollar signs around to display math' do - doc = filter('$<pre lang="math"><code>2+2</code></pre>$') - before = doc.xpath('descendant-or-self::text()[1]').first - after = doc.xpath('descendant-or-self::text()[3]').first - - expect(before.to_s).to eq '$' - expect(after.to_s).to eq '$' - end end describe 'unrecognized syntax' do - where(:text) do - [ - '<code>2+2</code>', - 'test $<code>2+2</code> test', - 'test <code>2+2</code>$ test', - '<em>$</em><code>2+2</code><em>$</em>', - '$20,000 and $30,000', - '$20,000 in $USD', - '$ a^2 $', - "test $$\n2+2\n$$", - "$\n$", - '$$$' - ] + where(:text, :result) do + '`2+2`' | '<p><code>2+2</code></p>' + 'test $`2+2` test' | '<p>test $<code>2+2</code> test</p>' + 'test `2+2`$ test' | '<p>test <code>2+2</code>$ test</p>' + '$20,000 and $30,000' | '<p>$20,000 and $30,000</p>' + '$20,000 in $USD' | '<p>$20,000 in $USD</p>' + '$ a^2 $' | '<p>$ a^2 $</p>' + "test $$\n2+2\n$$" | "<p>test $$\n2+2\n$$</p>" + "$\n$" | "<p>$\n$</p>" + '$$$' | '<p>$$$</p>' + '`$1+2$`' | '<p><code>$1+2$</code></p>' + '`$$1+2$$`' | '<p><code>$$1+2$$</code></p>' + '`$\$1+2$$`' | '<p><code>$\$1+2$$</code></p>' end with_them do it 'is ignored' do - expect(filter(text).to_s).to eq text + expect(pipeline_filter(text).to_s).to eq result end end end it 'handles multiple styles in one text block' do - doc = filter('$<code>2+2</code>$ + $3+3$ + $$4+4$$') + doc = pipeline_filter('$`2+2`$ + $3+3$ + $$4+4$$') expect(doc.search('.js-render-math').count).to eq(3) expect(doc.search('[data-math-style="inline"]').count).to eq(2) @@ -182,15 +209,17 @@ RSpec.describe Banzai::Filter::MathFilter do it 'limits how many elements can be marked as math' do stub_const('Banzai::Filter::MathFilter::RENDER_NODES_LIMIT', 2) - doc = filter('$<code>2+2</code>$ + $<code>3+3</code>$ + $<code>4+4</code>$') + doc = pipeline_filter('$`2+2`$ + $3+3$ + $$4+4$$') expect(doc.search('.js-render-math').count).to eq(2) end - it 'does not recognize new syntax when feature flag is off' do - stub_feature_flags(markdown_dollar_math: false) - doc = filter('$1+2$') + def pipeline_filter(text) + context = { project: nil, no_sourcepos: true } + doc = Banzai::Pipeline::PreProcessPipeline.call(text, {}) + doc = Banzai::Pipeline::PlainMarkdownPipeline.call(doc[:output], context) + doc = Banzai::Filter::SanitizationFilter.call(doc[:output], context, nil) - expect(doc.to_s).to eq '$1+2$' + filter(doc) end end diff --git a/spec/lib/banzai/filter/references/reference_filter_spec.rb b/spec/lib/banzai/filter/references/reference_filter_spec.rb index 6d7396ef216..88404f2039d 100644 --- a/spec/lib/banzai/filter/references/reference_filter_spec.rb +++ b/spec/lib/banzai/filter/references/reference_filter_spec.rb @@ -189,9 +189,9 @@ RSpec.describe Banzai::Filter::References::ReferenceFilter do let(:filter) { described_class.new(document, project: project) } it 'updates all new nodes', :aggregate_failures do - filter.instance_variable_set('@nodes', nodes) + filter.instance_variable_set(:@nodes, nodes) - expect(filter).to receive(:call) { filter.instance_variable_set('@new_nodes', new_nodes) } + expect(filter).to receive(:call) { filter.instance_variable_set(:@new_nodes, new_nodes) } expect(filter).to receive(:with_update_nodes).and_call_original expect(filter).to receive(:update_nodes!).and_call_original @@ -212,7 +212,7 @@ RSpec.describe Banzai::Filter::References::ReferenceFilter do expect_next_instance_of(described_class) do |filter| expect(filter).to receive(:call_and_update_nodes).and_call_original expect(filter).to receive(:with_update_nodes).and_call_original - expect(filter).to receive(:call) { filter.instance_variable_set('@new_nodes', new_nodes) } + expect(filter).to receive(:call) { filter.instance_variable_set(:@new_nodes, new_nodes) } expect(filter).to receive(:update_nodes!).and_call_original end diff --git a/spec/lib/banzai/filter/repository_link_filter_spec.rb b/spec/lib/banzai/filter/repository_link_filter_spec.rb index 0df680dc0c8..b2162ea2756 100644 --- a/spec/lib/banzai/filter/repository_link_filter_spec.rb +++ b/spec/lib/banzai/filter/repository_link_filter_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Banzai::Filter::RepositoryLinkFilter do +RSpec.describe Banzai::Filter::RepositoryLinkFilter, feature_category: :team_planning do include RepoHelpers def filter(doc, contexts = {}) @@ -303,6 +303,12 @@ RSpec.describe Banzai::Filter::RepositoryLinkFilter do expect(doc.at_css('img')['src']).to eq "/#{project_path}/-/raw/#{Addressable::URI.escape(ref)}/#{escaped}" end + it 'supports percent sign in filenames' do + doc = filter(link('doc/api/README%.md')) + expect(doc.at_css('a')['href']) + .to eq "/#{project_path}/-/blob/#{ref}/doc/api/README%25.md" + end + context 'when requested path is a file in the repo' do let(:requested_path) { 'doc/api/README.md' } diff --git a/spec/lib/banzai/filter/service_desk_upload_link_filter_spec.rb b/spec/lib/banzai/filter/service_desk_upload_link_filter_spec.rb new file mode 100644 index 00000000000..08d6fe03606 --- /dev/null +++ b/spec/lib/banzai/filter/service_desk_upload_link_filter_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Banzai::Filter::ServiceDeskUploadLinkFilter, feature_category: :service_desk do + def filter(doc, contexts = {}) + described_class.call(doc, contexts) + end + + def link(path, text) + %(<a href="#{path}">#{text}</a>) + end + + let(:file_name) { 'test.jpg' } + let(:secret) { 'e90decf88d8f96fe9e1389afc2e4a91f' } + let(:upload_path) { "/uploads/#{secret}/#{file_name}" } + let(:html_link) { link(upload_path, file_name) } + + context 'when replace_upload_links enabled' do + context 'when it has only one attachment to replace' do + let(:contexts) { { uploads_as_attachments: ["#{secret}/#{file_name}"] } } + + context 'when filename in text is same as in link' do + it 'replaces the link with original filename in strong' do + doc = filter(html_link, contexts) + + expect(doc.at_css('a')).to be_nil + expect(doc.at_css('strong').text).to eq(file_name) + end + end + + context 'when filename in text is not same as in link' do + let(:filename_in_text) { 'Custom name' } + let(:html_link) { link(upload_path, filename_in_text) } + + it 'replaces the link with filename in text & original filename, in strong' do + doc = filter(html_link, contexts) + + expect(doc.at_css('a')).to be_nil + expect(doc.at_css('strong').text).to eq("#{filename_in_text} (#{file_name})") + end + end + end + + context 'when it has more than one attachment to replace' do + let(:file_name_1) { 'test1.jpg' } + let(:secret_1) { '17817c73e368777e6f743392e334fb8a' } + let(:upload_path_1) { "/uploads/#{secret_1}/#{file_name_1}" } + let(:html_link_1) { link(upload_path_1, file_name_1) } + + context 'when all of uploads can be replaced' do + let(:contexts) { { uploads_as_attachments: ["#{secret}/#{file_name}", "#{secret_1}/#{file_name_1}"] } } + + it 'replaces all links with original filename in strong' do + doc = filter("#{html_link} #{html_link_1}", contexts) + + expect(doc.at_css('a')).to be_nil + expect(doc.at_css("strong:contains('#{file_name}')")).not_to be_nil + expect(doc.at_css("strong:contains('#{file_name_1}')")).not_to be_nil + end + end + + context 'when not all of uploads can be replaced' do + let(:contexts) { { uploads_as_attachments: ["#{secret}/#{file_name}"] } } + + it 'replaces only specific links with original filename in strong' do + doc = filter("#{html_link} #{html_link_1}", contexts) + + expect(doc.at_css("strong:contains('#{file_name}')")).not_to be_nil + expect(doc.at_css("a:contains('#{file_name_1}')")).not_to be_nil + end + end + end + end + + context 'when uploads_as_attachments is empty' do + let(:contexts) { { uploads_as_attachments: [] } } + + it 'does not replaces the link' do + doc = filter(html_link, contexts) + + expect(doc.at_css('a')).not_to be_nil + expect(doc.at_css('a')['href']).to eq upload_path + end + end +end |