更新
更旧
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BlobPresenter do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
let(:repository) { project.repository }
let(:blob) { repository.blob_at(ref, path) }
let(:ref) { 'HEAD' }
let(:path) { 'files/ruby/regex.rb' }
subject(:presenter) { described_class.new(blob, current_user: user) }
it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{ref}/#{path}") }
it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{ref}/#{path}") }
it { expect(presenter.edit_blob_path).to eq("/#{project.full_path}/-/edit/#{ref}/#{path}") }
it { expect(presenter.raw_path).to eq("/#{project.full_path}/-/raw/#{ref}/#{path}") }
it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/update/#{ref}/#{path}") }
end
shared_examples_for '#can_current_user_push_to_branch?' do
let(:branch_exists) { true }
before do
allow(project.repository).to receive(:branch_exists?).with(blob.commit_id).and_return(branch_exists)
end
it { expect(presenter.can_current_user_push_to_branch?).to eq(true) }
context 'current_user is nil' do
let(:user) { nil }
it { expect(presenter.can_current_user_push_to_branch?).to eq(false) }
end
context 'branch does not exist' do
let(:branch_exists) { false }
it { expect(presenter.can_current_user_push_to_branch?).to eq(false) }
end
%w[heads tags].each do |ref_type|
context "when ref_type is #{ref_type}" do
before do
blob.ref_type = ref_type
end
describe '#web_url' do
it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{ref}/#{path}?ref_type=#{ref_type}") }
end
describe '#web_path' do
it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{ref}/#{path}?ref_type=#{ref_type}") }
end
describe '#edit_blob_path' do
it { expect(presenter.edit_blob_path).to eq("/#{project.full_path}/-/edit/#{ref}/#{path}?ref_type=#{ref_type}") }
end
describe '#raw_path' do
it { expect(presenter.raw_path).to eq("/#{project.full_path}/-/raw/#{ref}/#{path}?ref_type=#{ref_type}") }
end
describe '#replace_path' do
it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/update/#{ref}/#{path}?ref_type=#{ref_type}") }
end
describe '#history_path' do
it { expect(presenter.history_path).to eq("/#{project.full_path}/-/commits/#{ref}/#{path}?ref_type=#{ref_type}") }
end
describe '#blame_path' do
it { expect(presenter.blame_path).to eq("/#{project.full_path}/-/blame/#{ref}/#{path}?ref_type=#{ref_type}") }
end
it_behaves_like '#can_current_user_push_to_branch?'
end
end
describe '#can_modify_blob?' do
context 'when blob is store externally' do
before do
allow(blob).to receive(:stored_externally?).and_return(true)
end
it { expect(presenter.can_modify_blob?).to be_falsey }
context 'when the user cannot edit the tree' do
before do
allow(presenter).to receive(:can_edit_tree?).with(project, ref).and_return(false)
end
it { expect(presenter.can_modify_blob?).to be_falsey }
end
context 'when ref is a branch' do
let(:ref) { 'feature' }
it { expect(presenter.can_modify_blob?).to be_truthy }
end
describe '#can_modify_blob_with_web_ide?' do
before do
allow(blob).to receive(:stored_externally?).and_return(false)
allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(false)
end
it { expect(presenter.can_modify_blob_with_web_ide?).to be_falsey }
context 'when blob is stored externally' do
before do
allow(blob).to receive(:stored_externally?).and_return(true)
end
it { expect(presenter.can_modify_blob_with_web_ide?).to be_falsey }
end
context 'when user can collaborate with the project' do
before do
allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(true)
end
it { expect(presenter.can_modify_blob_with_web_ide?).to be_truthy }
end
describe '#can_current_user_push_to_branch?' do
context 'when ref is a branch' do
let(:ref) { 'feature' }
it 'delegates to UserAccess' do
allow_next_instance_of(Gitlab::UserAccess) do |instance|
expect(instance).to receive(:can_push_to_branch?).with(ref).and_call_original
end
expect(presenter.can_current_user_push_to_branch?).to be_truthy
end
it_behaves_like '#can_current_user_push_to_branch?'
it { expect(presenter.can_current_user_push_to_branch?).to be_falsey }
describe '#archived?' do
it { expect(presenter.archived?).to eq(project.archived) }
end
describe '#pipeline_editor_path' do
context 'when blob is .gitlab-ci.yml' do
project.repository.create_file(
user,
'.gitlab-ci.yml',
'',
message: 'Add a ci file',
branch_name: 'main'
)
let(:ref) { 'main' }
let(:path) { '.gitlab-ci.yml' }
it { expect(presenter.pipeline_editor_path).to eq("/#{project.full_path}/-/ci/editor?branch_name=#{ref}") }
context 'when ref includes the qualifier' do
let(:ref) { 'refs/heads/main' }
it 'returns path to unqualified ref' do
allow(blob).to receive(:ref_type).and_return('heads')
expect(presenter.pipeline_editor_path).to eq("/#{project.full_path}/-/ci/editor?branch_name=main")
end
end
context 'Gitpod' do
let(:gitpod_url) { "https://gitpod.io" }
let(:gitpod_application_enabled) { true }
let(:gitpod_user_enabled) { true }
before do
allow(user).to receive(:gitpod_enabled).and_return(gitpod_user_enabled)
allow(Gitlab::CurrentSettings).to receive(:gitpod_enabled).and_return(gitpod_application_enabled)
allow(Gitlab::CurrentSettings).to receive(:gitpod_url).and_return(gitpod_url)
end
context 'Gitpod enabled for application and user' do
describe '#gitpod_blob_url' do
it { expect(presenter.gitpod_blob_url).to eq("#{gitpod_url}##{"http://localhost/#{project.full_path}/-/tree/#{ref}/#{path}"}") }
end
end
context 'Gitpod disabled at application level' do
let(:gitpod_application_enabled) { false }
describe '#gitpod_blob_url' do
it { expect(presenter.gitpod_blob_url).to eq(nil) }
end
end
context 'Gitpod disabled at user level' do
let(:gitpod_user_enabled) { false }
describe '#gitpod_blob_url' do
it { expect(presenter.gitpod_blob_url).to eq(nil) }
end
end
end
describe '#find_file_path' do
it { expect(presenter.find_file_path).to eq("/#{project.full_path}/-/find_file/HEAD") }
end
describe '#blame_path' do
it { expect(presenter.blame_path).to eq("/#{project.full_path}/-/blame/HEAD/files/ruby/regex.rb") }
end
describe '#history_path' do
it { expect(presenter.history_path).to eq("/#{project.full_path}/-/commits/HEAD/files/ruby/regex.rb") }
end
describe '#permalink_path' do
it { expect(presenter.permalink_path).to eq("/#{project.full_path}/-/blob/#{project.repository.commit(blob.commit_id).sha}/files/ruby/regex.rb") }
context 'environment has been deployed' do
let(:external_url) { "https://some.environment" }
let(:environment) { create(:environment, project: project, external_url: external_url) }
let!(:deployment) { create(:deployment, :success, environment: environment, project: project, sha: blob.commit_id) }
before do
allow(project).to receive(:public_path_for_source_path).with(path, blob.commit_id).and_return(path)
end
describe '#environment_formatted_external_url' do
it { expect(presenter.environment_formatted_external_url).to eq("some.environment") }
end
describe '#environment_external_url_for_route_map' do
it { expect(presenter.environment_external_url_for_route_map).to eq("#{external_url}/#{path}") }
end
describe 'chooses the latest deployed environment for #environment_formatted_external_url and #environment_external_url_for_route_map' do
let(:another_external_url) { "https://another.environment" }
let(:another_environment) { create(:environment, project: project, external_url: another_external_url) }
let!(:another_deployment) { create(:deployment, :success, environment: another_environment, project: project, sha: blob.commit_id) }
it { expect(presenter.environment_formatted_external_url).to eq("another.environment") }
it { expect(presenter.environment_external_url_for_route_map).to eq("#{another_external_url}/#{path}") }
it { expect(presenter.code_owners).to be_empty }
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
describe '#ide_edit_path' do
it { expect(presenter.ide_edit_path).to eq("/-/ide/project/#{project.full_path}/edit/HEAD/-/files/ruby/regex.rb") }
end
describe '#fork_and_edit_path' do
it 'generates expected URI + query' do
uri = URI.parse(presenter.fork_and_edit_path)
query = Rack::Utils.parse_query(uri.query)
expect(uri.path).to eq("/#{project.full_path}/-/forks")
expect(query).to include('continue[to]' => presenter.edit_blob_path, 'namespace_key' => user.namespace_id.to_s)
end
context 'current_user is nil' do
let(:user) { nil }
it { expect(presenter.fork_and_edit_path).to be_nil }
end
end
describe '#ide_fork_and_edit_path' do
it 'generates expected URI + query' do
uri = URI.parse(presenter.ide_fork_and_edit_path)
query = Rack::Utils.parse_query(uri.query)
expect(uri.path).to eq("/#{project.full_path}/-/forks")
expect(query).to include('continue[to]' => presenter.ide_edit_path, 'namespace_key' => user.namespace_id.to_s)
end
context 'current_user is nil' do
let(:user) { nil }
it { expect(presenter.ide_fork_and_edit_path).to be_nil }
end
end
let(:code_navigation_path) { Gitlab::CodeNavigationPath.new(project, blob.commit_id).full_json_path_for(path) }
it { expect(presenter.code_navigation_path).to eq(code_navigation_path) }
end
describe '#project_blob_path_root' do
it { expect(presenter.project_blob_path_root).to eq("/#{project.full_path}/-/blob/HEAD") }
end
context 'given a Gitlab::Graphql::Representation::TreeEntry' do
let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(super(), repository) }
it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{ref}/#{path}") }
it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{ref}/#{path}") }
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby', used_on: :blob)
it 'returns plain content when :plain is true' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: true, language: 'ruby', used_on: :blob)
context '"to" param is present' do
before do
allow(git_blob)
.to receive(:data)
.and_return("line one\nline two\nline 3")
end
it 'returns limited highlighted content' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', "line one\n", plain: nil, language: 'ruby', used_on: :blob)
context 'gitlab-language contains a match' do
before do
allow(blob).to receive(:language_from_gitattributes).and_return('ruby')
end
it 'passes language to inner call' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby', used_on: :blob)
context 'when used_on param is present' do
it 'returns highlighted content' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby', used_on: :diff)
presenter.highlight(used_on: :diff)
end
end
describe '#highlight_and_trim' do
let(:git_blob) { blob.__getobj__ }
it 'returns trimmed content for longer line' do
trimmed_lines = git_blob.data.split("\n").map { |line| line[0, 55] }.join("\n")
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', "#{trimmed_lines}\n", plain: nil, language: 'ruby', context: { ellipsis_svg: "svg_icon", ellipsis_indexes: [21, 26, 49] })
presenter.highlight_and_trim(ellipsis_svg: "svg_icon", trim_length: 55)
end
end
describe '#blob_language' do
subject { presenter.blob_language }
it { is_expected.to eq('ruby') }
context 'gitlab-language contains a match' do
before do
allow(blob).to receive(:language_from_gitattributes).and_return('cpp')
end
it { is_expected.to eq('cpp') }
end
context 'when blob is binary' do
let(:blob) { repository.blob_at('HEAD', 'Gemfile.zip') }
it { is_expected.to be_nil }
end
end
let(:blob) { repository.blob_at('HEAD', file) }
let(:file) { 'files/ruby/popen.rb' }
it 'does not include html in the content' do
expect(presenter.base64_encoded_blob.include?('</span>')).to be_falsey
end
it 'encodes the raw blob base 64' do
expect(presenter.base64_encoded_blob).to include("cmVxdWlyZSAnZmlsZXV0")
expect(presenter.base64_encoded_blob).to include("R1cwogIGVuZAplbmQK\n")
let(:blob) { repository.blob_at('HEAD', file) }
context 'when blob is text' do
let(:file) { 'files/ruby/popen.rb' }
it 'does not include html in the content' do
expect(presenter.raw_plain_data.include?('</span>')).to be_falsey
end
end
end
describe '#plain_data' do
let(:blob) { repository.blob_at('HEAD', file) }
context 'when blob is binary' do
let(:file) { 'files/images/logo-black.png' }
it 'returns nil' do
end
end
context 'when blob is markup' do
let(:file) { 'README.md' }
it 'returns plain content' do
expect(presenter.plain_data).to include('<span id="LC1" class="line" lang="markdown">')
end
end
context 'when blob has syntax' do
let(:file) { 'files/ruby/regex.rb' }
it 'returns highlighted syntax content' do
.to include '<span id="LC1" class="line" lang="ruby"><span class="k">module</span> <span class="nn">Gitlab</span>'
end
end
context 'when blob has plain data' do
let(:file) { 'LICENSE' }
it 'returns plain text highlighted content' do
expect(presenter.plain_data).to include('<span id="LC1" class="line" lang="plaintext">The MIT License (MIT)</span>')