gitlab-org--gitlab-foss/spec/lib/banzai/reference_parser/base_parser_spec.rb
Adam Buckland ace833b31d Add indication for closed or merged issuables in GFM
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.
2017-04-07 14:31:43 -05:00

293 lines
8 KiB
Ruby

require 'spec_helper'
describe Banzai::ReferenceParser::BaseParser, lib: true do
include ReferenceParserHelpers
let(:user) { create(:user) }
let(:project) { create(:empty_project, :public) }
subject do
klass = Class.new(described_class) do
self.reference_type = :foo
end
klass.new(project, user)
end
describe '.reference_type=' do
it 'sets the reference type' do
dummy = Class.new(described_class)
dummy.reference_type = :foo
expect(dummy.reference_type).to eq(:foo)
end
end
describe '#nodes_visible_to_user' do
let(:link) { empty_html_link }
context 'when the link has a data-project attribute' do
it 'checks if user can read the resource' do
link['data-project'] = project.id.to_s
expect(subject).to receive(:can_read_reference?).with(user, project)
subject.nodes_visible_to_user(user, [link])
end
end
context 'when the link does not have a data-project attribute' do
it 'returns the nodes' do
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
end
end
describe '#nodes_user_can_reference' do
it 'returns the nodes' do
link = double(:link)
expect(subject.nodes_user_can_reference(user, [link])).to eq([link])
end
end
describe '#referenced_by' do
context 'when references_relation is implemented' do
it 'returns a collection of objects' do
links = Nokogiri::HTML.fragment("<a data-foo='#{user.id}'></a>").
children
expect(subject).to receive(:references_relation).and_return(User)
expect(subject.referenced_by(links)).to eq([user])
end
end
context 'when references_relation is not implemented' do
it 'raises NotImplementedError' do
links = Nokogiri::HTML.fragment('<a data-foo="1"></a>').children
expect { subject.referenced_by(links) }.
to raise_error(NotImplementedError)
end
end
end
describe '#references_relation' do
it 'raises NotImplementedError' do
expect { subject.references_relation }.to raise_error(NotImplementedError)
end
end
describe '#gather_attributes_per_project' do
it 'returns a Hash containing attribute values per project' do
link = Nokogiri::HTML.fragment('<a data-project="1" data-foo="2"></a>').
children[0]
hash = subject.gather_attributes_per_project([link], 'data-foo')
expect(hash).to be_an_instance_of(Hash)
expect(hash[1].to_a).to eq(['2'])
end
end
describe '#grouped_objects_for_nodes' do
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.to_s])
hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user')
expect(hash).to eq({ link => user })
end
it 'returns an empty Hash when the list of nodes is empty' do
expect(subject.grouped_objects_for_nodes([], User, 'data-user')).to eq({})
end
end
describe '#unique_attribute_values' do
it 'returns an Array of unique values' do
link = double(:link)
expect(link).to receive(:has_attribute?).
with('data-foo').
twice.
and_return(true)
expect(link).to receive(:attr).
with('data-foo').
twice.
and_return('1')
nodes = [link, link]
expect(subject.unique_attribute_values(nodes, 'data-foo')).to eq(['1'])
end
end
describe '#process' do
it 'gathers the references for every node matching the reference type' do
dummy = Class.new(described_class) do
self.reference_type = :test
end
instance = dummy.new(project, user)
document = Nokogiri::HTML.fragment('<a class="gfm"></a><a class="gfm" data-reference-type="test"></a>')
expect(instance).to receive(:gather_references).
with([document.children[1]]).
and_return([user])
expect(instance.process([document])).to eq([user])
end
end
describe '#gather_references' do
let(:link) { double(:link) }
it 'does not process links a user can not reference' do
expect(subject).to receive(:nodes_user_can_reference).
with(user, [link]).
and_return([])
expect(subject).to receive(:referenced_by).with([])
subject.gather_references([link])
end
it 'does not process links a user can not see' do
expect(subject).to receive(:nodes_user_can_reference).
with(user, [link]).
and_return([link])
expect(subject).to receive(:nodes_visible_to_user).
with(user, [link]).
and_return([])
expect(subject).to receive(:referenced_by).with([])
subject.gather_references([link])
end
it 'returns the references if a user can reference and see a link' do
expect(subject).to receive(:nodes_user_can_reference).
with(user, [link]).
and_return([link])
expect(subject).to receive(:nodes_visible_to_user).
with(user, [link]).
and_return([link])
expect(subject).to receive(:referenced_by).with([link])
subject.gather_references([link])
end
end
describe '#can?' do
it 'delegates the permissions check to the Ability class' do
user = double(:user)
expect(Ability).to receive(:allowed?).
with(user, :read_project, project)
subject.can?(user, :read_project, project)
end
end
describe '#find_projects_for_hash_keys' do
it 'returns a list of Projects' do
expect(subject.find_projects_for_hash_keys(project.id => project)).
to eq([project])
end
end
describe '#collection_objects_for_ids' do
context 'with RequestStore disabled' do
it 'queries the collection directly' do
collection = User.all
expect(collection).to receive(:where).twice.and_call_original
2.times do
expect(subject.collection_objects_for_ids(collection, [user.id])).
to eq([user])
end
end
end
context 'with RequestStore enabled' do
before do
cache = Hash.new { |hash, key| hash[key] = {} }
allow(RequestStore).to receive(:active?).and_return(true)
allow(subject).to receive(:collection_cache).and_return(cache)
end
it 'queries the collection on the first call' do
expect(subject.collection_objects_for_ids(User, [user.id])).
to eq([user])
end
it 'does not query previously queried objects' do
collection = User.all
expect(collection).to receive(:where).once.and_call_original
2.times do
expect(subject.collection_objects_for_ids(collection, [user.id])).
to eq([user])
end
end
it 'casts String based IDs to Fixnums before querying objects' do
2.times do
expect(subject.collection_objects_for_ids(User, [user.id.to_s])).
to eq([user])
end
end
it 'queries any additional objects after the first call' do
other_user = create(:user)
expect(subject.collection_objects_for_ids(User, [user.id])).
to eq([user])
expect(subject.collection_objects_for_ids(User, [user.id, other_user.id])).
to eq([user, other_user])
end
it 'caches objects on a per collection class basis' do
expect(subject.collection_objects_for_ids(User, [user.id])).
to eq([user])
expect(subject.collection_objects_for_ids(Project, [project.id])).
to eq([project])
end
end
end
describe '#collection_cache_key' do
it 'returns the cache key for a Class' do
expect(subject.collection_cache_key(Project)).to eq(Project)
end
it 'returns the cache key for an ActiveRecord::Relation' do
expect(subject.collection_cache_key(Project.all)).to eq(Project)
end
end
end