summaryrefslogtreecommitdiff
path: root/spec/lib/banzai/filter/design_reference_filter_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/banzai/filter/design_reference_filter_spec.rb')
-rw-r--r--spec/lib/banzai/filter/design_reference_filter_spec.rb287
1 files changed, 0 insertions, 287 deletions
diff --git a/spec/lib/banzai/filter/design_reference_filter_spec.rb b/spec/lib/banzai/filter/design_reference_filter_spec.rb
deleted file mode 100644
index 847c398964a..00000000000
--- a/spec/lib/banzai/filter/design_reference_filter_spec.rb
+++ /dev/null
@@ -1,287 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Banzai::Filter::DesignReferenceFilter do
- include FilterSpecHelper
- include DesignManagementTestHelpers
-
- let_it_be(:issue) { create(:issue, iid: 10) }
- let_it_be(:issue_proj_2) { create(:issue, iid: 20) }
- let_it_be(:issue_b) { create(:issue, project: issue.project) }
- let_it_be(:developer) { create(:user, developer_projects: [issue.project, issue_proj_2.project]) }
- let_it_be(:design_a) { create(:design, :with_versions, issue: issue) }
- let_it_be(:design_b) { create(:design, :with_versions, issue: issue_b) }
- let_it_be(:design_proj_2) { create(:design, :with_versions, issue: issue_proj_2) }
- let_it_be(:project_with_no_lfs) { create(:project, :public, lfs_enabled: false) }
-
- let(:design) { design_a }
- let(:project) { issue.project }
- let(:project_2) { issue_proj_2.project }
- let(:reference) { design.to_reference }
- let(:design_url) { url_for_design(design) }
- let(:input_text) { "Added #{design_url}" }
- let(:doc) { process_doc(input_text) }
- let(:current_user) { developer }
-
- before do
- enable_design_management
- end
-
- shared_examples 'a no-op filter' do
- it 'does nothing' do
- expect(process(input_text)).to eq(baseline(input_text).to_html)
- end
- end
-
- shared_examples 'a good link reference' do
- let(:link) { doc.css('a').first }
- let(:href) { url_for_design(design) }
- let(:title) { design.filename }
-
- it 'produces a good link', :aggregate_failures do
- expect(link.attr('href')).to eq(href)
- expect(link.attr('title')).to eq(title)
- expect(link.attr('class')).to eq('gfm gfm-design has-tooltip')
- expect(link.attr('data-project')).to eq(design.project.id.to_s)
- expect(link.attr('data-issue')).to eq(design.issue.id.to_s)
- expect(link.attr('data-original')).to eq(href)
- expect(link.attr('data-reference-type')).to eq('design')
- expect(link.text).to eq(design.to_reference(project))
- end
- end
-
- describe '.call' do
- it 'requires project context' do
- expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
- end
- end
-
- it 'does not error when we add redaction to the pipeline' do
- enable_design_management
-
- res = reference_pipeline(redact: true).to_document(input_text)
-
- expect(res.css('a').first).to be_present
- end
-
- describe '#call' do
- describe 'feature flags' do
- context 'design management is not enabled' do
- before do
- enable_design_management(false)
- end
-
- it_behaves_like 'a no-op filter'
- end
- end
- end
-
- %w(pre code a style).each do |elem|
- context "wrapped in a <#{elem}/>" do
- let(:input_text) { "<#{elem}>Design #{url_for_design(design)}</#{elem}>" }
-
- it_behaves_like 'a no-op filter'
- end
- end
-
- describe '.identifier' do
- where(:filename) do
- [
- ['simple.png'],
- ['SIMPLE.PNG'],
- ['has spaces.png'],
- ['has-hyphen.jpg'],
- ['snake_case.svg'],
- ['has "quotes".svg'],
- ['has <special> characters [o].svg']
- ]
- end
-
- with_them do
- let(:design) { build(:design, issue: issue, filename: filename) }
- let(:url) { url_for_design(design) }
- let(:pattern) { described_class.object_class.link_reference_pattern }
- let(:parsed) do
- m = pattern.match(url)
- described_class.identifier(m) if m
- end
-
- it 'can parse the reference' do
- expect(parsed).to have_attributes(
- filename: filename,
- issue_iid: issue.iid
- )
- end
- end
- end
-
- describe 'static properties' do
- specify do
- expect(described_class).to have_attributes(
- object_sym: :design,
- object_class: ::DesignManagement::Design
- )
- end
- end
-
- describe '#data_attributes_for' do
- let(:subject) { filter_instance.data_attributes_for(input_text, project, design) }
-
- specify do
- is_expected.to include(issue: design.issue_id,
- original: input_text,
- project: project.id,
- design: design.id)
- end
- end
-
- context 'a design with a quoted filename' do
- let(:filename) { %q{A "very" good file.png} }
- let(:design) { create(:design, :with_versions, issue: issue, filename: filename) }
-
- it 'links to the design' do
- expect(doc.css('a').first.attr('href'))
- .to eq url_for_design(design)
- end
- end
-
- context 'internal reference' do
- it_behaves_like 'a reference containing an element node'
-
- context 'the reference is valid' do
- it_behaves_like 'a good link reference'
-
- context 'the filename needs to be escaped' do
- where(:filename) do
- [
- ['with some spaces.png'],
- ['with <script>console.log("pwded")<%2Fscript>.png']
- ]
- end
-
- with_them do
- let(:design) { create(:design, :with_versions, filename: filename, issue: issue) }
- let(:link) { doc.css('a').first }
-
- it 'replaces the content with the reference, but keeps the link', :aggregate_failures do
- expect(doc.text).to eq(CGI.unescapeHTML("Added #{design.to_reference}"))
- expect(link.attr('title')).to eq(design.filename)
- expect(link.attr('href')).to eq(design_url)
- end
- end
- end
- end
-
- context 'the reference is to a non-existant design' do
- let(:design_url) { url_for_design(build(:design, issue: issue)) }
-
- it_behaves_like 'a no-op filter'
- end
-
- context 'design management is disabled for the referenced project' do
- let(:public_issue) { create(:issue, project: project_with_no_lfs) }
- let(:design) { create(:design, :with_versions, issue: public_issue) }
-
- it_behaves_like 'a no-op filter'
- end
- end
-
- describe 'link pattern' do
- let(:reference) { url_for_design(design) }
-
- it 'matches' do
- expect(reference).to match(DesignManagement::Design.link_reference_pattern)
- end
- end
-
- context 'cross-project / cross-namespace complete reference' do
- let(:design) { design_proj_2 }
-
- it_behaves_like 'a reference containing an element node'
-
- it_behaves_like 'a good link reference'
-
- it 'links to a valid reference' do
- expect(doc.css('a').first.attr('href')).to eq(design_url)
- end
-
- context 'design management is disabled for that project' do
- let(:design) { create(:design, project: project_with_no_lfs) }
-
- it_behaves_like 'a no-op filter'
- end
-
- it 'link has valid text' do
- ref = "#{design.project.full_path}##{design.issue.iid}[#{design.filename}]"
-
- expect(doc.css('a').first.text).to eql(ref)
- end
-
- it 'includes default classes' do
- expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-design has-tooltip'
- end
-
- context 'the reference is invalid' do
- let(:design_url) { url_for_design(design).gsub(/jpg/, 'gif') }
-
- it_behaves_like 'a no-op filter'
- end
- end
-
- describe 'performance' do
- it 'is linear in the number of projects with design management enabled each design refers to' do
- design_c = build(:design, :with_versions, issue: issue)
- design_d = build(:design, :with_versions, issue: issue_b)
- design_e = build(:design, :with_versions, issue: build_stubbed(:issue, project: project_2))
-
- one_ref_per_project = <<~MD
- Design #{url_for_design(design_a)}, #{url_for_design(design_proj_2)}
- MD
-
- multiple_references = <<~MD
- Designs that affect the count:
- * #{url_for_design(design_a)}
- * #{url_for_design(design_b)}
- * #{url_for_design(design_c)}
- * #{url_for_design(design_d)}
- * #{url_for_design(design_proj_2)}
- * #{url_for_design(design_e)}
-
- Things that do not affect the count:
- * #{url_for_design(build_stubbed(:design, project: project_with_no_lfs))}
- * #{url_for_designs(issue)}
- * #1[not a valid reference.gif]
- MD
-
- baseline = ActiveRecord::QueryRecorder.new { process(one_ref_per_project) }
-
- # each project mentioned requires 2 queries:
- #
- # * SELECT "issues".* FROM "issues" WHERE "issues"."project_id" = 1 AND ...
- # :in `parent_records'*/
- # * SELECT "_designs".* FROM "_designs"
- # WHERE (issue_id = ? AND filename = ?) OR ...
- # :in `parent_records'*/
- #
- # In addition there is a 1 query overhead for all the projects at the
- # start. Currently, the baseline for 2 projects is `2 * 2 + 1 = 5` queries
- #
- expect { process(multiple_references) }.not_to exceed_query_limit(baseline.count)
- end
- end
-
- private
-
- def process_doc(text)
- reference_filter(text, project: project)
- end
-
- def baseline(text)
- null_filter(text, project: project)
- end
-
- def process(text)
- process_doc(text).to_html
- end
-end