diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-18 13:16:36 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-18 13:16:36 +0000 |
commit | 311b0269b4eb9839fa63f80c8d7a58f32b8138a0 (patch) | |
tree | 07e7870bca8aed6d61fdcc810731c50d2c40af47 /spec/lib/banzai/filter | |
parent | 27909cef6c4170ed9205afa7426b8d3de47cbb0c (diff) | |
download | gitlab-ce-311b0269b4eb9839fa63f80c8d7a58f32b8138a0.tar.gz |
Add latest changes from gitlab-org/gitlab@14-5-stable-eev14.5.0-rc42
Diffstat (limited to 'spec/lib/banzai/filter')
-rw-r--r-- | spec/lib/banzai/filter/emoji_filter_spec.rb | 6 | ||||
-rw-r--r-- | spec/lib/banzai/filter/footnote_filter_spec.rb | 88 | ||||
-rw-r--r-- | spec/lib/banzai/filter/markdown_filter_spec.rb | 153 | ||||
-rw-r--r-- | spec/lib/banzai/filter/plantuml_filter_spec.rb | 73 | ||||
-rw-r--r-- | spec/lib/banzai/filter/sanitization_filter_spec.rb | 64 | ||||
-rw-r--r-- | spec/lib/banzai/filter/syntax_highlight_filter_spec.rb | 232 |
6 files changed, 431 insertions, 185 deletions
diff --git a/spec/lib/banzai/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb index cb0b470eaa1..d621f63211b 100644 --- a/spec/lib/banzai/filter/emoji_filter_spec.rb +++ b/spec/lib/banzai/filter/emoji_filter_spec.rb @@ -28,9 +28,9 @@ RSpec.describe Banzai::Filter::EmojiFilter do it 'replaces name versions of trademark, copyright, and registered trademark' do doc = filter('<p>:tm: :copyright: :registered:</p>') - expect(doc.css('gl-emoji')[0].text).to eq '™' - expect(doc.css('gl-emoji')[1].text).to eq '©' - expect(doc.css('gl-emoji')[2].text).to eq '®' + expect(doc.css('gl-emoji')[0].text).to eq '™️' + expect(doc.css('gl-emoji')[1].text).to eq '©️' + expect(doc.css('gl-emoji')[2].text).to eq '®️' end it 'correctly encodes the URL' do diff --git a/spec/lib/banzai/filter/footnote_filter_spec.rb b/spec/lib/banzai/filter/footnote_filter_spec.rb index 01b7319fab1..54faa748d53 100644 --- a/spec/lib/banzai/filter/footnote_filter_spec.rb +++ b/spec/lib/banzai/filter/footnote_filter_spec.rb @@ -5,34 +5,42 @@ require 'spec_helper' RSpec.describe Banzai::Filter::FootnoteFilter do include FilterSpecHelper - # first[^1] and second[^second] + # rubocop:disable Style/AsciiComments + # first[^1] and second[^second] and third[^_😄_] # [^1]: one # [^second]: two + # [^_😄_]: three + # rubocop:enable Style/AsciiComments let(:footnote) do - <<~EOF - <p>first<sup><a href="#fn1" id="fnref1">1</a></sup> and second<sup><a href="#fn2" id="fnref2">2</a></sup></p> - <p>same reference<sup><a href="#fn1" id="fnref1">1</a></sup></p> + <<~EOF.strip_heredoc + <p>first<sup><a href="#fn-1" id="fnref-1">1</a></sup> and second<sup><a href="#fn-second" id="fnref-second">2</a></sup> and third<sup><a href="#fn-_%F0%9F%98%84_" id="fnref-_%F0%9F%98%84_">3</a></sup></p> + <ol> - <li id="fn1"> - <p>one <a href="#fnref1">↩</a></p> + <li id="fn-1"> + <p>one <a href="#fnref-1" aria-label="Back to content">↩</a></p> </li> - <li id="fn2"> - <p>two <a href="#fnref2">↩</a></p> + <li id="fn-second"> + <p>two <a href="#fnref-second" aria-label="Back to content">↩</a></p> + </li>\n<li id="fn-_%F0%9F%98%84_"> + <p>three <a href="#fnref-_%F0%9F%98%84_" aria-label="Back to content">↩</a></p> </li> </ol> EOF end let(:filtered_footnote) do - <<~EOF - <p>first<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup> and second<sup class="footnote-ref"><a href="#fn2-#{identifier}" id="fnref2-#{identifier}">2</a></sup></p> - <p>same reference<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup></p> - <section class="footnotes"><ol> - <li id="fn1-#{identifier}"> - <p>one <a href="#fnref1-#{identifier}" class="footnote-backref">↩</a></p> + <<~EOF.strip_heredoc + <p>first<sup class="footnote-ref"><a href="#fn-1-#{identifier}" id="fnref-1-#{identifier}" data-footnote-ref="">1</a></sup> and second<sup class="footnote-ref"><a href="#fn-second-#{identifier}" id="fnref-second-#{identifier}" data-footnote-ref="">2</a></sup> and third<sup class="footnote-ref"><a href="#fn-_%F0%9F%98%84_-#{identifier}" id="fnref-_%F0%9F%98%84_-#{identifier}" data-footnote-ref="">3</a></sup></p> + + <section class=\"footnotes\" data-footnotes><ol> + <li id="fn-1-#{identifier}"> + <p>one <a href="#fnref-1-#{identifier}" aria-label="Back to content" class="footnote-backref" data-footnote-backref="">↩</a></p> + </li> + <li id="fn-second-#{identifier}"> + <p>two <a href="#fnref-second-#{identifier}" aria-label="Back to content" class="footnote-backref" data-footnote-backref="">↩</a></p> </li> - <li id="fn2-#{identifier}"> - <p>two <a href="#fnref2-#{identifier}" class="footnote-backref">↩</a></p> + <li id="fn-_%F0%9F%98%84_-#{identifier}"> + <p>three <a href="#fnref-_%F0%9F%98%84_-#{identifier}" aria-label="Back to content" class="footnote-backref" data-footnote-backref="">↩</a></p> </li> </ol></section> EOF @@ -41,10 +49,56 @@ RSpec.describe Banzai::Filter::FootnoteFilter do context 'when footnotes exist' do let(:doc) { filter(footnote) } let(:link_node) { doc.css('sup > a').first } - let(:identifier) { link_node[:id].delete_prefix('fnref1-') } + let(:identifier) { link_node[:id].delete_prefix('fnref-1-') } it 'properly adds the necessary ids and classes' do expect(doc.to_html).to eq filtered_footnote end + + context 'using ruby-based HTML renderer' do + # first[^1] and second[^second] + # [^1]: one + # [^second]: two + let(:footnote) do + <<~EOF + <p>first<sup><a href="#fn1" id="fnref1">1</a></sup> and second<sup><a href="#fn2" id="fnref2">2</a></sup></p> + <p>same reference<sup><a href="#fn1" id="fnref1">1</a></sup></p> + <ol> + <li id="fn1"> + <p>one <a href="#fnref1">↩</a></p> + </li> + <li id="fn2"> + <p>two <a href="#fnref2">↩</a></p> + </li> + </ol> + EOF + end + + let(:filtered_footnote) do + <<~EOF + <p>first<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup> and second<sup class="footnote-ref"><a href="#fn2-#{identifier}" id="fnref2-#{identifier}">2</a></sup></p> + <p>same reference<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup></p> + <section class="footnotes"><ol> + <li id="fn1-#{identifier}"> + <p>one <a href="#fnref1-#{identifier}" class="footnote-backref">↩</a></p> + </li> + <li id="fn2-#{identifier}"> + <p>two <a href="#fnref2-#{identifier}" class="footnote-backref">↩</a></p> + </li> + </ol></section> + EOF + end + + let(:doc) { filter(footnote) } + let(:identifier) { link_node[:id].delete_prefix('fnref1-') } + + before do + stub_feature_flags(use_cmark_renderer: false) + end + + it 'properly adds the necessary ids and classes' do + expect(doc.to_html).to eq filtered_footnote + end + end end end diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb index c5e84a0c1e7..a310de5c015 100644 --- a/spec/lib/banzai/filter/markdown_filter_spec.rb +++ b/spec/lib/banzai/filter/markdown_filter_spec.rb @@ -5,90 +5,125 @@ require 'spec_helper' RSpec.describe Banzai::Filter::MarkdownFilter do include FilterSpecHelper - describe 'markdown engine from context' do - it 'defaults to CommonMark' do - expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance| - expect(instance).to receive(:render).and_return('test') + shared_examples_for 'renders correct markdown' do + describe 'markdown engine from context' do + it 'defaults to CommonMark' do + expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance| + expect(instance).to receive(:render).and_return('test') + end + + filter('test') end - filter('test') - end + it 'uses CommonMark' do + expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance| + expect(instance).to receive(:render).and_return('test') + end - it 'uses CommonMark' do - expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance| - expect(instance).to receive(:render).and_return('test') + filter('test', { markdown_engine: :common_mark }) end - - filter('test', { markdown_engine: :common_mark }) end - end - describe 'code block' do - context 'using CommonMark' do - before do - stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark) + describe 'code block' do + context 'using CommonMark' do + before do + stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark) + end + + it 'adds language to lang attribute when specified' do + result = filter("```html\nsome code\n```", no_sourcepos: true) + + if Feature.enabled?(:use_cmark_renderer) + expect(result).to start_with('<pre lang="html"><code>') + else + expect(result).to start_with('<pre><code lang="html">') + end + end + + it 'does not add language to lang attribute when not specified' do + result = filter("```\nsome code\n```", no_sourcepos: true) + + expect(result).to start_with('<pre><code>') + end + + it 'works with utf8 chars in language' do + result = filter("```日\nsome code\n```", no_sourcepos: true) + + if Feature.enabled?(:use_cmark_renderer) + expect(result).to start_with('<pre lang="日"><code>') + else + expect(result).to start_with('<pre><code lang="日">') + end + end + + it 'works with additional language parameters' do + result = filter("```ruby:red gem foo\nsome code\n```", no_sourcepos: true) + + if Feature.enabled?(:use_cmark_renderer) + expect(result).to start_with('<pre lang="ruby:red" data-meta="gem foo"><code>') + else + expect(result).to start_with('<pre><code lang="ruby:red gem foo">') + end + end end + end - it 'adds language to lang attribute when specified' do - result = filter("```html\nsome code\n```", no_sourcepos: true) - - expect(result).to start_with('<pre><code lang="html">') - end - - it 'does not add language to lang attribute when not specified' do - result = filter("```\nsome code\n```", no_sourcepos: true) - - expect(result).to start_with('<pre><code>') - end + describe 'source line position' do + context 'using CommonMark' do + before do + stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark) + end - it 'works with utf8 chars in language' do - result = filter("```日\nsome code\n```", no_sourcepos: true) + it 'defaults to add data-sourcepos' do + result = filter('test') - expect(result).to start_with('<pre><code lang="日">') - end + expect(result).to eq '<p data-sourcepos="1:1-1:4">test</p>' + end - it 'works with additional language parameters' do - result = filter("```ruby:red gem\nsome code\n```", no_sourcepos: true) + it 'disables data-sourcepos' do + result = filter('test', no_sourcepos: true) - expect(result).to start_with('<pre><code lang="ruby:red gem">') + expect(result).to eq '<p>test</p>' + end end end - end - describe 'source line position' do - context 'using CommonMark' do - before do - stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark) - end + describe 'footnotes in tables' do + it 'processes footnotes in table cells' do + text = <<-MD.strip_heredoc + | Column1 | + | --------- | + | foot [^1] | - it 'defaults to add data-sourcepos' do - result = filter('test') + [^1]: a footnote + MD - expect(result).to eq '<p data-sourcepos="1:1-1:4">test</p>' - end + result = filter(text, no_sourcepos: true) - it 'disables data-sourcepos' do - result = filter('test', no_sourcepos: true) + expect(result).to include('<td>foot <sup') - expect(result).to eq '<p>test</p>' + if Feature.enabled?(:use_cmark_renderer) + expect(result).to include('<section class="footnotes" data-footnotes>') + else + expect(result).to include('<section class="footnotes">') + end end end end - describe 'footnotes in tables' do - it 'processes footnotes in table cells' do - text = <<-MD.strip_heredoc - | Column1 | - | --------- | - | foot [^1] | - - [^1]: a footnote - MD + context 'using ruby-based HTML renderer' do + before do + stub_feature_flags(use_cmark_renderer: false) + end - result = filter(text, no_sourcepos: true) + it_behaves_like 'renders correct markdown' + end - expect(result).to include('<td>foot <sup') - expect(result).to include('<section class="footnotes">') + context 'using c-based HTML renderer' do + before do + stub_feature_flags(use_cmark_renderer: true) end + + it_behaves_like 'renders correct markdown' end end diff --git a/spec/lib/banzai/filter/plantuml_filter_spec.rb b/spec/lib/banzai/filter/plantuml_filter_spec.rb index 5ad94c74514..d1a3b5689a8 100644 --- a/spec/lib/banzai/filter/plantuml_filter_spec.rb +++ b/spec/lib/banzai/filter/plantuml_filter_spec.rb @@ -5,30 +5,67 @@ require 'spec_helper' RSpec.describe Banzai::Filter::PlantumlFilter do include FilterSpecHelper - it 'replaces plantuml pre tag with img tag' do - stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080") - input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' - output = '<div class="imageblock"><div class="content"><img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq"></div></div>' - doc = filter(input) + shared_examples_for 'renders correct markdown' do + it 'replaces plantuml pre tag with img tag' do + stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080") - expect(doc.to_s).to eq output + input = if Feature.enabled?(:use_cmark_renderer) + '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>' + else + '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' + end + + output = '<div class="imageblock"><div class="content"><img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq"></div></div>' + doc = filter(input) + + expect(doc.to_s).to eq output + end + + it 'does not replace plantuml pre tag with img tag if disabled' do + stub_application_setting(plantuml_enabled: false) + + if Feature.enabled?(:use_cmark_renderer) + input = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>' + output = '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>' + else + input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' + output = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' + end + + doc = filter(input) + + expect(doc.to_s).to eq output + end + + it 'does not replace plantuml pre tag with img tag if url is invalid' do + stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid") + + input = if Feature.enabled?(:use_cmark_renderer) + '<pre lang="plantuml"><code>Bob -> Sara : Hello</code></pre>' + else + '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' + end + + output = '<div class="listingblock"><div class="content"><pre class="plantuml plantuml-error"> Error: cannot connect to PlantUML server at "invalid"</pre></div></div>' + doc = filter(input) + + expect(doc.to_s).to eq output + end end - it 'does not replace plantuml pre tag with img tag if disabled' do - stub_application_setting(plantuml_enabled: false) - input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' - output = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' - doc = filter(input) + context 'using ruby-based HTML renderer' do + before do + stub_feature_flags(use_cmark_renderer: false) + end - expect(doc.to_s).to eq output + it_behaves_like 'renders correct markdown' end - it 'does not replace plantuml pre tag with img tag if url is invalid' do - stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid") - input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' - output = '<div class="listingblock"><div class="content"><pre class="plantuml plantuml-error"> Error: cannot connect to PlantUML server at "invalid"</pre></div></div>' - doc = filter(input) + context 'using c-based HTML renderer' do + before do + stub_feature_flags(use_cmark_renderer: true) + end - expect(doc.to_s).to eq output + it_behaves_like 'renders correct markdown' end end diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index f880fe06ce3..8eb8e5cf800 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -45,10 +45,10 @@ RSpec.describe Banzai::Filter::SanitizationFilter do it 'allows `text-align` property in `style` attribute on table elements' do html = <<~HTML - <table> - <tr><th style="text-align: center">Head</th></tr> - <tr><td style="text-align: right">Body</th></tr> - </table> + <table> + <tr><th style="text-align: center">Head</th></tr> + <tr><td style="text-align: right">Body</th></tr> + </table> HTML doc = filter(html) @@ -140,14 +140,14 @@ RSpec.describe Banzai::Filter::SanitizationFilter do describe 'footnotes' do it 'allows correct footnote id property on links' do - exp = %q(<a href="#fn1" id="fnref1">foo/bar.md</a>) + exp = %q(<a href="#fn-first" id="fnref-first">foo/bar.md</a>) act = filter(exp) expect(act.to_html).to eq exp end it 'allows correct footnote id property on li element' do - exp = %q(<ol><li id="fn1">footnote</li></ol>) + exp = %q(<ol><li id="fn-last">footnote</li></ol>) act = filter(exp) expect(act.to_html).to eq exp @@ -156,7 +156,7 @@ RSpec.describe Banzai::Filter::SanitizationFilter do it 'removes invalid id for footnote links' do exp = %q(<a href="#fn1">link</a>) - %w[fnrefx test xfnref1].each do |id| + %w[fnrefx test xfnref-1].each do |id| act = filter(%(<a href="#fn1" id="#{id}">link</a>)) expect(act.to_html).to eq exp @@ -166,18 +166,58 @@ RSpec.describe Banzai::Filter::SanitizationFilter do it 'removes invalid id for footnote li' do exp = %q(<ol><li>footnote</li></ol>) - %w[fnx test xfn1].each do |id| + %w[fnx test xfn-1].each do |id| act = filter(%(<ol><li id="#{id}">footnote</li></ol>)) expect(act.to_html).to eq exp end end - it 'allows footnotes numbered higher than 9' do - exp = %q(<a href="#fn15" id="fnref15">link</a><ol><li id="fn15">footnote</li></ol>) - act = filter(exp) + context 'using ruby-based HTML renderer' do + before do + stub_feature_flags(use_cmark_renderer: false) + end - expect(act.to_html).to eq exp + it 'allows correct footnote id property on links' do + exp = %q(<a href="#fn1" id="fnref1">foo/bar.md</a>) + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'allows correct footnote id property on li element' do + exp = %q(<ol><li id="fn1">footnote</li></ol>) + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'removes invalid id for footnote links' do + exp = %q(<a href="#fn1">link</a>) + + %w[fnrefx test xfnref1].each do |id| + act = filter(%(<a href="#fn1" id="#{id}">link</a>)) + + expect(act.to_html).to eq exp + end + end + + it 'removes invalid id for footnote li' do + exp = %q(<ol><li>footnote</li></ol>) + + %w[fnx test xfn1].each do |id| + act = filter(%(<ol><li id="#{id}">footnote</li></ol>)) + + expect(act.to_html).to eq exp + end + end + + it 'allows footnotes numbered higher than 9' do + exp = %q(<a href="#fn15" id="fnref15">link</a><ol><li id="fn15">footnote</li></ol>) + act = filter(exp) + + expect(act.to_html).to eq exp + end end end end diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb index 7e45ecdd135..dfe022b51d2 100644 --- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb +++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb @@ -11,130 +11,210 @@ RSpec.describe Banzai::Filter::SyntaxHighlightFilter do # after Markdown rendering. result = filter(%{<pre lang="#{lang}"><code><script>alert(1)</script></code></pre>}) - expect(result.to_html).not_to include("<script>alert(1)</script>") - expect(result.to_html).to include("alert(1)") + # `(1)` symbols are wrapped by lexer tags. + expect(result.to_html).not_to match(%r{<script>alert.*<\/script>}) + + # `<>` stands for lexer tags like <span ...>, not <s above. + expect(result.to_html).to match(%r{alert(<.*>)?\((<.*>)?1(<.*>)?\)}) end end - context "when no language is specified" do - it "highlights as plaintext" do - result = filter('<pre><code>def fun end</code></pre>') + shared_examples_for 'renders correct markdown' do + context "when no language is specified" do + it "highlights as plaintext" do + result = filter('<pre><code>def fun end</code></pre>') - expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">def fun end</span></code></pre>') - end + expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">def fun end</span></code></pre>') + end - include_examples "XSS prevention", "" - end + include_examples "XSS prevention", "" + end - context "when contains mermaid diagrams" do - it "ignores mermaid blocks" do - result = filter('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') + context "when contains mermaid diagrams" do + it "ignores mermaid blocks" do + result = filter('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') - expect(result.to_html).to eq('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') + expect(result.to_html).to eq('<pre data-mermaid-style="display"><code>mermaid code</code></pre>') + end end - end - context "when a valid language is specified" do - it "highlights as that language" do - result = filter('<pre><code lang="ruby">def fun end</code></pre>') + context "when a valid language is specified" do + it "highlights as that language" do + result = if Feature.enabled?(:use_cmark_renderer) + filter('<pre lang="ruby"><code>def fun end</code></pre>') + else + filter('<pre><code lang="ruby">def fun end</code></pre>') + end + + expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight language-ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></span></code></pre>') + end - expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight language-ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></span></code></pre>') + include_examples "XSS prevention", "ruby" end - include_examples "XSS prevention", "ruby" - end + context "when an invalid language is specified" do + it "highlights as plaintext" do + result = if Feature.enabled?(:use_cmark_renderer) + filter('<pre lang="gnuplot"><code>This is a test</code></pre>') + else + filter('<pre><code lang="gnuplot">This is a test</code></pre>') + end - context "when an invalid language is specified" do - it "highlights as plaintext" do - result = filter('<pre><code lang="gnuplot">This is a test</code></pre>') + expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre>') + end - expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre>') + include_examples "XSS prevention", "gnuplot" end - include_examples "XSS prevention", "gnuplot" - end + context "languages that should be passed through" do + let(:delimiter) { described_class::LANG_PARAMS_DELIMITER } + let(:data_attr) { described_class::LANG_PARAMS_ATTR } - context "languages that should be passed through" do - let(:delimiter) { described_class::PARAMS_DELIMITER } - let(:data_attr) { described_class::LANG_PARAMS_ATTR } + %w(math mermaid plantuml suggestion).each do |lang| + context "when #{lang} is specified" do + it "highlights as plaintext but with the correct language attribute and class" do + result = if Feature.enabled?(:use_cmark_renderer) + filter(%{<pre lang="#{lang}"><code>This is a test</code></pre>}) + else + filter(%{<pre><code lang="#{lang}">This is a test</code></pre>}) + end - %w(math mermaid plantuml suggestion).each do |lang| - context "when #{lang} is specified" do - it "highlights as plaintext but with the correct language attribute and class" do - result = filter(%{<pre><code lang="#{lang}">This is a test</code></pre>}) + expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>}) + end - expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>}) + include_examples "XSS prevention", lang end - include_examples "XSS prevention", lang + context "when #{lang} has extra params" do + let(:lang_params) { 'foo-bar-kux' } + + let(:xss_lang) do + if Feature.enabled?(:use_cmark_renderer) + "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" + else + "#{lang}#{described_class::LANG_PARAMS_DELIMITER}<script>alert(1)</script>" + end + end + + it "includes data-lang-params tag with extra information" do + result = if Feature.enabled?(:use_cmark_renderer) + filter(%{<pre lang="#{lang}" data-meta="#{lang_params}"><code>This is a test</code></pre>}) + else + filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}">This is a test</code></pre>}) + end + + expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>}) + end + + include_examples "XSS prevention", lang + + if Feature.enabled?(:use_cmark_renderer) + include_examples "XSS prevention", + "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" + else + include_examples "XSS prevention", + "#{lang}#{described_class::LANG_PARAMS_DELIMITER}<script>alert(1)</script>" + end + + include_examples "XSS prevention", + "#{lang} data-meta=\"foo-bar-kux\"<script>alert(1)</script>" + end end - context "when #{lang} has extra params" do - let(:lang_params) { 'foo-bar-kux' } + context 'when multiple param delimiters are used' do + let(:lang) { 'suggestion' } + let(:lang_params) { '-1+10' } - it "includes data-lang-params tag with extra information" do - result = filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}">This is a test</code></pre>}) + let(:expected_result) do + %{<pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params} more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>} + end - expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>}) + context 'when delimiter is space' do + it 'delimits on the first appearance' do + if Feature.enabled?(:use_cmark_renderer) + result = filter(%{<pre lang="#{lang}" data-meta="#{lang_params} more-things"><code>This is a test</code></pre>}) + + expect(result.to_html).to eq(expected_result) + else + result = filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}#{delimiter}more-things">This is a test</code></pre>}) + + expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}#{delimiter}more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>}) + end + end end - include_examples "XSS prevention", lang - include_examples "XSS prevention", - "#{lang}#{described_class::PARAMS_DELIMITER}<script>alert(1)</script>" - include_examples "XSS prevention", - "#{lang}#{described_class::PARAMS_DELIMITER}<script>alert(1)</script>" + context 'when delimiter is colon' do + it 'delimits on the first appearance' do + result = filter(%{<pre lang="#{lang}#{delimiter}#{lang_params} more-things"><code>This is a test</code></pre>}) + + if Feature.enabled?(:use_cmark_renderer) + expect(result.to_html).to eq(expected_result) + else + expect(result.to_html).to eq(%{<pre class=\"code highlight js-syntax-highlight language-plaintext\" lang=\"plaintext\" v-pre=\"true\"><code><span id=\"LC1\" class=\"line\" lang=\"plaintext\">This is a test</span></code></pre>}) + end + end + end end end - context 'when multiple param delimiters are used' do - let(:lang) { 'suggestion' } - let(:lang_params) { '-1+10' } + context "when sourcepos metadata is available" do + it "includes it in the highlighted code block" do + result = filter('<pre data-sourcepos="1:1-3:3"><code lang="plaintext">This is a test</code></pre>') - it "delimits on the first appearance" do - result = filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}#{delimiter}more-things">This is a test</code></pre>}) - - expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight language-#{lang}" lang="#{lang}" #{data_attr}="#{lang_params}#{delimiter}more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>}) + expect(result.to_html).to eq('<pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre>') end end - end - context "when sourcepos metadata is available" do - it "includes it in the highlighted code block" do - result = filter('<pre data-sourcepos="1:1-3:3"><code lang="plaintext">This is a test</code></pre>') + context "when Rouge lexing fails" do + before do + allow_next_instance_of(Rouge::Lexers::Ruby) do |instance| + allow(instance).to receive(:stream_tokens).and_raise(StandardError) + end + end - expect(result.to_html).to eq('<pre data-sourcepos="1:1-3:3" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre>') - end - end + it "highlights as plaintext" do + result = if Feature.enabled?(:use_cmark_renderer) + filter('<pre lang="ruby"><code>This is a test</code></pre>') + else + filter('<pre><code lang="ruby">This is a test</code></pre>') + end - context "when Rouge lexing fails" do - before do - allow_next_instance_of(Rouge::Lexers::Ruby) do |instance| - allow(instance).to receive(:stream_tokens).and_raise(StandardError) + expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code><span id="LC1" class="line" lang="">This is a test</span></code></pre>') end + + include_examples "XSS prevention", "ruby" end - it "highlights as plaintext" do - result = filter('<pre><code lang="ruby">This is a test</code></pre>') + context "when Rouge lexing fails after a retry" do + before do + allow_next_instance_of(Rouge::Lexers::PlainText) do |instance| + allow(instance).to receive(:stream_tokens).and_raise(StandardError) + end + end - expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code><span id="LC1" class="line" lang="">This is a test</span></code></pre>') - end + it "does not add highlighting classes" do + result = filter('<pre><code>This is a test</code></pre>') + + expect(result.to_html).to eq('<pre><code>This is a test</code></pre>') + end - include_examples "XSS prevention", "ruby" + include_examples "XSS prevention", "ruby" + end end - context "when Rouge lexing fails after a retry" do + context 'using ruby-based HTML renderer' do before do - allow_next_instance_of(Rouge::Lexers::PlainText) do |instance| - allow(instance).to receive(:stream_tokens).and_raise(StandardError) - end + stub_feature_flags(use_cmark_renderer: false) end - it "does not add highlighting classes" do - result = filter('<pre><code>This is a test</code></pre>') + it_behaves_like 'renders correct markdown' + end - expect(result.to_html).to eq('<pre><code>This is a test</code></pre>') + context 'using c-based HTML renderer' do + before do + stub_feature_flags(use_cmark_renderer: true) end - include_examples "XSS prevention", "ruby" + it_behaves_like 'renders correct markdown' end end |