summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Buckland <adamjbuckland@gmail.com>2016-08-24 18:11:48 +0100
committerDouwe Maan <douwe@selenight.nl>2017-04-07 14:31:43 -0500
commitace833b31dfac64a8b44242ce7d91c60285bf983 (patch)
tree65868bc5cb28f228c09ad5dccb477e5769a39122
parent5d4449151fb576dc927ff1d0ff343fca4645159b (diff)
downloadgitlab-ce-adam-finish-5993-closed-issuable.tar.gz
Add indication for closed or merged issuables in GFMadam-finish-5993-closed-issuable
Example: for issues that are closed, the links will now show '[closed]' following the issue number. This is done as post-process after the markdown has been loaded from the cache as the status of the issue may change between the cache being populated and the content being displayed. In order to avoid N+1 queries problem when rendering notes ObjectRenderer populates the cache of referenced issuables for all notes at once, before the post processing phase. As a part of this change, the Banzai BaseParser#grouped_objects_for_nodes method has been refactored to return a Hash utilising the node itself as the key, since this was a common pattern of usage for this method.
-rw-r--r--changelogs/unreleased/adam-finish-5993-closed-issuable.yml4
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb35
-rw-r--r--lib/banzai/filter/redactor_filter.rb2
-rw-r--r--lib/banzai/issuable_extractor.rb36
-rw-r--r--lib/banzai/object_renderer.rb46
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb1
-rw-r--r--lib/banzai/reference_parser/base_parser.rb20
-rw-r--r--lib/banzai/reference_parser/issue_parser.rb10
-rw-r--r--lib/banzai/reference_parser/merge_request_parser.rb8
-rw-r--r--lib/banzai/reference_parser/user_parser.rb6
-rw-r--r--spec/factories/merge_requests.rb4
-rw-r--r--spec/lib/banzai/filter/issuable_state_filter_spec.rb95
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb10
-rw-r--r--spec/lib/banzai/issuable_extractor_spec.rb52
-rw-r--r--spec/lib/banzai/object_renderer_spec.rb139
-rw-r--r--spec/lib/banzai/reference_parser/base_parser_spec.rb18
-rw-r--r--spec/lib/banzai/reference_parser/issue_parser_spec.rb12
-rw-r--r--spec/lib/banzai/reference_parser/user_parser_spec.rb9
18 files changed, 354 insertions, 153 deletions
diff --git a/changelogs/unreleased/adam-finish-5993-closed-issuable.yml b/changelogs/unreleased/adam-finish-5993-closed-issuable.yml
new file mode 100644
index 00000000000..b324566313f
--- /dev/null
+++ b/changelogs/unreleased/adam-finish-5993-closed-issuable.yml
@@ -0,0 +1,4 @@
+---
+title: Add indication for closed or merged issuables in GFM
+merge_request: 9462
+author: Adam Buckland
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
new file mode 100644
index 00000000000..6b78aa795b4
--- /dev/null
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -0,0 +1,35 @@
+module Banzai
+ module Filter
+ # HTML filter that appends state information to issuable links.
+ # Runs as a post-process filter as issuable state might change whilst
+ # Markdown is in the cache.
+ #
+ # This filter supports cross-project references.
+ class IssuableStateFilter < HTML::Pipeline::Filter
+ VISIBLE_STATES = %w(closed merged).freeze
+
+ def call
+ extractor = Banzai::IssuableExtractor.new(project, current_user)
+ issuables = extractor.extract([doc])
+
+ issuables.each do |node, issuable|
+ if VISIBLE_STATES.include?(issuable.state)
+ node.children.last.content += " [#{issuable.state}]"
+ end
+ end
+
+ doc
+ end
+
+ private
+
+ def current_user
+ context[:current_user]
+ end
+
+ def project
+ context[:project]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index c59a80dd1c7..9f9882b3b40 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -7,7 +7,7 @@ module Banzai
#
class RedactorFilter < HTML::Pipeline::Filter
def call
- Redactor.new(project, current_user).redact([doc])
+ Redactor.new(project, current_user).redact([doc]) unless context[:skip_redaction]
doc
end
diff --git a/lib/banzai/issuable_extractor.rb b/lib/banzai/issuable_extractor.rb
new file mode 100644
index 00000000000..c5ce360e172
--- /dev/null
+++ b/lib/banzai/issuable_extractor.rb
@@ -0,0 +1,36 @@
+module Banzai
+ # Extract references to issuables from multiple documents
+
+ # This populates RequestStore cache used in Banzai::ReferenceParser::IssueParser
+ # and Banzai::ReferenceParser::MergeRequestParser
+ # Populating the cache should happen before processing documents one-by-one
+ # so we can avoid N+1 queries problem
+
+ class IssuableExtractor
+ QUERY = %q(
+ descendant-or-self::a[contains(concat(" ", @class, " "), " gfm ")]
+ [@data-reference-type="issue" or @data-reference-type="merge_request"]
+ ).freeze
+
+ attr_reader :project, :user
+
+ def initialize(project, user)
+ @project = project
+ @user = user
+ end
+
+ # Returns Hash in the form { node => issuable_instance }
+ def extract(documents)
+ nodes = documents.flat_map do |document|
+ document.xpath(QUERY)
+ end
+
+ issue_parser = Banzai::ReferenceParser::IssueParser.new(project, user)
+ merge_request_parser = Banzai::ReferenceParser::MergeRequestParser.new(project, user)
+
+ issue_parser.issues_for_nodes(nodes).merge(
+ merge_request_parser.merge_requests_for_nodes(nodes)
+ )
+ end
+ end
+end
diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb
index 9f8eb0931b8..002a3341ccd 100644
--- a/lib/banzai/object_renderer.rb
+++ b/lib/banzai/object_renderer.rb
@@ -31,7 +31,8 @@ module Banzai
#
# Returns the same input objects.
def render(objects, attribute)
- documents = render_objects(objects, attribute)
+ documents = render_documents(objects, attribute)
+ documents = post_process_documents(documents, objects, attribute)
redacted = redact_documents(documents)
objects.each_with_index do |object, index|
@@ -41,9 +42,24 @@ module Banzai
end
end
- # Renders the attribute of every given object.
- def render_objects(objects, attribute)
- render_attributes(objects, attribute)
+ private
+
+ def render_documents(objects, attribute)
+ pipeline = HTML::Pipeline.new([])
+
+ objects.map do |object|
+ pipeline.to_document(Banzai.render_field(object, attribute))
+ end
+ end
+
+ def post_process_documents(documents, objects, attribute)
+ # Called here to populate cache, refer to IssuableExtractor docs
+ IssuableExtractor.new(project, user).extract(documents)
+
+ documents.zip(objects).map do |document, object|
+ context = context_for(object, attribute)
+ Banzai::Pipeline[:post_process].to_document(document, context)
+ end
end
# Redacts the list of documents.
@@ -57,25 +73,15 @@ module Banzai
# Returns a Banzai context for the given object and attribute.
def context_for(object, attribute)
- context = base_context.dup
- context = context.merge(object.banzai_render_context(attribute))
- context
- end
-
- # Renders the attributes of a set of objects.
- #
- # Returns an Array of `Nokogiri::HTML::Document`.
- def render_attributes(objects, attribute)
- objects.map do |object|
- string = Banzai.render_field(object, attribute)
- context = context_for(object, attribute)
-
- Banzai::Pipeline[:relative_link].to_document(string, context)
- end
+ base_context.merge(object.banzai_render_context(attribute))
end
def base_context
- @base_context ||= @redaction_context.merge(current_user: user, project: project)
+ @base_context ||= @redaction_context.merge(
+ current_user: user,
+ project: project,
+ skip_redaction: true
+ )
end
end
end
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
index ecff094b1e5..131ac3b0eec 100644
--- a/lib/banzai/pipeline/post_process_pipeline.rb
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -4,6 +4,7 @@ module Banzai
def self.filters
FilterArray[
Filter::RelativeLinkFilter,
+ Filter::IssuableStateFilter,
Filter::RedactorFilter
]
end
diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb
index 52fdb9a2140..dabf71d6aeb 100644
--- a/lib/banzai/reference_parser/base_parser.rb
+++ b/lib/banzai/reference_parser/base_parser.rb
@@ -62,8 +62,7 @@ module Banzai
nodes.select do |node|
if node.has_attribute?(project_attr)
- node_id = node.attr(project_attr).to_i
- can_read_reference?(user, projects[node_id])
+ can_read_reference?(user, projects[node])
else
true
end
@@ -112,12 +111,12 @@ module Banzai
per_project
end
- # Returns a Hash containing objects for an attribute grouped per their
- # IDs.
+ # Returns a Hash containing objects for an attribute grouped per the
+ # nodes that reference them.
#
# The returned Hash uses the following format:
#
- # { id value => row }
+ # { node => row }
#
# nodes - An Array of HTML nodes to process.
#
@@ -132,9 +131,14 @@ module Banzai
return {} if nodes.empty?
ids = unique_attribute_values(nodes, attribute)
- rows = collection_objects_for_ids(collection, ids)
+ collection_objects = collection_objects_for_ids(collection, ids)
+ objects_by_id = collection_objects.index_by(&:id)
- rows.index_by(&:id)
+ nodes.each_with_object({}) do |node, hash|
+ if node.has_attribute?(attribute)
+ hash[node] = objects_by_id[node.attr(attribute).to_i]
+ end
+ end
end
# Returns an Array containing all unique values of an attribute of the
@@ -201,7 +205,7 @@ module Banzai
#
# The returned Hash uses the following format:
#
- # { project ID => project }
+ # { node => project }
#
def projects_for_nodes(nodes)
@projects_for_nodes ||=
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index 6c20dec5734..e02b360924a 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -13,14 +13,14 @@ module Banzai
issues_readable_by_user(issues.values, user).to_set
nodes.select do |node|
- readable_issues.include?(issue_for_node(issues, node))
+ readable_issues.include?(issues[node])
end
end
def referenced_by(nodes)
issues = issues_for_nodes(nodes)
- nodes.map { |node| issue_for_node(issues, node) }.uniq
+ nodes.map { |node| issues[node] }.compact.uniq
end
def issues_for_nodes(nodes)
@@ -44,12 +44,6 @@ module Banzai
self.class.data_attribute
)
end
-
- private
-
- def issue_for_node(issues, node)
- issues[node.attr(self.class.data_attribute).to_i]
- end
end
end
end
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
index 40451947e6c..7d7dcce017e 100644
--- a/lib/banzai/reference_parser/merge_request_parser.rb
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -3,6 +3,14 @@ module Banzai
class MergeRequestParser < BaseParser
self.reference_type = :merge_request
+ def merge_requests_for_nodes(nodes)
+ @merge_requests_for_nodes ||= grouped_objects_for_nodes(
+ nodes,
+ MergeRequest.all,
+ self.class.data_attribute
+ )
+ end
+
def references_relation
MergeRequest.includes(:author, :assignee, :target_project)
end
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
index 7adaffa19c1..09b66cbd8fb 100644
--- a/lib/banzai/reference_parser/user_parser.rb
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -49,7 +49,7 @@ module Banzai
# Check if project belongs to a group which
# user can read.
def can_read_group_reference?(node, user, groups)
- node_group = groups[node.attr('data-group').to_i]
+ node_group = groups[node]
node_group && can?(user, :read_group, node_group)
end
@@ -74,8 +74,8 @@ module Banzai
if project && project_id && project.id == project_id.to_i
true
elsif project_id && user_id
- project = projects[project_id.to_i]
- user = users[user_id.to_i]
+ project = projects[node]
+ user = users[node]
project && user ? project.team.member?(user) : false
else
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index e36fe326e1c..361f9dac191 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -44,6 +44,10 @@ FactoryGirl.define do
state :reopened
end
+ trait :locked do
+ state :locked
+ end
+
trait :simple do
source_branch "feature"
target_branch "master"
diff --git a/spec/lib/banzai/filter/issuable_state_filter_spec.rb b/spec/lib/banzai/filter/issuable_state_filter_spec.rb
new file mode 100644
index 00000000000..603b79a323c
--- /dev/null
+++ b/spec/lib/banzai/filter/issuable_state_filter_spec.rb
@@ -0,0 +1,95 @@
+require 'spec_helper'
+
+describe Banzai::Filter::IssuableStateFilter, lib: true do
+ include ActionView::Helpers::UrlHelper
+ include FilterSpecHelper
+
+ let(:user) { create(:user) }
+
+ def create_link(data)
+ link_to('text', '', class: 'gfm has-tooltip', data: data)
+ end
+
+ it 'ignores non-GFM links' do
+ html = %(See <a href="https://google.com/">Google</a>)
+ doc = filter(html, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('Google')
+ end
+
+ it 'ignores non-issuable links' do
+ project = create(:empty_project, :public)
+ link = create_link(project: project, reference_type: 'issue')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text')
+ end
+
+ context 'for issue references' do
+ it 'ignores open issue references' do
+ issue = create(:issue)
+ link = create_link(issue: issue.id, reference_type: 'issue')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text')
+ end
+
+ it 'ignores reopened issue references' do
+ reopened_issue = create(:issue, :reopened)
+ link = create_link(issue: reopened_issue.id, reference_type: 'issue')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text')
+ end
+
+ it 'appends [closed] to closed issue references' do
+ closed_issue = create(:issue, :closed)
+ link = create_link(issue: closed_issue.id, reference_type: 'issue')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text [closed]')
+ end
+ end
+
+ context 'for merge request references' do
+ it 'ignores open merge request references' do
+ mr = create(:merge_request)
+ link = create_link(merge_request: mr.id, reference_type: 'merge_request')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text')
+ end
+
+ it 'ignores reopened merge request references' do
+ mr = create(:merge_request, :reopened)
+ link = create_link(merge_request: mr.id, reference_type: 'merge_request')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text')
+ end
+
+ it 'ignores locked merge request references' do
+ mr = create(:merge_request, :locked)
+ link = create_link(merge_request: mr.id, reference_type: 'merge_request')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text')
+ end
+
+ it 'appends [closed] to closed merge request references' do
+ mr = create(:merge_request, :closed)
+ link = create_link(merge_request: mr.id, reference_type: 'merge_request')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text [closed]')
+ end
+
+ it 'appends [merged] to merged merge request references' do
+ mr = create(:merge_request, :merged)
+ link = create_link(merge_request: mr.id, reference_type: 'merge_request')
+ doc = filter(link, current_user: user)
+
+ expect(doc.css('a').last.text).to eq('text [merged]')
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 0140a91c7ba..8a6fe1ad6a3 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -15,6 +15,16 @@ describe Banzai::Filter::RedactorFilter, lib: true do
link_to('text', '', class: 'gfm', data: data)
end
+ it 'skips when the skip_redaction flag is set' do
+ user = create(:user)
+ project = create(:empty_project)
+
+ link = reference_link(project: project.id, reference_type: 'test')
+ doc = filter(link, current_user: user, skip_redaction: true)
+
+ expect(doc.css('a').length).to eq 1
+ end
+
context 'with data-project' do
let(:parser_class) do
Class.new(Banzai::ReferenceParser::BaseParser) do
diff --git a/spec/lib/banzai/issuable_extractor_spec.rb b/spec/lib/banzai/issuable_extractor_spec.rb
new file mode 100644
index 00000000000..e5d332efb08
--- /dev/null
+++ b/spec/lib/banzai/issuable_extractor_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Banzai::IssuableExtractor, lib: true do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:extractor) { described_class.new(project, user) }
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:issue_link) do
+ html_to_node(
+ "<a href='' data-issue='#{issue.id}' data-reference-type='issue' class='gfm'>text</a>"
+ )
+ end
+ let(:merge_request_link) do
+ html_to_node(
+ "<a href='' data-merge-request='#{merge_request.id}' data-reference-type='merge_request' class='gfm'>text</a>"
+ )
+ end
+
+ def html_to_node(html)
+ Nokogiri::HTML.fragment(
+ html
+ ).children[0]
+ end
+
+ it 'returns instances of issuables for nodes with references' do
+ result = extractor.extract([issue_link, merge_request_link])
+
+ expect(result).to eq(issue_link => issue, merge_request_link => merge_request)
+ end
+
+ describe 'caching' do
+ before do
+ RequestStore.begin!
+ end
+
+ after do
+ RequestStore.end!
+ RequestStore.clear!
+ end
+
+ it 'saves records to cache' do
+ extractor.extract([issue_link, merge_request_link])
+
+ second_call_queries = ActiveRecord::QueryRecorder.new do
+ extractor.extract([issue_link, merge_request_link])
+ end.count
+
+ expect(second_call_queries).to eq 0
+ end
+ end
+end
diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb
index 6bcda87c999..4817fcd031a 100644
--- a/spec/lib/banzai/object_renderer_spec.rb
+++ b/spec/lib/banzai/object_renderer_spec.rb
@@ -3,128 +3,51 @@ require 'spec_helper'
describe Banzai::ObjectRenderer do
let(:project) { create(:empty_project) }
let(:user) { project.owner }
-
- def fake_object(attrs = {})
- object = double(attrs.merge("new_record?" => true, "destroyed?" => true))
- allow(object).to receive(:markdown_cache_field_for).with(:note).and_return(:note_html)
- allow(object).to receive(:banzai_render_context).with(:note).and_return(project: nil, author: nil)
- allow(object).to receive(:update_column).with(:note_html, anything).and_return(true)
- object
- end
+ let(:renderer) { described_class.new(project, user, custom_value: 'value') }
+ let(:object) { Note.new(note: 'hello', note_html: '<p>hello</p>') }
describe '#render' do
it 'renders and redacts an Array of objects' do
- renderer = described_class.new(project, user)
- object = fake_object(note: 'hello', note_html: nil)
-
- expect(renderer).to receive(:render_objects).with([object], :note).
- and_call_original
-
- expect(renderer).to receive(:redact_documents).
- with(an_instance_of(Array)).
- and_call_original
-
- expect(object).to receive(:redacted_note_html=).with('<p dir="auto">hello</p>')
- expect(object).to receive(:user_visible_reference_count=).with(0)
-
renderer.render([object], :note)
- end
- end
-
- describe '#render_objects' do
- it 'renders an Array of objects' do
- object = fake_object(note: 'hello', note_html: nil)
-
- renderer = described_class.new(project, user)
- expect(renderer).to receive(:render_attributes).with([object], :note).
- and_call_original
-
- rendered = renderer.render_objects([object], :note)
-
- expect(rendered).to be_an_instance_of(Array)
- expect(rendered[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
- end
- end
-
- describe '#redact_documents' do
- it 'redacts a set of documents and returns them as an Array of Hashes' do
- doc = Nokogiri::HTML.fragment('<p>hello</p>')
- renderer = described_class.new(project, user)
-
- expect_any_instance_of(Banzai::Redactor).to receive(:redact).
- with([doc]).
- and_call_original
-
- redacted = renderer.redact_documents([doc])
-
- expect(redacted.count).to eq(1)
- expect(redacted.first[:visible_reference_count]).to eq(0)
- expect(redacted.first[:document].to_html).to eq('<p>hello</p>')
+ expect(object.redacted_note_html).to eq '<p>hello</p>'
+ expect(object.user_visible_reference_count).to eq 0
end
- end
- describe '#context_for' do
- let(:object) { fake_object(note: 'hello') }
- let(:renderer) { described_class.new(project, user) }
+ it 'calls Banzai::Redactor to perform redaction' do
+ expect_any_instance_of(Banzai::Redactor).to receive(:redact).and_call_original
- it 'returns a Hash' do
- expect(renderer.context_for(object, :note)).to be_an_instance_of(Hash)
- end
-
- it 'includes the banzai render context for the object' do
- expect(object).to receive(:banzai_render_context).with(:note).and_return(foo: :bar)
- context = renderer.context_for(object, :note)
- expect(context).to have_key(:foo)
- expect(context[:foo]).to eq(:bar)
- end
- end
-
- describe '#render_attributes' do
- it 'renders the attribute of a list of objects' do
- objects = [fake_object(note: 'hello', note_html: nil), fake_object(note: 'bye', note_html: nil)]
- renderer = described_class.new(project, user)
-
- objects.each do |object|
- expect(Banzai).to receive(:render_field).with(object, :note).and_call_original
- end
-
- docs = renderer.render_attributes(objects, :note)
-
- expect(docs[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
- expect(docs[0].to_html).to eq('<p dir="auto">hello</p>')
-
- expect(docs[1]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment)
- expect(docs[1].to_html).to eq('<p dir="auto">bye</p>')
- end
-
- it 'returns when no objects to render' do
- objects = []
- renderer = described_class.new(project, user, pipeline: :note)
-
- expect(renderer.render_attributes(objects, :note)).to eq([])
+ renderer.render([object], :note)
end
- end
- describe '#base_context' do
- let(:context) do
- described_class.new(project, user, foo: :bar).base_context
- end
+ it 'retrieves field content using Banzai.render_field' do
+ expect(Banzai).to receive(:render_field).with(object, :note).and_call_original
- it 'returns a Hash' do
- expect(context).to be_an_instance_of(Hash)
- end
-
- it 'includes the custom attributes' do
- expect(context[:foo]).to eq(:bar)
+ renderer.render([object], :note)
end
- it 'includes the current user' do
- expect(context[:current_user]).to eq(user)
- end
+ it 'passes context to PostProcessPipeline' do
+ another_user = create(:user)
+ another_project = create(:empty_project)
+ object = Note.new(
+ note: 'hello',
+ note_html: 'hello',
+ author: another_user,
+ project: another_project
+ )
+
+ expect(Banzai::Pipeline::PostProcessPipeline).to receive(:to_document).with(
+ anything,
+ hash_including(
+ skip_redaction: true,
+ current_user: user,
+ project: another_project,
+ author: another_user,
+ custom_value: 'value'
+ )
+ ).and_call_original
- it 'includes the current project' do
- expect(context[:project]).to eq(project)
+ renderer.render([object], :note)
end
end
end
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index aa127f0179d..a3141894c74 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -92,16 +92,26 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
end
describe '#grouped_objects_for_nodes' do
- it 'returns a Hash grouping objects per ID' do
- nodes = [double(:node)]
+ it 'returns a Hash grouping objects per node' do
+ link = double(:link)
+
+ expect(link).to receive(:has_attribute?).
+ with('data-user').
+ and_return(true)
+
+ expect(link).to receive(:attr).
+ with('data-user').
+ and_return(user.id.to_s)
+
+ nodes = [link]
expect(subject).to receive(:unique_attribute_values).
with(nodes, 'data-user').
- and_return([user.id])
+ and_return([user.id.to_s])
hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user')
- expect(hash).to eq({ user.id => user })
+ expect(hash).to eq({ link => user })
end
it 'returns an empty Hash when the list of nodes is empty' do
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index 6873b7b85f9..7031c47231c 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -67,6 +67,16 @@ describe Banzai::ReferenceParser::IssueParser, lib: true do
expect(subject.referenced_by([])).to eq([])
end
end
+
+ context 'when issue with given ID does not exist' do
+ before do
+ link['data-issue'] = '-1'
+ end
+
+ it 'returns an empty Array' do
+ expect(subject.referenced_by([link])).to eq([])
+ end
+ end
end
end
@@ -75,7 +85,7 @@ describe Banzai::ReferenceParser::IssueParser, lib: true do
link['data-issue'] = issue.id.to_s
nodes = [link]
- expect(subject.issues_for_nodes(nodes)).to eq({ issue.id => issue })
+ expect(subject.issues_for_nodes(nodes)).to eq({ link => issue })
end
end
end
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index 31ca9d27b0b..4ec998efe53 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -180,6 +180,15 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
expect(subject.nodes_user_can_reference(user, [link])).to eq([])
end
+
+ it 'returns the nodes if the project attribute value equals the current project ID' do
+ other_user = create(:user)
+
+ link['data-project'] = project.id.to_s
+ link['data-author'] = other_user.id.to_s
+
+ expect(subject.nodes_user_can_reference(user, [link])).to eq([link])
+ end
end
context 'when the link does not have a data-author attribute' do