Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 13:34:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 13:34:06 +0300
commit859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch)
treed7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /spec/lib/banzai
parent446d496a6d000c73a304be52587cd9bbc7493136 (diff)
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'spec/lib/banzai')
-rw-r--r--spec/lib/banzai/filter/asset_proxy_filter_spec.rb16
-rw-r--r--spec/lib/banzai/filter/custom_emoji_filter_spec.rb80
-rw-r--r--spec/lib/banzai/filter/feature_flag_reference_filter_spec.rb223
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/truncate_source_filter_spec.rb2
-rw-r--r--spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb5
-rw-r--r--spec/lib/banzai/pipeline/full_pipeline_spec.rb12
-rw-r--r--spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb118
-rw-r--r--spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb2
-rw-r--r--spec/lib/banzai/reference_parser/feature_flag_parser_spec.rb53
10 files changed, 506 insertions, 7 deletions
diff --git a/spec/lib/banzai/filter/asset_proxy_filter_spec.rb b/spec/lib/banzai/filter/asset_proxy_filter_spec.rb
index 1f886059bf6..81aa8d35ebc 100644
--- a/spec/lib/banzai/filter/asset_proxy_filter_spec.rb
+++ b/spec/lib/banzai/filter/asset_proxy_filter_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Banzai::Filter::AssetProxyFilter do
stub_application_setting(asset_proxy_enabled: true)
stub_application_setting(asset_proxy_secret_key: 'shared-secret')
stub_application_setting(asset_proxy_url: 'https://assets.example.com')
- stub_application_setting(asset_proxy_whitelist: %w(gitlab.com *.mydomain.com))
+ stub_application_setting(asset_proxy_allowlist: %w(gitlab.com *.mydomain.com))
described_class.initialize_settings
@@ -39,16 +39,26 @@ RSpec.describe Banzai::Filter::AssetProxyFilter do
expect(Gitlab.config.asset_proxy.domain_regexp).to eq(/^(gitlab\.com|.*?\.mydomain\.com)$/i)
end
- context 'when whitelist is empty' do
+ context 'when allowlist is empty' do
it 'defaults to the install domain' do
stub_application_setting(asset_proxy_enabled: true)
- stub_application_setting(asset_proxy_whitelist: [])
+ stub_application_setting(asset_proxy_allowlist: [])
described_class.initialize_settings
expect(Gitlab.config.asset_proxy.allowlist).to eq [Gitlab.config.gitlab.host]
end
end
+
+ it 'supports deprecated whitelist settings' do
+ stub_application_setting(asset_proxy_enabled: true)
+ stub_application_setting(asset_proxy_whitelist: %w(foo.com bar.com))
+ stub_application_setting(asset_proxy_allowlist: [])
+
+ described_class.initialize_settings
+
+ expect(Gitlab.config.asset_proxy.allowlist).to eq %w(foo.com bar.com)
+ end
end
context 'when properly configured' do
diff --git a/spec/lib/banzai/filter/custom_emoji_filter_spec.rb b/spec/lib/banzai/filter/custom_emoji_filter_spec.rb
new file mode 100644
index 00000000000..ca8c9750e7f
--- /dev/null
+++ b/spec/lib/banzai/filter/custom_emoji_filter_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::CustomEmojiFilter do
+ include FilterSpecHelper
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:custom_emoji) { create(:custom_emoji, name: 'tanuki', group: group) }
+ let_it_be(:custom_emoji2) { create(:custom_emoji, name: 'happy_tanuki', group: group, file: 'https://foo.bar/happy.png') }
+
+ it 'replaces supported name custom emoji' do
+ doc = filter('<p>:tanuki:</p>', project: project)
+
+ expect(doc.css('gl-emoji').first.attributes['title'].value).to eq('tanuki')
+ expect(doc.css('gl-emoji img').size).to eq 1
+ end
+
+ it 'ignores non existent custom emoji' do
+ exp = act = '<p>:foo:</p>'
+ doc = filter(act)
+
+ expect(doc.to_html).to match Regexp.escape(exp)
+ end
+
+ it 'correctly uses the custom emoji URL' do
+ doc = filter('<p>:tanuki:</p>')
+
+ expect(doc.css('img').first.attributes['src'].value).to eq(custom_emoji.file)
+ end
+
+ it 'matches with adjacent text' do
+ doc = filter('tanuki (:tanuki:)')
+
+ expect(doc.css('img').size).to eq 1
+ end
+
+ it 'matches multiple same custom emoji' do
+ doc = filter(':tanuki: :tanuki:')
+
+ expect(doc.css('img').size).to eq 2
+ end
+
+ it 'matches multiple custom emoji' do
+ doc = filter(':tanuki: (:happy_tanuki:)')
+
+ expect(doc.css('img').size).to eq 2
+ end
+
+ it 'does not match enclosed colons' do
+ doc = filter('tanuki:tanuki:')
+
+ expect(doc.css('img').size).to be 0
+ end
+
+ it 'keeps whitespace intact' do
+ doc = filter('This deserves a :tanuki:, big time.')
+
+ expect(doc.to_html).to match(/^This deserves a <gl-emoji.+>, big time\.\z/)
+ end
+
+ it 'does not match emoji in a string' do
+ doc = filter("'2a00:tanuki:100::1'")
+
+ expect(doc.css('gl-emoji').size).to eq 0
+ end
+
+ it 'does not do N+1 query' do
+ create(:custom_emoji, name: 'party-parrot', group: group)
+
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ filter('<p>:tanuki:</p>')
+ end
+
+ expect do
+ filter('<p>:tanuki: :party-parrot:</p>')
+ end.not_to exceed_all_query_limit(control_count.count)
+ end
+end
diff --git a/spec/lib/banzai/filter/feature_flag_reference_filter_spec.rb b/spec/lib/banzai/filter/feature_flag_reference_filter_spec.rb
new file mode 100644
index 00000000000..2d7089853cf
--- /dev/null
+++ b/spec/lib/banzai/filter/feature_flag_reference_filter_spec.rb
@@ -0,0 +1,223 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::FeatureFlagReferenceFilter do
+ include FilterSpecHelper
+
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:feature_flag) { create(:operations_feature_flag, project: project) }
+ let_it_be(:reference) { feature_flag.to_reference }
+
+ it 'requires project context' do
+ expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
+ end
+
+ %w(pre code a style).each do |elem|
+ it "ignores valid references contained inside '#{elem}' element" do
+ exp = act = "<#{elem}>Feature Flag #{reference}</#{elem}>"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'with internal reference' do
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.edit_project_feature_flag_url(project, feature_flag)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Feature Flag (#{reference}.)")
+
+ expect(doc.to_html).to match(%r{\(<a.+>#{Regexp.escape(reference)}</a>\.\)})
+ end
+
+ it 'ignores invalid feature flag IIDs' do
+ exp = act = "Check [feature_flag:#{non_existing_record_id}]"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'includes a title attribute' do
+ doc = reference_filter("Feature Flag #{reference}")
+
+ expect(doc.css('a').first.attr('title')).to eq feature_flag.name
+ end
+
+ it 'escapes the title attribute' do
+ allow(feature_flag).to receive(:name).and_return(%{"></a>whatever<a title="})
+ doc = reference_filter("Feature Flag #{reference}")
+
+ expect(doc.text).to eq "Feature Flag #{reference}"
+ end
+
+ it 'includes default classes' do
+ doc = reference_filter("Feature Flag #{reference}")
+
+ expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-feature_flag has-tooltip'
+ end
+
+ it 'includes a data-project attribute' do
+ doc = reference_filter("Feature Flag #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-project')
+ expect(link.attr('data-project')).to eq project.id.to_s
+ end
+
+ it 'includes a data-feature-flag attribute' do
+ doc = reference_filter("See #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-feature-flag')
+ expect(link.attr('data-feature-flag')).to eq feature_flag.id.to_s
+ end
+
+ it 'supports an :only_path context' do
+ doc = reference_filter("Feature Flag #{reference}", only_path: true)
+ link = doc.css('a').first.attr('href')
+
+ expect(link).not_to match %r(https?://)
+ expect(link).to eq urls.edit_project_feature_flag_url(project, feature_flag.iid, only_path: true)
+ end
+ end
+
+ context 'with cross-project / cross-namespace complete reference' do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project2) { create(:project, :public, namespace: namespace) }
+ let_it_be(:feature_flag) { create(:operations_feature_flag, project: project2) }
+ let_it_be(:reference) { "[feature_flag:#{project2.full_path}/#{feature_flag.iid}]" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.edit_project_feature_flag_url(project2, feature_flag)
+ end
+
+ it 'produces a valid text in a link' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eql(reference)
+ end
+
+ it 'produces a valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{reference}.)")
+ end
+
+ it 'ignores invalid feature flag IIDs on the referenced project' do
+ exp = act = "Check [feature_flag:#{non_existing_record_id}]"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'with cross-project / same-namespace complete reference' do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, :public, namespace: namespace) }
+ let_it_be(:project2) { create(:project, :public, namespace: namespace) }
+ let_it_be(:feature_flag) { create(:operations_feature_flag, project: project2) }
+ let_it_be(:reference) { "[feature_flag:#{project2.full_path}/#{feature_flag.iid}]" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.edit_project_feature_flag_url(project2, feature_flag)
+ end
+
+ it 'produces a valid text in a link' do
+ doc = reference_filter("See ([feature_flag:#{project2.path}/#{feature_flag.iid}].)")
+
+ expect(doc.css('a').first.text).to eql("[feature_flag:#{project2.path}/#{feature_flag.iid}]")
+ end
+
+ it 'produces a valid text' do
+ doc = reference_filter("See ([feature_flag:#{project2.path}/#{feature_flag.iid}].)")
+
+ expect(doc.text).to eql("See ([feature_flag:#{project2.path}/#{feature_flag.iid}].)")
+ end
+
+ it 'ignores invalid feature flag IIDs on the referenced project' do
+ exp = act = "Check [feature_flag:#{non_existing_record_id}]"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'with cross-project shorthand reference' do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, :public, namespace: namespace) }
+ let_it_be(:project2) { create(:project, :public, namespace: namespace) }
+ let_it_be(:feature_flag) { create(:operations_feature_flag, project: project2) }
+ let_it_be(:reference) { "[feature_flag:#{project2.path}/#{feature_flag.iid}]" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.edit_project_feature_flag_url(project2, feature_flag)
+ end
+
+ it 'produces a valid text in a link' do
+ doc = reference_filter("See ([feature_flag:#{project2.path}/#{feature_flag.iid}].)")
+
+ expect(doc.css('a').first.text).to eql("[feature_flag:#{project2.path}/#{feature_flag.iid}]")
+ end
+
+ it 'produces a valid text' do
+ doc = reference_filter("See ([feature_flag:#{project2.path}/#{feature_flag.iid}].)")
+
+ expect(doc.text).to eql("See ([feature_flag:#{project2.path}/#{feature_flag.iid}].)")
+ end
+
+ it 'ignores invalid feature flag IDs on the referenced project' do
+ exp = act = "Check [feature_flag:#{non_existing_record_id}]"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'with cross-project URL reference' do
+ let_it_be(:namespace) { create(:namespace, name: 'cross-reference') }
+ let_it_be(:project2) { create(:project, :public, namespace: namespace) }
+ let_it_be(:feature_flag) { create(:operations_feature_flag, project: project2) }
+ let_it_be(:reference) { urls.edit_project_feature_flag_url(project2, feature_flag) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.edit_project_feature_flag_url(project2, feature_flag)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.to_html).to match(%r{\(<a.+>#{Regexp.escape(feature_flag.to_reference(project))}</a>\.\)})
+ end
+
+ it 'ignores invalid feature flag IIDs on the referenced project' do
+ act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to match(%r{<a.+>#{Regexp.escape(invalidate_reference(reference))}</a>})
+ end
+ end
+
+ context 'with group context' do
+ let_it_be(:group) { create(:group) }
+
+ it 'links to a valid reference' do
+ reference = "[feature_flag:#{project.full_path}/#{feature_flag.iid}]"
+ result = reference_filter("See #{reference}", { project: nil, group: group } )
+
+ expect(result.css('a').first.attr('href')).to eq(urls.edit_project_feature_flag_url(project, feature_flag))
+ end
+
+ it 'ignores internal references' do
+ exp = act = "See [feature_flag:#{feature_flag.iid}]"
+
+ expect(reference_filter(act, project: nil, group: group).to_html).to eq exp
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index df78a3321ba..811c2aca342 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -216,7 +216,7 @@ RSpec.describe Banzai::Filter::MergeRequestReferenceFilter do
end
context 'URL reference for a commit' do
- let(:mr) { create(:merge_request, :with_diffs) }
+ let(:mr) { create(:merge_request) }
let(:reference) do
urls.project_merge_request_url(mr.project, mr) + "/diffs?commit_id=#{mr.diff_head_sha}"
end
diff --git a/spec/lib/banzai/filter/truncate_source_filter_spec.rb b/spec/lib/banzai/filter/truncate_source_filter_spec.rb
index b0c6d91daa8..d5eb8b738b1 100644
--- a/spec/lib/banzai/filter/truncate_source_filter_spec.rb
+++ b/spec/lib/banzai/filter/truncate_source_filter_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Banzai::Filter::TruncateSourceFilter do
it 'truncates UTF-8 text by bytes, on a character boundary' do
utf8_text = '日本語の文字が大きい'
- truncated = '日…'
+ truncated = '日...'
expect(filter(utf8_text, limit: truncated.bytesize)).to eq(truncated)
expect(filter(utf8_text, limit: utf8_text.bytesize)).to eq(utf8_text)
diff --git a/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb b/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb
index 41a91c56f3b..ad4256c2045 100644
--- a/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb
@@ -3,11 +3,14 @@
require 'spec_helper'
RSpec.describe Banzai::Pipeline::BroadcastMessagePipeline do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
before do
stub_commonmark_sourcepos_disabled
end
- subject { described_class.to_html(exp, project: spy) }
+ subject { described_class.to_html(exp, project: project) }
context "allows `a` elements" do
let(:exp) { "<a>Link</a>" }
diff --git a/spec/lib/banzai/pipeline/full_pipeline_spec.rb b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
index 9391ca386cf..bcee6f8f65d 100644
--- a/spec/lib/banzai/pipeline/full_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
@@ -131,4 +131,16 @@ RSpec.describe Banzai::Pipeline::FullPipeline do
expect(output).to include("test [[<em>TOC</em>]]")
end
end
+
+ describe 'backslash escapes' do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue) { create(:issue, project: project) }
+
+ it 'does not convert an escaped reference' do
+ markdown = "\\#{issue.to_reference}"
+ output = described_class.to_html(markdown, project: project)
+
+ expect(output).to include("<span>#</span>#{issue.iid}")
+ end
+ end
end
diff --git a/spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb b/spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb
new file mode 100644
index 00000000000..241d6db4f11
--- /dev/null
+++ b/spec/lib/banzai/pipeline/plain_markdown_pipeline_spec.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Pipeline::PlainMarkdownPipeline do
+ using RSpec::Parameterized::TableSyntax
+
+ describe 'backslash escapes' do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue) { create(:issue, project: project) }
+
+ def correct_html_included(markdown, expected)
+ result = described_class.call(markdown, {})
+
+ expect(result[:output].to_html).to include(expected)
+
+ result
+ end
+
+ context 'when feature flag honor_escaped_markdown is disabled' do
+ before do
+ stub_feature_flags(honor_escaped_markdown: false)
+ end
+
+ it 'does not escape the markdown' do
+ result = described_class.call(%q(\!), project: project)
+ output = result[:output].to_html
+
+ expect(output).to eq('<p data-sourcepos="1:1-1:2">!</p>')
+ expect(result[:escaped_literals]).to be_falsey
+ end
+ end
+
+ # Test strings taken from https://spec.commonmark.org/0.29/#backslash-escapes
+ describe 'CommonMark tests', :aggregate_failures do
+ it 'converts all ASCII punctuation to literals' do
+ markdown = %q(\!\"\#\$\%\&\'\*\+\,\-\.\/\:\;\<\=\>\?\@\[\]\^\_\`\{\|\}\~) + %q[\(\)\\\\]
+ punctuation = %w(! " # $ % &amp; ' * + , - . / : ; &lt; = &gt; ? @ [ \\ ] ^ _ ` { | } ~) + %w[( )]
+
+ result = described_class.call(markdown, project: project)
+ output = result[:output].to_html
+
+ punctuation.each { |char| expect(output).to include("<span>#{char}</span>") }
+ expect(result[:escaped_literals]).to be_truthy
+ end
+
+ it 'does not convert other characters to literals' do
+ markdown = %q(\→\A\a\ \3\φ\«)
+ expected = '\→\A\a\ \3\φ\«'
+
+ result = correct_html_included(markdown, expected)
+ expect(result[:escaped_literals]).to be_falsey
+ end
+
+ describe 'escaped characters are treated as regular characters and do not have their usual Markdown meanings' do
+ where(:markdown, :expected) do
+ %q(\*not emphasized*) | %q(<span>*</span>not emphasized*)
+ %q(\<br/> not a tag) | %q(<span>&lt;</span>br/&gt; not a tag)
+ %q!\[not a link](/foo)! | %q!<span>[</span>not a link](/foo)!
+ %q(\`not code`) | %q(<span>`</span>not code`)
+ %q(1\. not a list) | %q(1<span>.</span> not a list)
+ %q(\# not a heading) | %q(<span>#</span> not a heading)
+ %q(\[foo]: /url "not a reference") | %q(<span>[</span>foo]: /url "not a reference")
+ %q(\&ouml; not a character entity) | %q(<span>&amp;</span>ouml; not a character entity)
+ end
+
+ with_them do
+ it 'keeps them as literals' do
+ correct_html_included(markdown, expected)
+ end
+ end
+ end
+
+ it 'backslash is itself escaped, the following character is not' do
+ markdown = %q(\\\\*emphasis*)
+ expected = %q(<span>\</span><em>emphasis</em>)
+
+ correct_html_included(markdown, expected)
+ end
+
+ it 'backslash at the end of the line is a hard line break' do
+ markdown = <<~MARKDOWN
+ foo\\
+ bar
+ MARKDOWN
+ expected = "foo<br>\nbar"
+
+ correct_html_included(markdown, expected)
+ end
+
+ describe 'backslash escapes do not work in code blocks, code spans, autolinks, or raw HTML' do
+ where(:markdown, :expected) do
+ %q(`` \[\` ``) | %q(<code>\[\`</code>)
+ %q( \[\]) | %Q(<code>\\[\\]\n</code>)
+ %Q(~~~\n\\[\\]\n~~~) | %Q(<code>\\[\\]\n</code>)
+ %q(<http://example.com?find=\*>) | %q(<a href="http://example.com?find=%5C*">http://example.com?find=\*</a>)
+ %q[<a href="/bar\/)">] | %q[<a href="/bar%5C/)">]
+ end
+
+ with_them do
+ it { correct_html_included(markdown, expected) }
+ end
+ end
+
+ describe 'work in all other contexts, including URLs and link titles, link references, and info strings in fenced code blocks' do
+ where(:markdown, :expected) do
+ %q![foo](/bar\* "ti\*tle")! | %q(<a href="/bar*" title="ti*tle">foo</a>)
+ %Q![foo]\n\n[foo]: /bar\\* "ti\\*tle"! | %q(<a href="/bar*" title="ti*tle">foo</a>)
+ %Q(``` foo\\+bar\nfoo\n```) | %Q(<code lang="foo+bar">foo\n</code>)
+ end
+
+ with_them do
+ it { correct_html_included(markdown, expected) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb b/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb
index f0498f41b61..c628d8d5b41 100644
--- a/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb
@@ -30,6 +30,6 @@ RSpec.describe Banzai::Pipeline::PreProcessPipeline do
result = described_class.call(text, limit: 12)
- expect(result[:output]).to eq('foo foo f…')
+ expect(result[:output]).to eq('foo foo f...')
end
end
diff --git a/spec/lib/banzai/reference_parser/feature_flag_parser_spec.rb b/spec/lib/banzai/reference_parser/feature_flag_parser_spec.rb
new file mode 100644
index 00000000000..288eb9ae360
--- /dev/null
+++ b/spec/lib/banzai/reference_parser/feature_flag_parser_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::ReferenceParser::FeatureFlagParser do
+ include ReferenceParserHelpers
+
+ subject { described_class.new(Banzai::RenderContext.new(project, user)) }
+
+ let(:link) { empty_html_link }
+
+ describe '#nodes_visible_to_user' do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+ let(:feature_flag) { create(:operations_feature_flag, project: project) }
+
+ context 'when the link has a data-issue attribute' do
+ before do
+ link['data-feature-flag'] = feature_flag.id.to_s
+ end
+
+ it_behaves_like "referenced feature visibility", "issues", "merge_requests" do
+ before do
+ project.add_developer(user) if enable_user?
+ end
+ end
+ end
+ end
+
+ describe '#referenced_by' do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:feature_flag) { create(:operations_feature_flag, project: project) }
+
+ describe 'when the link has a data-feature-flag attribute' do
+ context 'using an existing feature flag ID' do
+ it 'returns an Array of feature flags' do
+ link['data-feature-flag'] = feature_flag.id.to_s
+
+ expect(subject.referenced_by([link])).to eq([feature_flag])
+ end
+ end
+
+ context 'using a non-existing feature flag ID' do
+ it 'returns an empty Array' do
+ link['data-feature-flag'] = ''
+
+ expect(subject.referenced_by([link])).to eq([])
+ end
+ end
+ end
+ end
+end