From 57129140ba1160fd3d61588708d98602c421ee97 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 26 Apr 2018 01:18:22 +0200 Subject: backport changes from gitlab-org/gitlab-ee!5461 --- .../services/projects/housekeeping_service_spec.rb | 72 ++++++++++++---------- 1 file changed, 41 insertions(+), 31 deletions(-) (limited to 'spec') diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index b7b5de07380..1cf373d1d72 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Projects::HousekeepingService do subject { described_class.new(project) } - let(:project) { create(:project, :repository) } + set(:project) { create(:project, :repository) } before do project.reset_pushes_since_gc @@ -16,12 +16,12 @@ describe Projects::HousekeepingService do it 'enqueues a sidekiq job' do expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid) expect(subject).to receive(:lease_key).and_return(:the_lease_key) - expect(subject).to receive(:task).and_return(:the_task) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :the_task, :the_lease_key, :the_uuid) + expect(subject).to receive(:task).and_return(:incremental_repack) + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid).and_call_original - subject.execute - - expect(project.reload.pushes_since_gc).to eq(0) + Sidekiq::Testing.fake! do + expect { subject.execute }.to change(GitGarbageCollectWorker.jobs, :size).by(1) + end end it 'yields the block if given' do @@ -30,6 +30,16 @@ describe Projects::HousekeepingService do end.to yield_with_no_args end + it 'resets counter after execution' do + expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid) + allow(subject).to receive(:gc_period).and_return(1) + project.increment_pushes_since_gc + + Sidekiq::Testing.inline! do + expect { subject.execute }.to change { project.pushes_since_gc }.to(0) + end + end + context 'when no lease can be obtained' do before do expect(subject).to receive(:try_obtain_lease).and_return(false) @@ -54,6 +64,30 @@ describe Projects::HousekeepingService do end.not_to yield_with_no_args end end + + context 'task type' do + it 'goes through all three housekeeping tasks, executing only the highest task when there is overlap' do + allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid) + allow(subject).to receive(:lease_key).and_return(:the_lease_key) + + # At push 200 + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid) + .exactly(1).times + # At push 50, 100, 150 + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid) + .exactly(3).times + # At push 10, 20, ... (except those above) + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid) + .exactly(16).times + + 201.times do + subject.increment! + subject.execute if subject.needed? + end + + expect(project.pushes_since_gc).to eq(1) + end + end end describe '#needed?' do @@ -69,31 +103,7 @@ describe Projects::HousekeepingService do describe '#increment!' do it 'increments the pushes_since_gc counter' do - expect do - subject.increment! - end.to change { project.pushes_since_gc }.from(0).to(1) + expect { subject.increment! }.to change { project.pushes_since_gc }.by(1) end end - - it 'goes through all three housekeeping tasks, executing only the highest task when there is overlap' do - allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid) - allow(subject).to receive(:lease_key).and_return(:the_lease_key) - - # At push 200 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid) - .exactly(1).times - # At push 50, 100, 150 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid) - .exactly(3).times - # At push 10, 20, ... (except those above) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid) - .exactly(16).times - - 201.times do - subject.increment! - subject.execute if subject.needed? - end - - expect(project.pushes_since_gc).to eq(1) - end end -- cgit v1.2.1 From 35cf604b72ec6cabffc46cc7c2da76fb303525cb Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 30 Apr 2018 22:25:56 +0900 Subject: Add to_param override to lookup :id path --- spec/models/ci/pipeline_spec.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index dd94515b0a4..51cdf185c9f 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -35,6 +35,15 @@ describe Ci::Pipeline, :mailer do end end + describe 'modules' do + it_behaves_like 'AtomicInternalId' do + let(:internal_id_attribute) { :iid } + let(:instance) { build(:ci_pipeline) } + let(:scope_attrs) { { project: instance.project } } + let(:usage) { :ci_pipelines } + end + end + describe '#source' do context 'when creating new pipeline' do let(:pipeline) do @@ -173,7 +182,7 @@ describe Ci::Pipeline, :mailer do it 'includes all predefined variables in a valid order' do keys = subject.map { |variable| variable[:key] } - expect(keys).to eq %w[CI_PIPELINE_ID CI_CONFIG_PATH CI_PIPELINE_SOURCE] + expect(keys).to eq %w[CI_PIPELINE_ID CI_PIPELINE_IID CI_CONFIG_PATH CI_PIPELINE_SOURCE] end end -- cgit v1.2.1 From 04dc80dbb5cb991172ebeb69b9a20c7b6eef4dbf Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 8 May 2018 16:01:18 +0900 Subject: Fix spec --- spec/models/ci/pipeline_spec.rb | 1 + .../shared_examples/models/atomic_internal_id_spec.rb | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 60ba9e62be7..d3e0389cc72 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -41,6 +41,7 @@ describe Ci::Pipeline, :mailer do let(:instance) { build(:ci_pipeline) } let(:scope_attrs) { { project: instance.project } } let(:usage) { :ci_pipelines } + let(:validate_presence) { false } end end diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index 6a6e13418a9..1bccb578d79 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' -shared_examples_for 'AtomicInternalId' do +shared_examples_for 'AtomicInternalId' do + let(:validate_presence) { true } + describe '.has_internal_id' do describe 'Module inclusion' do subject { described_class } @@ -15,7 +17,14 @@ shared_examples_for 'AtomicInternalId' do allow(InternalId).to receive(:generate_next).and_return(nil) end - it { is_expected.to validate_presence_of(internal_id_attribute) } + it 'checks presence' do + if validate_presence + is_expected.to validate_presence_of(internal_id_attribute) + else + is_expected.not_to validate_presence_of(internal_id_attribute) + end + end + it { is_expected.to validate_numericality_of(internal_id_attribute) } end -- cgit v1.2.1 From 30a6fb64dbce39cc0298e805935bb242c2d7b715 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Thu, 10 May 2018 15:56:47 +0900 Subject: Fix atomic internal id spec to allow generate pipeline --- .../shared_examples/models/atomic_internal_id_spec.rb | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'spec') diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index 1bccb578d79..cd6465675b5 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -shared_examples_for 'AtomicInternalId' do +shared_examples_for 'AtomicInternalId' do let(:validate_presence) { true } describe '.has_internal_id' do @@ -11,21 +11,16 @@ shared_examples_for 'AtomicInternalId' do end describe 'Validation' do - subject { instance } - before do - allow(InternalId).to receive(:generate_next).and_return(nil) + allow_any_instance_of(described_class).to receive(:ensure_iid!) {} end - it 'checks presence' do - if validate_presence - is_expected.to validate_presence_of(internal_id_attribute) - else - is_expected.not_to validate_presence_of(internal_id_attribute) - end - end + it 'validates presence' do + instance.valid? - it { is_expected.to validate_numericality_of(internal_id_attribute) } + expect(instance.errors[:iid]).to include("can't be blank") if validate_presence + expect(instance.errors[:iid]).to include("is not a number") # numericality + end end describe 'Creating an instance' do -- cgit v1.2.1 From 910a7d02a812b1203e320d843a77cad2c7069b63 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 11 May 2018 15:34:36 +0900 Subject: Remove numericality as it's redandant with integer column and validates nil IID --- spec/lib/gitlab/import_export/safe_model_attributes.yml | 1 + spec/models/ci/build_spec.rb | 1 + spec/support/shared_examples/models/atomic_internal_id_spec.rb | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 62da967cf96..2619c6a10d8 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -228,6 +228,7 @@ Ci::Pipeline: - config_source - failure_reason - protected +- iid Ci::Stage: - id - name diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index dc810489011..490b1f0b54e 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1517,6 +1517,7 @@ describe Ci::Build do { key: 'CI_PROJECT_URL', value: project.web_url, public: true }, { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true }, { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }, + { key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true }, { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true }, { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true }, { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true }, diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index cd6465675b5..ac1cfa47def 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -12,14 +12,13 @@ shared_examples_for 'AtomicInternalId' do describe 'Validation' do before do - allow_any_instance_of(described_class).to receive(:ensure_iid!) {} + allow_any_instance_of(described_class).to receive(:"ensure_#{scope_attrs.keys.first}_#{internal_id_attribute}!") {} end it 'validates presence' do instance.valid? expect(instance.errors[:iid]).to include("can't be blank") if validate_presence - expect(instance.errors[:iid]).to include("is not a number") # numericality end end -- cgit v1.2.1 From 46fa3089c84642170d86799c4f60fe87507e575d Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 11 May 2018 16:49:18 +0900 Subject: Rescue RecordNotUnique when pipeline is created with non-unique iid --- spec/lib/gitlab/ci/pipeline/chain/create_spec.rb | 37 ++++++++++++++++------ spec/models/ci/pipeline_spec.rb | 2 +- .../models/atomic_internal_id_spec.rb | 8 +++-- 3 files changed, 34 insertions(+), 13 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb index 0edc3f315bb..b8ab0135092 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -37,21 +37,38 @@ describe Gitlab::Ci::Pipeline::Chain::Create do end context 'when pipeline has validation errors' do - let(:pipeline) do - build(:ci_pipeline, project: project, ref: nil) + shared_examples_for 'expectations' do + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'appends validation error' do + expect(pipeline.errors.to_a) + .to include /Failed to persist the pipeline/ + end end + + context 'when ref is nil' do + let(:pipeline) do + build(:ci_pipeline, project: project, ref: nil) + end - before do - step.perform! - end + before do + step.perform! + end - it 'breaks the chain' do - expect(step.break?).to be true + it_behaves_like 'expectations' end - it 'appends validation error' do - expect(pipeline.errors.to_a) - .to include /Failed to persist the pipeline/ + context 'when pipeline has a duplicate iid' do + before do + allow_any_instance_of(Ci::Pipeline).to receive(:ensure_project_iid!) { |p| p.send(:write_attribute, :iid, 1) } + create(:ci_pipeline, project: project) + + step.perform! + end + + it_behaves_like 'expectations' end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index d3e0389cc72..c10d0eb55da 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -41,7 +41,7 @@ describe Ci::Pipeline, :mailer do let(:instance) { build(:ci_pipeline) } let(:scope_attrs) { { project: instance.project } } let(:usage) { :ci_pipelines } - let(:validate_presence) { false } + let(:allow_nil) { true } end end diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index ac1cfa47def..dce2622172b 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' shared_examples_for 'AtomicInternalId' do - let(:validate_presence) { true } + let(:allow_nil) { false } describe '.has_internal_id' do describe 'Module inclusion' do @@ -18,7 +18,11 @@ shared_examples_for 'AtomicInternalId' do it 'validates presence' do instance.valid? - expect(instance.errors[:iid]).to include("can't be blank") if validate_presence + if allow_nil + expect(instance.errors[:iid]).to be_empty + else + expect(instance.errors[:iid]).to include("can't be blank") + end end end -- cgit v1.2.1 From a74184eb5e692ef77fe3be28b1f4a40549c8fcff Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 11 May 2018 16:52:48 +0900 Subject: Fix static analysys --- spec/lib/gitlab/ci/pipeline/chain/create_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb index b8ab0135092..9c0bedfb6b7 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -41,13 +41,13 @@ describe Gitlab::Ci::Pipeline::Chain::Create do it 'breaks the chain' do expect(step.break?).to be true end - + it 'appends validation error' do expect(pipeline.errors.to_a) .to include /Failed to persist the pipeline/ end end - + context 'when ref is nil' do let(:pipeline) do build(:ci_pipeline, project: project, ref: nil) @@ -64,7 +64,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do before do allow_any_instance_of(Ci::Pipeline).to receive(:ensure_project_iid!) { |p| p.send(:write_attribute, :iid, 1) } create(:ci_pipeline, project: project) - + step.perform! end -- cgit v1.2.1 From 82a49d0fea1ace87b5619fbc4ed728f43fdef7bd Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 14 May 2018 17:41:56 +0900 Subject: Clarify scope for AtomicInternalId shared spec --- spec/models/ci/pipeline_spec.rb | 1 + spec/models/deployment_spec.rb | 1 + spec/models/issue_spec.rb | 1 + spec/models/merge_request_spec.rb | 1 + spec/models/milestone_spec.rb | 2 ++ spec/support/shared_examples/models/atomic_internal_id_spec.rb | 6 +++--- 6 files changed, 9 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index c10d0eb55da..e0fe62b39e6 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -39,6 +39,7 @@ describe Ci::Pipeline, :mailer do it_behaves_like 'AtomicInternalId' do let(:internal_id_attribute) { :iid } let(:instance) { build(:ci_pipeline) } + let(:scope) { :project } let(:scope_attrs) { { project: instance.project } } let(:usage) { :ci_pipelines } let(:allow_nil) { true } diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index aee70bcfb29..e01906f4b6c 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -20,6 +20,7 @@ describe Deployment do it_behaves_like 'AtomicInternalId' do let(:internal_id_attribute) { :iid } let(:instance) { build(:deployment) } + let(:scope) { :project } let(:scope_attrs) { { project: instance.project } } let(:usage) { :deployments } end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 128acf83686..e818fbeb9cf 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -17,6 +17,7 @@ describe Issue do it_behaves_like 'AtomicInternalId' do let(:internal_id_attribute) { :iid } let(:instance) { build(:issue) } + let(:scope) { :project } let(:scope_attrs) { { project: instance.project } } let(:usage) { :issues } end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 04379e7d2c3..c2ab1259ebf 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -25,6 +25,7 @@ describe MergeRequest do it_behaves_like 'AtomicInternalId' do let(:internal_id_attribute) { :iid } let(:instance) { build(:merge_request) } + let(:scope) { :target_project } let(:scope_attrs) { { project: instance.target_project } } let(:usage) { :merge_requests } end diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 4bb9717d33e..204d6b47832 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -6,6 +6,7 @@ describe Milestone do it_behaves_like 'AtomicInternalId' do let(:internal_id_attribute) { :iid } let(:instance) { build(:milestone, project: build(:project), group: nil) } + let(:scope) { :project } let(:scope_attrs) { { project: instance.project } } let(:usage) { :milestones } end @@ -15,6 +16,7 @@ describe Milestone do it_behaves_like 'AtomicInternalId' do let(:internal_id_attribute) { :iid } let(:instance) { build(:milestone, project: nil, group: build(:group)) } + let(:scope) { :group } let(:scope_attrs) { { namespace: instance.group } } let(:usage) { :milestones } end diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index dce2622172b..a05279364f2 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -12,16 +12,16 @@ shared_examples_for 'AtomicInternalId' do describe 'Validation' do before do - allow_any_instance_of(described_class).to receive(:"ensure_#{scope_attrs.keys.first}_#{internal_id_attribute}!") {} + allow_any_instance_of(described_class).to receive(:"ensure_#{scope}_#{internal_id_attribute}!") {} end it 'validates presence' do instance.valid? if allow_nil - expect(instance.errors[:iid]).to be_empty + expect(instance.errors[internal_id_attribute]).to be_empty else - expect(instance.errors[:iid]).to include("can't be blank") + expect(instance.errors[internal_id_attribute]).to include("can't be blank") end end end -- cgit v1.2.1 From 631bd9bf082c396059867d512fcfbdc80445c65e Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 21 May 2018 11:39:46 +0200 Subject: Use persisted stages to load pipelines index table --- .../projects/pipelines_controller_spec.rb | 52 +++++++++++++++------- 1 file changed, 37 insertions(+), 15 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 9e7bc20a6d1..d7ff2d6175c 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -19,16 +19,21 @@ describe Projects::PipelinesController do before do %w(pending running created success).each_with_index do |status, index| sha = project.commit("HEAD~#{index}") - create(:ci_empty_pipeline, status: status, project: project, sha: sha) - end - end - subject do - get :index, namespace_id: project.namespace, project_id: project, format: :json + pipeline = create(:ci_empty_pipeline, status: status, + project: project, + sha: sha) + + + create_build(pipeline, 'test', 1, 'unit') + create_build(pipeline, 'test', 1, 'feature') + create_build(pipeline, 'review', 2, 'staging') + create_build(pipeline, 'deploy', 3, 'production') + end end - it 'returns JSON with serialized pipelines' do - subject + it 'returns JSON with serialized pipelines', :request_store do + queries = ActiveRecord::QueryRecorder.new { get_pipelines_index_json } expect(response).to have_gitlab_http_status(:ok) expect(response).to match_response_schema('pipeline') @@ -39,22 +44,35 @@ describe Projects::PipelinesController do expect(json_response['count']['running']).to eq '1' expect(json_response['count']['pending']).to eq '1' expect(json_response['count']['finished']).to eq '1' + puts queries.log + expect(queries.count).to be < 25 end it 'does not include coverage data for the pipelines' do - subject + get_pipelines_index_json expect(json_response['pipelines'][0]).not_to include('coverage') end context 'when performing gitaly calls', :request_store do it 'limits the Gitaly requests' do - expect { subject }.to change { Gitlab::GitalyClient.get_request_count }.by(3) + expect { get_pipelines_index_json } + .to change { Gitlab::GitalyClient.get_request_count }.by(2) end end + + def get_pipelines_index_json + get :index, namespace_id: project.namespace, + project_id: project, + format: :json + end + + def create_build(pipeline, stage, stage_idx, name) + create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name) + end end - describe 'GET show JSON' do + describe 'GET show.json' do let(:pipeline) { create(:ci_pipeline_with_one_job, project: project) } it 'returns the pipeline' do @@ -67,6 +85,14 @@ describe Projects::PipelinesController do end context 'when the pipeline has multiple stages and groups', :request_store do + let(:project) { create(:project, :repository) } + + let(:pipeline) do + create(:ci_empty_pipeline, project: project, + user: user, + sha: project.commit.id) + end + before do create_build('build', 0, 'build') create_build('test', 1, 'rspec 0') @@ -74,11 +100,6 @@ describe Projects::PipelinesController do create_build('post deploy', 3, 'pages 0') end - let(:project) { create(:project, :repository) } - let(:pipeline) do - create(:ci_empty_pipeline, project: project, user: user, sha: project.commit.id) - end - it 'does not perform N + 1 queries' do control_count = ActiveRecord::QueryRecorder.new { get_pipeline_json }.count @@ -90,6 +111,7 @@ describe Projects::PipelinesController do create_build('post deploy', 3, 'pages 2') new_count = ActiveRecord::QueryRecorder.new { get_pipeline_json }.count + expect(new_count).to be_within(12).of(control_count) end end -- cgit v1.2.1 From 8e92e25b62ca108de775362e6d2981e54535f094 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 22 May 2018 15:21:45 +0900 Subject: Remvoe disable_ddl_transaction! and redandant RecordNotUnique exception rescue --- spec/lib/gitlab/ci/pipeline/chain/create_spec.rb | 27 ++++++------------------ 1 file changed, 6 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb index 9c0bedfb6b7..de69a65aee4 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -37,17 +37,6 @@ describe Gitlab::Ci::Pipeline::Chain::Create do end context 'when pipeline has validation errors' do - shared_examples_for 'expectations' do - it 'breaks the chain' do - expect(step.break?).to be true - end - - it 'appends validation error' do - expect(pipeline.errors.to_a) - .to include /Failed to persist the pipeline/ - end - end - context 'when ref is nil' do let(:pipeline) do build(:ci_pipeline, project: project, ref: nil) @@ -57,18 +46,14 @@ describe Gitlab::Ci::Pipeline::Chain::Create do step.perform! end - it_behaves_like 'expectations' - end - - context 'when pipeline has a duplicate iid' do - before do - allow_any_instance_of(Ci::Pipeline).to receive(:ensure_project_iid!) { |p| p.send(:write_attribute, :iid, 1) } - create(:ci_pipeline, project: project) - - step.perform! + it 'breaks the chain' do + expect(step.break?).to be true end - it_behaves_like 'expectations' + it 'appends validation error' do + expect(pipeline.errors.to_a) + .to include /Failed to persist the pipeline/ + end end end end -- cgit v1.2.1 From 6c63f96e0a26fa046fb0cc4664240e37db8b37d8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 22 May 2018 12:30:45 +0200 Subject: Preload number of warnings in every stage in a pipeline This makes it possible to avoid N+1 queries when loading pipelines table. --- .../projects/pipelines_controller_spec.rb | 45 ++++++++++++---------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index d7ff2d6175c..d5a0d32054b 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -17,35 +17,27 @@ describe Projects::PipelinesController do describe 'GET index.json' do before do - %w(pending running created success).each_with_index do |status, index| - sha = project.commit("HEAD~#{index}") - - pipeline = create(:ci_empty_pipeline, status: status, - project: project, - sha: sha) - - - create_build(pipeline, 'test', 1, 'unit') - create_build(pipeline, 'test', 1, 'feature') - create_build(pipeline, 'review', 2, 'staging') - create_build(pipeline, 'deploy', 3, 'production') - end + %w(pending running running success canceled) + .each_with_index do |status, index| + create_pipeline(status, project.commit("HEAD~#{index}")) + end end it 'returns JSON with serialized pipelines', :request_store do - queries = ActiveRecord::QueryRecorder.new { get_pipelines_index_json } + queries = ActiveRecord::QueryRecorder.new do + get_pipelines_index_json + end expect(response).to have_gitlab_http_status(:ok) expect(response).to match_response_schema('pipeline') expect(json_response).to include('pipelines') - expect(json_response['pipelines'].count).to eq 4 - expect(json_response['count']['all']).to eq '4' - expect(json_response['count']['running']).to eq '1' + expect(json_response['pipelines'].count).to eq 5 + expect(json_response['count']['all']).to eq '5' + expect(json_response['count']['running']).to eq '2' expect(json_response['count']['pending']).to eq '1' - expect(json_response['count']['finished']).to eq '1' - puts queries.log - expect(queries.count).to be < 25 + expect(json_response['count']['finished']).to eq '2' + expect(queries.count).to be < 32 end it 'does not include coverage data for the pipelines' do @@ -67,8 +59,19 @@ describe Projects::PipelinesController do format: :json end + def create_pipeline(status, sha) + pipeline = create(:ci_empty_pipeline, status: status, + project: project, + sha: sha) + + create_build(pipeline, 'build', 1, 'build') + create_build(pipeline, 'test', 2, 'test') + create_build(pipeline, 'deploy', 3, 'deploy') + end + def create_build(pipeline, stage, stage_idx, name) - create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name) + status = %w[created running pending success failed canceled].sample + create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status) end end -- cgit v1.2.1 From 76a7157c76c47fd4f835a27eeeda7b42012936be Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 22 May 2018 13:04:07 +0200 Subject: Abstract persisted/legacy stages in pipeline model --- spec/controllers/projects/pipelines_controller_spec.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index d5a0d32054b..c8080056f69 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -17,8 +17,7 @@ describe Projects::PipelinesController do describe 'GET index.json' do before do - %w(pending running running success canceled) - .each_with_index do |status, index| + %w(pending running success failed).each_with_index do |status, index| create_pipeline(status, project.commit("HEAD~#{index}")) end end @@ -32,11 +31,13 @@ describe Projects::PipelinesController do expect(response).to match_response_schema('pipeline') expect(json_response).to include('pipelines') - expect(json_response['pipelines'].count).to eq 5 - expect(json_response['count']['all']).to eq '5' - expect(json_response['count']['running']).to eq '2' + expect(json_response['pipelines'].count).to eq 4 + expect(json_response['count']['all']).to eq '4' + expect(json_response['count']['running']).to eq '1' expect(json_response['count']['pending']).to eq '1' expect(json_response['count']['finished']).to eq '2' + puts queries.log + puts "Queries count: #{queries.count}" expect(queries.count).to be < 32 end -- cgit v1.2.1 From ea35fd05bb175cf921e94d8b1c5e6bc5f3e217c9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 22 May 2018 13:55:05 +0200 Subject: Refactor pipeline preloader to split reponsibilities better --- .../projects/pipelines_controller_spec.rb | 2 -- spec/lib/gitlab/ci/pipeline/preloader_spec.rb | 22 ++++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index c8080056f69..536ef9b2d79 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -36,8 +36,6 @@ describe Projects::PipelinesController do expect(json_response['count']['running']).to eq '1' expect(json_response['count']['pending']).to eq '1' expect(json_response['count']['finished']).to eq '2' - puts queries.log - puts "Queries count: #{queries.count}" expect(queries.count).to be < 32 end diff --git a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb index 477c7477df0..c8cfe2c696d 100644 --- a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb @@ -1,20 +1,22 @@ # frozen_string_literal: true -require 'spec_helper' +require 'fast_spec_helper' describe Gitlab::Ci::Pipeline::Preloader do - describe '.preload' do - it 'preloads the author of every pipeline commit' do - commit = double(:commit) - pipeline = double(:pipeline, commit: commit) + let(:stage) { double(:stage) } + let(:commit) { double(:commit) } - expect(commit) - .to receive(:lazy_author) + let(:pipeline) do + double(:pipeline, commit: commit, stages: [stage]) + end - expect(pipeline) - .to receive(:number_of_warnings) + describe '#preload!' do + it 'preloads commit authors and number of warnings' do + expect(commit).to receive(:lazy_author) + expect(pipeline).to receive(:number_of_warnings) + expect(stage).to receive(:number_of_warnings) - described_class.preload([pipeline]) + described_class.new([pipeline]).preload! end end end -- cgit v1.2.1 From e4eb330af7577940d2440544004d1335fe221839 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 22 May 2018 14:00:42 +0200 Subject: Add pipeline stages factory method import/export support --- spec/lib/gitlab/import_export/all_models.yml | 1 + 1 file changed, 1 insertion(+) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 8b46b04b8b5..002e5e47208 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -256,6 +256,7 @@ project: - import_data - commit_statuses - pipelines +- stages - builds - runner_projects - runners -- cgit v1.2.1 From 0b3cca568ddaa43b4531392a8ccc391b1688bfdc Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 22 May 2018 14:38:25 +0200 Subject: Fix rubocop offense in pipeline controller specs :cop: --- spec/controllers/projects/pipelines_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 536ef9b2d79..2e0f9b00023 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -18,8 +18,8 @@ describe Projects::PipelinesController do describe 'GET index.json' do before do %w(pending running success failed).each_with_index do |status, index| - create_pipeline(status, project.commit("HEAD~#{index}")) - end + create_pipeline(status, project.commit("HEAD~#{index}")) + end end it 'returns JSON with serialized pipelines', :request_store do -- cgit v1.2.1 From f89f232d19a6c28a3504e964fbfd2b5eb344aa85 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 23 May 2018 10:22:28 +0200 Subject: Simplify pipelines preloader implementation --- spec/lib/gitlab/ci/pipeline/preloader_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb index c8cfe2c696d..16d3631ec7c 100644 --- a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb @@ -10,13 +10,13 @@ describe Gitlab::Ci::Pipeline::Preloader do double(:pipeline, commit: commit, stages: [stage]) end - describe '#preload!' do + describe '.preload!' do it 'preloads commit authors and number of warnings' do expect(commit).to receive(:lazy_author) expect(pipeline).to receive(:number_of_warnings) expect(stage).to receive(:number_of_warnings) - described_class.new([pipeline]).preload! + described_class.preload!([pipeline]) end end end -- cgit v1.2.1 From 8cca6c83a99100fcf3a0a3a56f10eebe3c9b7716 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 23 May 2018 10:37:01 +0200 Subject: DRY creating groups of common builds in a stage --- spec/models/ci/group_spec.rb | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'spec') diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb index 51123e73fe6..9052dd59369 100644 --- a/spec/models/ci/group_spec.rb +++ b/spec/models/ci/group_spec.rb @@ -41,4 +41,55 @@ describe Ci::Group do end end end + + describe '.fabricate' do + let(:pipeline) { create(:ci_empty_pipeline) } + let(:stage) { create(:ci_stage_entity, pipeline: pipeline) } + + before do + create_build(:ci_build, name: 'rspec 0 2') + create_build(:ci_build, name: 'rspec 0 1') + create_build(:ci_build, name: 'spinach 0 1') + create_build(:commit_status, name: 'aaaaa') + end + + it 'returns an array of three groups' do + expect(stage.groups).to be_a Array + expect(stage.groups).to all(be_a Ci::Group) + expect(stage.groups.size).to eq 3 + end + + it 'returns groups with correctly ordered statuses' do + expect(stage.groups.first.jobs.map(&:name)) + .to eq ['aaaaa'] + expect(stage.groups.second.jobs.map(&:name)) + .to eq ['rspec 0 1', 'rspec 0 2'] + expect(stage.groups.third.jobs.map(&:name)) + .to eq ['spinach 0 1'] + end + + it 'returns groups with correct names' do + expect(stage.groups.map(&:name)) + .to eq %w[aaaaa rspec spinach] + end + + context 'when a name is nil on legacy pipelines' do + before do + pipeline.builds.first.update_attribute(:name, nil) + end + + it 'returns an array of three groups' do + expect(stage.groups.map(&:name)) + .to eq ['', 'aaaaa', 'rspec', 'spinach'] + end + end + + def create_build(type, status: 'success', **opts) + create(type, pipeline: pipeline, + stage: stage.name, + status: status, + stage_id: stage.id, + **opts) + end + end end -- cgit v1.2.1 From dab3ae39a2093fa924daf9c1902a2784002cca63 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 23 May 2018 13:29:21 +0200 Subject: Do not paginate pipelines active relation twice --- spec/controllers/projects/pipelines_controller_spec.rb | 2 +- spec/lib/gitlab/ci/pipeline/preloader_spec.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 2e0f9b00023..d7253c90ee5 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -36,7 +36,7 @@ describe Projects::PipelinesController do expect(json_response['count']['running']).to eq '1' expect(json_response['count']['pending']).to eq '1' expect(json_response['count']['finished']).to eq '2' - expect(queries.count).to be < 32 + expect(queries.count).to be_within(2).of(29) end it 'does not include coverage data for the pipelines' do diff --git a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb index 16d3631ec7c..7821d28ab06 100644 --- a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb @@ -18,5 +18,15 @@ describe Gitlab::Ci::Pipeline::Preloader do described_class.preload!([pipeline]) end + + it 'returns original collection' do + allow(commit).to receive(:lazy_author) + allow(pipeline).to receive(:number_of_warnings) + allow(stage).to receive(:number_of_warnings) + + pipelines = [pipeline, pipeline] + + expect(described_class.preload!(pipelines)).to eq pipelines + end end end -- cgit v1.2.1 From 92de5f778faa989fb25f9a5c93c5e23618f2d87b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 23 May 2018 13:30:34 +0200 Subject: Fix Rubocop offense in grouped statuses class specs --- spec/models/ci/group_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb index 9052dd59369..838fa63cb1f 100644 --- a/spec/models/ci/group_spec.rb +++ b/spec/models/ci/group_spec.rb @@ -55,7 +55,7 @@ describe Ci::Group do it 'returns an array of three groups' do expect(stage.groups).to be_a Array - expect(stage.groups).to all(be_a Ci::Group) + expect(stage.groups).to all(be_a described_class) expect(stage.groups.size).to eq 3 end -- cgit v1.2.1 From bc9a0e10b50430e0b253a15d1628b6776d0bd9fe Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 23 May 2018 14:22:22 +0200 Subject: Reduce amount of expected pipeline serialization queries in specs --- spec/controllers/projects/pipelines_controller_spec.rb | 2 +- spec/serializers/pipeline_serializer_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index d7253c90ee5..62eece8de4a 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -36,7 +36,7 @@ describe Projects::PipelinesController do expect(json_response['count']['running']).to eq '1' expect(json_response['count']['pending']).to eq '1' expect(json_response['count']['finished']).to eq '2' - expect(queries.count).to be_within(2).of(29) + expect(queries.count).to be < 30 end it 'does not include coverage data for the pipelines' do diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index b741308e2c5..5108eb4deec 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -120,7 +120,7 @@ describe PipelineSerializer do it 'verifies number of queries', :request_store do recorded = ActiveRecord::QueryRecorder.new { subject } - expect(recorded.count).to be_within(1).of(44) + expect(recorded.count).to be_within(1).of(38) expect(recorded.cached_count).to eq(0) end end @@ -139,7 +139,7 @@ describe PipelineSerializer do # pipeline. With the same ref this check is cached but if refs are # different then there is an extra query per ref # https://gitlab.com/gitlab-org/gitlab-ce/issues/46368 - expect(recorded.count).to be_within(1).of(51) + expect(recorded.count).to be_within(1).of(45) expect(recorded.cached_count).to eq(0) end end -- cgit v1.2.1 From f0d7445b88f6598db85198296c076bf59508188a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 24 May 2018 16:55:24 +0200 Subject: Reduce pipeline serialization queries when preloaded --- spec/serializers/pipeline_serializer_spec.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 5108eb4deec..9319d29279a 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -99,7 +99,8 @@ describe PipelineSerializer do end end - context 'number of queries' do + describe 'number of queries when preloaded' do + subject { serializer.represent(resource, preload: true) } let(:resource) { Ci::Pipeline.all } before do @@ -120,7 +121,7 @@ describe PipelineSerializer do it 'verifies number of queries', :request_store do recorded = ActiveRecord::QueryRecorder.new { subject } - expect(recorded.count).to be_within(1).of(38) + expect(recorded.count).to be_within(1).of(31) expect(recorded.cached_count).to eq(0) end end @@ -139,7 +140,7 @@ describe PipelineSerializer do # pipeline. With the same ref this check is cached but if refs are # different then there is an extra query per ref # https://gitlab.com/gitlab-org/gitlab-ce/issues/46368 - expect(recorded.count).to be_within(1).of(45) + expect(recorded.count).to be_within(1).of(38) expect(recorded.cached_count).to eq(0) end end -- cgit v1.2.1 From 887818d3cb58406555bc038ed36b75b7a4ce2631 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 25 May 2018 12:43:27 +0200 Subject: Do not update stage status when it is just created --- spec/models/ci/stage_spec.rb | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index a00db1d2bfc..d82bf20b032 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -65,7 +65,31 @@ describe Ci::Stage, :models do end end - context 'when stage is skipped' do + context 'when stage has only created builds' do + let(:stage) { create(:ci_stage_entity, status: :created) } + + before do + create(:ci_build, :created, stage_id: stage.id) + end + + it 'updates status to skipped' do + expect(stage.reload.status).to eq 'created' + end + end + + context 'when stage is skipped because of skipped builds' do + before do + create(:ci_build, :skipped, stage_id: stage.id) + end + + it 'updates status to skipped' do + expect { stage.update_status } + .to change { stage.reload.status } + .to 'skipped' + end + end + + context 'when stage is skipped because is empty' do it 'updates status to skipped' do expect { stage.update_status } .to change { stage.reload.status } -- cgit v1.2.1 From c0827455ab20a156b86a20556c79139e4eb114e5 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 25 May 2018 12:47:56 +0200 Subject: Add specs for stage detailed status and warnings --- spec/models/ci/stage_spec.rb | 104 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index d82bf20b032..b482e7de88b 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -112,7 +112,71 @@ describe Ci::Stage, :models do end end - describe '#index' do + + describe '#detailed_status' do + using RSpec::Parameterized::TableSyntax + + let(:user) { create(:user) } + let(:stage) { create(:ci_stage_entity, status: :created) } + subject { stage.detailed_status(user) } + + where(:statuses, :label) do + %w[created] | :created + %w[success] | :passed + %w[pending] | :pending + %w[skipped] | :skipped + %w[canceled] | :canceled + %w[success failed] | :failed + %w[running pending] | :running + end + + with_them do + before do + statuses.each do |status| + create(:commit_status, project: stage.project, + pipeline: stage.pipeline, + stage_id: stage.id, + status: status) + + stage.update_status + end + end + + it 'has a correct label' do + expect(subject.label).to eq label.to_s + end + end + + context 'when stage has warnings' do + before do + create(:ci_build, project: stage.project, + pipeline: stage.pipeline, + stage_id: stage.id, + status: :failed, + allow_failure: true) + + stage.update_status + end + + it 'is passed with warnings' do + expect(subject.label).to eq 'passed with warnings' + end + end + end + + describe '#groups' do + before do + create(:ci_build, stage_id: stage.id, name: 'rspec 0 1') + create(:ci_build, stage_id: stage.id, name: 'rspec 0 2') + end + + it 'groups stage builds by name' do + expect(stage.groups).to be_one + expect(stage.groups.first.name).to eq 'rspec' + end + end + + describe '#position' do context 'when stage has been imported and does not have position index set' do before do stage.update_column(:position, nil) @@ -143,4 +207,42 @@ describe Ci::Stage, :models do end end end + + context 'when stage has warnings' do + before do + create(:ci_build, :failed, :allowed_to_fail, stage_id: stage.id) + end + + describe '#has_warnings?' do + it 'returns true' do + expect(stage).to have_warnings + end + end + + describe '#number_of_warnings' do + it 'returns a lazy stage warnings counter' do + lazy_queries = ActiveRecord::QueryRecorder.new do + stage.number_of_warnings + end + + synced_queries = ActiveRecord::QueryRecorder.new do + stage.number_of_warnings.to_i + end + + expect(lazy_queries.count).to eq 0 + expect(synced_queries.count).to eq 1 + + expect(stage.number_of_warnings.inspect).to include 'BatchLoader' + expect(stage.number_of_warnings).to eq 1 + end + end + end + + context 'when stage does not have warnings' do + describe '#has_warnings?' do + it 'returns false' do + expect(stage).not_to have_warnings + end + end + end end -- cgit v1.2.1 From c896ae7be889dd3935795b1079ab7a11a12f52e0 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 25 May 2018 12:38:45 +0100 Subject: BE review --- spec/features/projects/labels/update_prioritization_spec.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'spec') diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index ae8b1364ec7..9500e3f3311 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -23,8 +23,10 @@ feature 'Prioritize labels' do expect(page).to have_content('Star labels to start sorting by priority') page.within('.other-labels') do + screenshot_and_open_image all('.js-toggle-priority')[1].click wait_for_requests + screenshot_and_open_image expect(page).not_to have_content('feature') end -- cgit v1.2.1 From 1af2274b4126b3e910604ef321b58d327ea21e63 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 25 May 2018 13:43:11 +0200 Subject: Restore lazy loading of pipeline commits in preloader --- spec/lib/gitlab/ci/pipeline/preloader_spec.rb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb index 7821d28ab06..40dfd893465 100644 --- a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'fast_spec_helper' +require 'spec_helper' describe Gitlab::Ci::Pipeline::Preloader do let(:stage) { double(:stage) } @@ -11,6 +11,23 @@ describe Gitlab::Ci::Pipeline::Preloader do end describe '.preload!' do + context 'when preloading multiple commits' do + let(:project) { create(:project, :repository) } + + it 'preloads all commits once' do + expect(Commit).to receive(:decorate).once.and_call_original + + pipelines = [build_pipeline(ref: 'HEAD'), + build_pipeline(ref: 'HEAD~1')] + + described_class.preload!(pipelines) + end + + def build_pipeline(ref:) + build_stubbed(:ci_pipeline, project: project, sha: project.commit(ref).id) + end + end + it 'preloads commit authors and number of warnings' do expect(commit).to receive(:lazy_author) expect(pipeline).to receive(:number_of_warnings) -- cgit v1.2.1 From 8a3aa3a6365f511a2620b56946721f1584bade99 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 25 May 2018 13:47:22 +0200 Subject: Fix Rubocop offenses in pipeline stage specs --- spec/models/ci/stage_spec.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index b482e7de88b..b40496252b4 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -112,7 +112,6 @@ describe Ci::Stage, :models do end end - describe '#detailed_status' do using RSpec::Parameterized::TableSyntax @@ -134,9 +133,9 @@ describe Ci::Stage, :models do before do statuses.each do |status| create(:commit_status, project: stage.project, - pipeline: stage.pipeline, - stage_id: stage.id, - status: status) + pipeline: stage.pipeline, + stage_id: stage.id, + status: status) stage.update_status end @@ -149,13 +148,13 @@ describe Ci::Stage, :models do context 'when stage has warnings' do before do - create(:ci_build, project: stage.project, - pipeline: stage.pipeline, - stage_id: stage.id, - status: :failed, - allow_failure: true) + create(:ci_build, project: stage.project, + pipeline: stage.pipeline, + stage_id: stage.id, + status: :failed, + allow_failure: true) - stage.update_status + stage.update_status end it 'is passed with warnings' do -- cgit v1.2.1 From aac8d1f3363a87f0bcd31009aad41d577f0a3f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 28 May 2018 21:10:51 +0200 Subject: Add check for nil auto_devops in Projects::UpdateService --- spec/services/projects/update_service_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 3e6073b9861..1f761bcbbad 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -275,6 +275,10 @@ describe Projects::UpdateService do it { is_expected.to eq(false) } end + context 'when auto devops is nil' do + it { is_expected.to eq(false) } + end + context 'when auto devops is explicitly enabled' do before do project.create_auto_devops!(enabled: true) -- cgit v1.2.1 From fad343ca5b1fcdd4d01e415f31ee4a9fddb4eb99 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 09:53:33 +0100 Subject: Remove screenshots --- spec/features/projects/labels/update_prioritization_spec.rb | 2 -- 1 file changed, 2 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index 9500e3f3311..ae8b1364ec7 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -23,10 +23,8 @@ feature 'Prioritize labels' do expect(page).to have_content('Star labels to start sorting by priority') page.within('.other-labels') do - screenshot_and_open_image all('.js-toggle-priority')[1].click wait_for_requests - screenshot_and_open_image expect(page).not_to have_content('feature') end -- cgit v1.2.1 From c9e4c1760dae5e1916bb5ad22f260b7fd0b58475 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 10:25:21 +0100 Subject: Fix project subscription_spec --- spec/features/projects/labels/subscription_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb index 70e8d436dcb..d5f5036ec72 100644 --- a/spec/features/projects/labels/subscription_spec.rb +++ b/spec/features/projects/labels/subscription_spec.rb @@ -36,7 +36,7 @@ feature 'Labels subscription' do within "#group_label_#{feature.id}" do expect(page).not_to have_button 'Unsubscribe' - click_link_on_dropdown('Group level') + click_link_on_dropdown('Subscribe at group level') expect(page).not_to have_selector('.dropdown-group-label') expect(page).to have_button 'Unsubscribe' @@ -45,7 +45,7 @@ feature 'Labels subscription' do expect(page).to have_selector('.dropdown-group-label') - click_link_on_dropdown('Project level') + click_link_on_dropdown('Subscribe at project level') expect(page).not_to have_selector('.dropdown-group-label') expect(page).to have_button 'Unsubscribe' @@ -67,6 +67,8 @@ feature 'Labels subscription' do def click_link_on_dropdown(text) find('.dropdown-group-label').click + screenshot_and_open_image + page.within('.dropdown-group-label') do find('a.js-subscribe-button', text: text).click end -- cgit v1.2.1 From 35b5c830a783eb36f86d9f5e8f8c08595fd62c54 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 10:48:26 +0100 Subject: Further fixes for project subscription_spec --- spec/features/projects/labels/subscription_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb index d5f5036ec72..fafd338e448 100644 --- a/spec/features/projects/labels/subscription_spec.rb +++ b/spec/features/projects/labels/subscription_spec.rb @@ -67,10 +67,8 @@ feature 'Labels subscription' do def click_link_on_dropdown(text) find('.dropdown-group-label').click - screenshot_and_open_image - page.within('.dropdown-group-label') do - find('a.js-subscribe-button', text: text).click + find('.js-subscribe-button', text: text).click end end end -- cgit v1.2.1 From 3b3645136bd524a5e9f79558ab16be976cfe5282 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 11:07:48 +0100 Subject: Fix update_prioritization_spec --- spec/features/projects/labels/update_prioritization_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index ae8b1364ec7..359381c391c 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -102,16 +102,16 @@ feature 'Prioritize labels' do drag_to(selector: '.label-list-item', from_index: 1, to_index: 2) page.within('.prioritized-labels') do - expect(first('li')).to have_content('feature') - expect(page.all('li').last).to have_content('bug') + expect(first('.label-list-item')).to have_content('feature') + expect(page.all('.label-list-item').last).to have_content('bug') end refresh wait_for_requests page.within('.prioritized-labels') do - expect(first('li')).to have_content('feature') - expect(page.all('li').last).to have_content('bug') + expect(first('.label-list-item')).to have_content('feature') + expect(page.all('.label-list-item').last).to have_content('bug') end end -- cgit v1.2.1 From cd0be3ba456f5cf61ed72f1c6b36bef799d7eaaf Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 12:11:14 +0100 Subject: Fix user_removes_labels_spec --- .../projects/labels/user_removes_labels_spec.rb | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb index f4fda6de465..120c618b530 100644 --- a/spec/features/projects/labels/user_removes_labels_spec.rb +++ b/spec/features/projects/labels/user_removes_labels_spec.rb @@ -17,8 +17,9 @@ describe "User removes labels" do end it "removes label" do - page.within(".labels") do + page.within(".other-labels") do page.first(".label-list-item") do + first('.js-label-options-dropdown').click first(".remove-row").click first(:link, "Delete label").click end @@ -36,17 +37,15 @@ describe "User removes labels" do end it "removes all labels" do - page.within(".labels") do - loop do - li = page.first(".label-list-item") - break unless li - - li.click_link("Delete") - click_link("Delete label") - end - - expect(page).to have_content("Generate a default set of labels").and have_content("New label") + loop do + li = page.first(".label-list-item") + break unless li + li.find('.js-label-options-dropdown').click + li.click_button("Delete") + click_link("Delete label") end + + expect(page).to have_content("Generate a default set of labels").and have_content("New label") end end end -- cgit v1.2.1 From 9b9cdc984cb785ceee4ed1902625c65492640621 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 29 May 2018 12:50:29 +0100 Subject: fix lint --- spec/features/projects/labels/user_removes_labels_spec.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'spec') diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb index 120c618b530..efa74015c6e 100644 --- a/spec/features/projects/labels/user_removes_labels_spec.rb +++ b/spec/features/projects/labels/user_removes_labels_spec.rb @@ -40,6 +40,7 @@ describe "User removes labels" do loop do li = page.first(".label-list-item") break unless li + li.find('.js-label-options-dropdown').click li.click_button("Delete") click_link("Delete label") -- cgit v1.2.1 From fc3d214130f2038b3c8bdf142506c9116f373244 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 29 May 2018 15:20:10 +0200 Subject: Add a feature flag for switching pipeline stages --- spec/models/ci/pipeline_spec.rb | 81 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'spec') diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index e7845b693a1..f3725e24470 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -500,6 +500,87 @@ describe Ci::Pipeline, :mailer do end end end + + describe '#stages' do + before do + create(:ci_stage_entity, project: project, + pipeline: pipeline, + name: 'build') + end + + it 'returns persisted stages' do + expect(pipeline.stages).not_to be_empty + expect(pipeline.stages).to all(be_persisted) + end + end + + describe '#ordered_stages' do + before do + create(:ci_stage_entity, project: project, + pipeline: pipeline, + position: 4, + name: 'deploy') + + create(:ci_build, project: project, + pipeline: pipeline, + stage: 'test', + stage_idx: 3, + name: 'test') + + create(:ci_build, project: project, + pipeline: pipeline, + stage: 'build', + stage_idx: 2, + name: 'build') + + create(:ci_stage_entity, project: project, + pipeline: pipeline, + position: 1, + name: 'sanity') + + create(:ci_stage_entity, project: project, + pipeline: pipeline, + position: 5, + name: 'cleanup') + end + + subject { pipeline.ordered_stages } + + context 'when using legacy stages' do + before do + stub_feature_flags(ci_pipeline_persisted_stages: false) + end + + it 'returns legacy stages in valid order' do + expect(subject.map(&:name)).to eq %w[build test] + end + end + + context 'when using persisted stages' do + before do + stub_feature_flags(ci_pipeline_persisted_stages: true) + end + + context 'when pipelines is not complete' do + it 'still returns legacy stages' do + expect(subject).to all(be_a Ci::LegacyStage) + expect(subject.map(&:name)).to eq %w[build test] + end + end + + context 'when pipeline is complete' do + before do + pipeline.succeed! + end + + it 'returns stages in valid order' do + expect(subject).to all(be_a Ci::Stage) + expect(subject.map(&:name)) + .to eq %w[sanity build test deploy cleanup] + end + end + end + end end describe 'state machine' do -- cgit v1.2.1 From 3662f147692749b20c5c8ad8ea391fd94e43ed38 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 29 May 2018 17:19:39 +0200 Subject: Improve specs for pipelines controller --- .../projects/pipelines_controller_spec.rb | 67 +++++++++++++++++----- 1 file changed, 53 insertions(+), 14 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 62eece8de4a..92886e93077 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -17,26 +17,65 @@ describe Projects::PipelinesController do describe 'GET index.json' do before do - %w(pending running success failed).each_with_index do |status, index| + %w(pending running success failed canceled).each_with_index do |status, index| create_pipeline(status, project.commit("HEAD~#{index}")) end end - it 'returns JSON with serialized pipelines', :request_store do - queries = ActiveRecord::QueryRecorder.new do - get_pipelines_index_json + context 'when using persisted stages', :request_store do + before do + stub_feature_flags(ci_pipeline_persisted_stages: true) end - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('pipeline') - - expect(json_response).to include('pipelines') - expect(json_response['pipelines'].count).to eq 4 - expect(json_response['count']['all']).to eq '4' - expect(json_response['count']['running']).to eq '1' - expect(json_response['count']['pending']).to eq '1' - expect(json_response['count']['finished']).to eq '2' - expect(queries.count).to be < 30 + it 'returns serialized pipelines', :request_store do + queries = ActiveRecord::QueryRecorder.new do + get_pipelines_index_json + end + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('pipeline') + + expect(json_response).to include('pipelines') + expect(json_response['pipelines'].count).to eq 5 + expect(json_response['count']['all']).to eq '5' + expect(json_response['count']['running']).to eq '1' + expect(json_response['count']['pending']).to eq '1' + expect(json_response['count']['finished']).to eq '3' + + json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages| + expect(stages.count).to eq 3 + end + + expect(queries.count).to be + end + end + + context 'when using legacy stages', :request_store do + before do + stub_feature_flags(ci_pipeline_persisted_stages: false) + end + + it 'returns JSON with serialized pipelines', :request_store do + queries = ActiveRecord::QueryRecorder.new do + get_pipelines_index_json + end + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('pipeline') + + expect(json_response).to include('pipelines') + expect(json_response['pipelines'].count).to eq 5 + expect(json_response['count']['all']).to eq '5' + expect(json_response['count']['running']).to eq '1' + expect(json_response['count']['pending']).to eq '1' + expect(json_response['count']['finished']).to eq '3' + + json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages| + expect(stages.count).to eq 3 + end + + expect(queries.count).to be_within(3).of(30) + end end it 'does not include coverage data for the pipelines' do -- cgit v1.2.1 From 162cc55e49e155d64ee862dfebefd97cec7d272c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 29 May 2018 17:23:49 +0200 Subject: Use persisted stage feature to serialize pipelines --- spec/serializers/pipeline_serializer_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 9319d29279a..7c7cdaedf10 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -8,6 +8,10 @@ describe PipelineSerializer do described_class.new(current_user: user) end + before do + stub_feature_flags(ci_pipeline_persisted_stages: true) + end + subject { serializer.represent(resource) } describe '#represent' do -- cgit v1.2.1 From 0e22b50df8b269ccae32ab68b9ba26e7eea861b0 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Wed, 30 May 2018 16:42:55 +0900 Subject: Add spec for variables expression --- spec/lib/gitlab/ci/pipeline/chain/create_spec.rb | 32 ++++++++++------------ spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 24 ++++++++++++++++ spec/models/ci/pipeline_spec.rb | 14 ++++++++++ 3 files changed, 53 insertions(+), 17 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb index de69a65aee4..0edc3f315bb 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -37,23 +37,21 @@ describe Gitlab::Ci::Pipeline::Chain::Create do end context 'when pipeline has validation errors' do - context 'when ref is nil' do - let(:pipeline) do - build(:ci_pipeline, project: project, ref: nil) - end - - before do - step.perform! - end - - it 'breaks the chain' do - expect(step.break?).to be true - end - - it 'appends validation error' do - expect(pipeline.errors.to_a) - .to include /Failed to persist the pipeline/ - end + let(:pipeline) do + build(:ci_pipeline, project: project, ref: nil) + end + + before do + step.perform! + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'appends validation error' do + expect(pipeline.errors.to_a) + .to include /Failed to persist the pipeline/ end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 4d7d6951a51..bcfa9f0c282 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -42,6 +42,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do it 'correctly assigns user' do expect(pipeline.builds).to all(have_attributes(user: user)) end + + it 'has pipeline iid' do + expect(pipeline.iid).to be > 0 + end end context 'when pipeline is empty' do @@ -68,6 +72,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do expect(pipeline.errors.to_a) .to include 'No stages / jobs for this pipeline.' end + + it 'wastes pipeline iid' do + expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0 + end end context 'when pipeline has validation errors' do @@ -87,6 +95,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do expect(pipeline.errors.to_a) .to include 'Failed to build the pipeline!' end + + it 'wastes pipeline iid' do + expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0 + end end context 'when there is a seed blocks present' do @@ -111,6 +123,12 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do expect(pipeline.variables.first.key).to eq 'VAR' expect(pipeline.variables.first.value).to eq '123' end + + it 'has pipeline iid' do + step.perform! + + expect(pipeline.iid).to be > 0 + end end context 'when seeds block tries to persist some resources' do @@ -121,6 +139,12 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do it 'raises exception' do expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved) end + + it 'does not waste pipeline iid' do + step.perform rescue nil + + expect(InternalId.ci_pipelines.where(project_id: project.id).exists?).to be_falsy + end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 314cb3a28ed..7d28f2eb86b 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -397,6 +397,20 @@ describe Ci::Pipeline, :mailer do expect(seeds.size).to eq 1 expect(seeds.dig(0, 0, :name)).to eq 'unit' end + + context "when pipeline iid is used for 'only' keyword" do + let(:config) do + { rspec: { script: 'rspec', only: { variables: ['$CI_PIPELINE_IID == 2'] } }, + prod: { script: 'cap prod', only: { variables: ['$CI_PIPELINE_IID == 1'] } } } + end + + it 'returns stage seeds only when variables expression is truthy' do + seeds = pipeline.stage_seeds + + expect(seeds.size).to eq 1 + expect(seeds.dig(0, 0, :name)).to eq 'prod' + end + end end end -- cgit v1.2.1 From 74caf54704704e30b8ed45e2459828890f416b08 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 30 May 2018 15:49:55 +0200 Subject: Fix pipeline serializer queries count specs --- spec/serializers/pipeline_serializer_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 7c7cdaedf10..e0e6eecb300 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -112,7 +112,7 @@ describe PipelineSerializer do # gitaly calls in this block # Issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/37772 Gitlab::GitalyClient.allow_n_plus_1_calls do - Ci::Pipeline::AVAILABLE_STATUSES.each do |status| + Ci::Pipeline::COMPLETED_STATUSES.each do |status| create_pipeline(status) end end @@ -125,7 +125,7 @@ describe PipelineSerializer do it 'verifies number of queries', :request_store do recorded = ActiveRecord::QueryRecorder.new { subject } - expect(recorded.count).to be_within(1).of(31) + expect(recorded.count).to be_within(2).of(27) expect(recorded.cached_count).to eq(0) end end @@ -144,7 +144,7 @@ describe PipelineSerializer do # pipeline. With the same ref this check is cached but if refs are # different then there is an extra query per ref # https://gitlab.com/gitlab-org/gitlab-ce/issues/46368 - expect(recorded.count).to be_within(1).of(38) + expect(recorded.count).to be_within(2).of(30) expect(recorded.cached_count).to eq(0) end end -- cgit v1.2.1 From 0d44f4d50ef175997fe1f90de9e622a4f3b867e3 Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Wed, 23 May 2018 09:54:57 +0800 Subject: Rephrase "maintainer" to more precise "members who can merge to the target branch" "Maintainer" will be freed to be used for #42751 --- .../merge_request/maintainer_edits_fork_spec.rb | 2 +- .../user_allows_a_maintainer_to_push_spec.rb | 85 ---------------------- ...ows_commits_from_memebers_who_can_merge_spec.rb | 85 ++++++++++++++++++++++ .../api/schemas/entities/merge_request_basic.json | 1 + .../api/schemas/entities/merge_request_widget.json | 2 +- .../api/schemas/public_api/v4/merge_requests.json | 1 + .../gitlab/import_export/safe_model_attributes.yml | 2 +- spec/lib/gitlab/user_access_spec.rb | 2 +- spec/models/merge_request_spec.rb | 28 +++---- spec/models/project_spec.rb | 22 +++--- spec/policies/ci/build_policy_spec.rb | 2 +- spec/policies/ci/pipeline_policy_spec.rb | 2 +- spec/policies/project_policy_spec.rb | 2 +- spec/requests/api/merge_requests_spec.rb | 12 +-- spec/services/ci/retry_pipeline_service_spec.rb | 4 +- .../services/merge_requests/update_service_spec.rb | 12 +-- 16 files changed, 134 insertions(+), 130 deletions(-) delete mode 100644 spec/features/merge_request/user_allows_a_maintainer_to_push_spec.rb create mode 100644 spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb (limited to 'spec') diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb index a3323da1b1f..1808d0c0a0c 100644 --- a/spec/features/merge_request/maintainer_edits_fork_spec.rb +++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb @@ -14,7 +14,7 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js source_branch: 'fix', target_branch: 'master', author: author, - allow_maintainer_to_push: true) + allow_collaboration: true) end before do diff --git a/spec/features/merge_request/user_allows_a_maintainer_to_push_spec.rb b/spec/features/merge_request/user_allows_a_maintainer_to_push_spec.rb deleted file mode 100644 index eb41d7de8ed..00000000000 --- a/spec/features/merge_request/user_allows_a_maintainer_to_push_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -require 'spec_helper' - -describe 'create a merge request that allows maintainers to push', :js do - include ProjectForksHelper - let(:user) { create(:user) } - let(:target_project) { create(:project, :public, :repository) } - let(:source_project) { fork_project(target_project, user, repository: true, namespace: user.namespace) } - - def visit_new_merge_request - visit project_new_merge_request_path( - source_project, - merge_request: { - source_project_id: source_project.id, - target_project_id: target_project.id, - source_branch: 'fix', - target_branch: 'master' - }) - end - - before do - sign_in(user) - end - - it 'allows setting maintainer push possible' do - visit_new_merge_request - - check 'Allow edits from maintainers' - - click_button 'Submit merge request' - - wait_for_requests - - expect(page).to have_content('Allows edits from maintainers') - end - - it 'shows a message when one of the projects is private' do - source_project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) - - visit_new_merge_request - - expect(page).to have_content('Not available for private projects') - end - - it 'shows a message when the source branch is protected' do - create(:protected_branch, project: source_project, name: 'fix') - - visit_new_merge_request - - expect(page).to have_content('Not available for protected branches') - end - - context 'when the merge request is being created within the same project' do - let(:source_project) { target_project } - - it 'hides the checkbox if the merge request is being created within the same project' do - target_project.add_developer(user) - - visit_new_merge_request - - expect(page).not_to have_content('Allows edits from maintainers') - end - end - - context 'when a maintainer tries to edit the option' do - let(:maintainer) { create(:user) } - let(:merge_request) do - create(:merge_request, - source_project: source_project, - target_project: target_project, - source_branch: 'fixes') - end - - before do - target_project.add_master(maintainer) - - sign_in(maintainer) - end - - it 'it hides the option from maintainers' do - visit edit_project_merge_request_path(target_project, merge_request) - - expect(page).not_to have_content('Allows edits from maintainers') - end - end -end diff --git a/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb new file mode 100644 index 00000000000..0af37d76539 --- /dev/null +++ b/spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe 'create a merge request, allowing commits from members who can merge to the target branch', :js do + include ProjectForksHelper + let(:user) { create(:user) } + let(:target_project) { create(:project, :public, :repository) } + let(:source_project) { fork_project(target_project, user, repository: true, namespace: user.namespace) } + + def visit_new_merge_request + visit project_new_merge_request_path( + source_project, + merge_request: { + source_project_id: source_project.id, + target_project_id: target_project.id, + source_branch: 'fix', + target_branch: 'master' + }) + end + + before do + sign_in(user) + end + + it 'allows setting possible' do + visit_new_merge_request + + check 'Allow commits from members who can merge to the target branch' + + click_button 'Submit merge request' + + wait_for_requests + + expect(page).to have_content('Allows commits from members who can merge to the target branch') + end + + it 'shows a message when one of the projects is private' do + source_project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) + + visit_new_merge_request + + expect(page).to have_content('Not available for private projects') + end + + it 'shows a message when the source branch is protected' do + create(:protected_branch, project: source_project, name: 'fix') + + visit_new_merge_request + + expect(page).to have_content('Not available for protected branches') + end + + context 'when the merge request is being created within the same project' do + let(:source_project) { target_project } + + it 'hides the checkbox if the merge request is being created within the same project' do + target_project.add_developer(user) + + visit_new_merge_request + + expect(page).not_to have_content('Allows commits from members who can merge to the target branch') + end + end + + context 'when a member who can merge tries to edit the option' do + let(:member) { create(:user) } + let(:merge_request) do + create(:merge_request, + source_project: source_project, + target_project: target_project, + source_branch: 'fixes') + end + + before do + target_project.add_master(member) + + sign_in(member) + end + + it 'it hides the option from members' do + visit edit_project_merge_request_path(target_project, merge_request) + + expect(page).not_to have_content('Allows commits from members who can merge to the target branch') + end + end +end diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json index 46031961cca..f7bc137c90c 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_basic.json +++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json @@ -13,6 +13,7 @@ "assignee_id": { "type": ["integer", "null"] }, "subscribed": { "type": ["boolean", "null"] }, "participants": { "type": "array" }, + "allow_collaboration": { "type": "boolean"}, "allow_maintainer_to_push": { "type": "boolean"} }, "additionalProperties": false diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json index 7be8c9e3e67..ee5588fa6c6 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json @@ -31,7 +31,7 @@ "source_project_id": { "type": "integer" }, "target_branch": { "type": "string" }, "target_project_id": { "type": "integer" }, - "allow_maintainer_to_push": { "type": "boolean"}, + "allow_collaboration": { "type": "boolean"}, "metrics": { "oneOf": [ { "type": "null" }, diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json index f97461ce9cc..f7adc4e0b91 100644 --- a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json +++ b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json @@ -82,6 +82,7 @@ "human_time_estimate": { "type": ["string", "null"] }, "human_total_time_spent": { "type": ["string", "null"] } }, + "allow_collaboration": { "type": ["boolean", "null"] }, "allow_maintainer_to_push": { "type": ["boolean", "null"] } }, "required": [ diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 74e7a45fd6c..d389dcb4e80 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -170,7 +170,7 @@ MergeRequest: - last_edited_by_id - head_pipeline_id - discussion_locked -- allow_maintainer_to_push +- allow_collaboration MergeRequestDiff: - id - state diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 97b6069f64d..0469d984a40 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -142,7 +142,7 @@ describe Gitlab::UserAccess do target_project: canonical_project, source_project: project, source_branch: 'awesome-feature', - allow_maintainer_to_push: true + allow_collaboration: true ) end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 9ffa91fc265..8718fe50769 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2236,25 +2236,25 @@ describe MergeRequest do end end - describe '#allow_maintainer_to_push' do + describe '#allow_collaboration' do let(:merge_request) do - build(:merge_request, source_branch: 'fixes', allow_maintainer_to_push: true) + build(:merge_request, source_branch: 'fixes', allow_collaboration: true) end it 'is false when pushing by a maintainer is not possible' do - expect(merge_request).to receive(:maintainer_push_possible?) { false } + expect(merge_request).to receive(:collaborative_push_possible?) { false } - expect(merge_request.allow_maintainer_to_push).to be_falsy + expect(merge_request.allow_collaboration).to be_falsy end it 'is true when pushing by a maintainer is possible' do - expect(merge_request).to receive(:maintainer_push_possible?) { true } + expect(merge_request).to receive(:collaborative_push_possible?) { true } - expect(merge_request.allow_maintainer_to_push).to be_truthy + expect(merge_request.allow_collaboration).to be_truthy end end - describe '#maintainer_push_possible?' do + describe '#collaborative_push_possible?' do let(:merge_request) do build(:merge_request, source_branch: 'fixes') end @@ -2266,14 +2266,14 @@ describe MergeRequest do it 'does not allow maintainer to push if the source project is the same as the target' do merge_request.target_project = merge_request.source_project = create(:project, :public) - expect(merge_request.maintainer_push_possible?).to be_falsy + expect(merge_request.collaborative_push_possible?).to be_falsy end it 'allows maintainer to push when both source and target are public' do merge_request.target_project = build(:project, :public) merge_request.source_project = build(:project, :public) - expect(merge_request.maintainer_push_possible?).to be_truthy + expect(merge_request.collaborative_push_possible?).to be_truthy end it 'is not available for protected branches' do @@ -2284,11 +2284,11 @@ describe MergeRequest do .with(merge_request.source_project, 'fixes') .and_return(true) - expect(merge_request.maintainer_push_possible?).to be_falsy + expect(merge_request.collaborative_push_possible?).to be_falsy end end - describe '#can_allow_maintainer_to_push?' do + describe '#can_allow_collaboration?' do let(:target_project) { create(:project, :public) } let(:source_project) { fork_project(target_project) } let(:merge_request) do @@ -2300,17 +2300,17 @@ describe MergeRequest do let(:user) { create(:user) } before do - allow(merge_request).to receive(:maintainer_push_possible?) { true } + allow(merge_request).to receive(:collaborative_push_possible?) { true } end it 'is false if the user does not have push access to the source project' do - expect(merge_request.can_allow_maintainer_to_push?(user)).to be_falsy + expect(merge_request.can_allow_collaboration?(user)).to be_falsy end it 'is true when the user has push access to the source project' do source_project.add_developer(user) - expect(merge_request.can_allow_maintainer_to_push?(user)).to be_truthy + expect(merge_request.can_allow_collaboration?(user)).to be_truthy end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index af2240f4f89..b0cbf8796e3 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3583,7 +3583,7 @@ describe Project do target_branch: 'target-branch', source_project: project, source_branch: 'awesome-feature-1', - allow_maintainer_to_push: true + allow_collaboration: true ) end @@ -3620,9 +3620,9 @@ describe Project do end end - describe '#branch_allows_maintainer_push?' do + describe '#branch_allows_collaboration_push?' do it 'allows access if the user can merge the merge request' do - expect(project.branch_allows_maintainer_push?(user, 'awesome-feature-1')) + expect(project.branch_allows_collaboration?(user, 'awesome-feature-1')) .to be_truthy end @@ -3630,7 +3630,7 @@ describe Project do guest = create(:user) target_project.add_guest(guest) - expect(project.branch_allows_maintainer_push?(guest, 'awesome-feature-1')) + expect(project.branch_allows_collaboration?(guest, 'awesome-feature-1')) .to be_falsy end @@ -3640,31 +3640,31 @@ describe Project do target_branch: 'target-branch', source_project: project, source_branch: 'rejected-feature-1', - allow_maintainer_to_push: true) + allow_collaboration: true) - expect(project.branch_allows_maintainer_push?(user, 'rejected-feature-1')) + expect(project.branch_allows_collaboration?(user, 'rejected-feature-1')) .to be_falsy end it 'does not allow access if the user cannot merge the merge request' do create(:protected_branch, :masters_can_push, project: target_project, name: 'target-branch') - expect(project.branch_allows_maintainer_push?(user, 'awesome-feature-1')) + expect(project.branch_allows_collaboration?(user, 'awesome-feature-1')) .to be_falsy end it 'caches the result' do - control = ActiveRecord::QueryRecorder.new { project.branch_allows_maintainer_push?(user, 'awesome-feature-1') } + control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') } - expect { 3.times { project.branch_allows_maintainer_push?(user, 'awesome-feature-1') } } + expect { 3.times { project.branch_allows_collaboration?(user, 'awesome-feature-1') } } .not_to exceed_query_limit(control) end context 'when the requeststore is active', :request_store do it 'only queries per project across instances' do - control = ActiveRecord::QueryRecorder.new { project.branch_allows_maintainer_push?(user, 'awesome-feature-1') } + control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') } - expect { 2.times { described_class.find(project.id).branch_allows_maintainer_push?(user, 'awesome-feature-1') } } + expect { 2.times { described_class.find(project.id).branch_allows_collaboration?(user, 'awesome-feature-1') } } .not_to exceed_query_limit(control).with_threshold(2) end end diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb index 9ca156deaa0..eead55d33ca 100644 --- a/spec/policies/ci/build_policy_spec.rb +++ b/spec/policies/ci/build_policy_spec.rb @@ -101,7 +101,7 @@ describe Ci::BuildPolicy do it 'enables update_build if user is maintainer' do allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false) - allow_any_instance_of(Project).to receive(:branch_allows_maintainer_push?).and_return(true) + allow_any_instance_of(Project).to receive(:branch_allows_collaboration?).and_return(true) expect(policy).to be_allowed :update_build expect(policy).to be_allowed :update_commit_status diff --git a/spec/policies/ci/pipeline_policy_spec.rb b/spec/policies/ci/pipeline_policy_spec.rb index a5e509cfa0f..bd32faf06ef 100644 --- a/spec/policies/ci/pipeline_policy_spec.rb +++ b/spec/policies/ci/pipeline_policy_spec.rb @@ -69,7 +69,7 @@ describe Ci::PipelinePolicy, :models do it 'enables update_pipeline if user is maintainer' do allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false) - allow_any_instance_of(Project).to receive(:branch_allows_maintainer_push?).and_return(true) + allow_any_instance_of(Project).to receive(:branch_allows_collaboration?).and_return(true) expect(policy).to be_allowed :update_pipeline end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 6609f5f7afd..6ac151f92f3 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -400,7 +400,7 @@ describe ProjectPolicy do :merge_request, target_project: target_project, source_project: project, - allow_maintainer_to_push: true + allow_collaboration: true ) end let(:maintainer_abilities) do diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 605761867bf..d4ebfc3f782 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -386,12 +386,13 @@ describe API::MergeRequests do source_project: forked_project, target_project: project, source_branch: 'fixes', - allow_maintainer_to_push: true) + allow_collaboration: true) end - it 'includes the `allow_maintainer_to_push` field' do + it 'includes the `allow_collaboration` field' do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) + expect(json_response['allow_collaboration']).to be_truthy expect(json_response['allow_maintainer_to_push']).to be_truthy end end @@ -654,11 +655,12 @@ describe API::MergeRequests do expect(response).to have_gitlab_http_status(400) end - it 'allows setting `allow_maintainer_to_push`' do + it 'allows setting `allow_collaboration`' do post api("/projects/#{forked_project.id}/merge_requests", user2), - title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", - author: user2, target_project_id: project.id, allow_maintainer_to_push: true + title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", + author: user2, target_project_id: project.id, allow_collaboration: true expect(response).to have_gitlab_http_status(201) + expect(json_response['allow_collaboration']).to be_truthy expect(json_response['allow_maintainer_to_push']).to be_truthy end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index a73bd7a0268..688d3b8c038 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -280,12 +280,12 @@ describe Ci::RetryPipelineService, '#execute' do source_project: forked_project, target_project: project, source_branch: 'fixes', - allow_maintainer_to_push: true) + allow_collaboration: true) create_build('rspec 1', :failed, 1) end it 'allows to retry failed pipeline' do - allow_any_instance_of(Project).to receive(:fetch_branch_allows_maintainer_push?).and_return(true) + allow_any_instance_of(Project).to receive(:fetch_branch_allows_collaboration?).and_return(true) allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false) service.execute(pipeline) diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 5279ea6164e..cc26df725c7 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -541,7 +541,7 @@ describe MergeRequests::UpdateService, :mailer do let(:closed_issuable) { create(:closed_merge_request, source_project: project) } end - context 'setting `allow_maintainer_to_push`' do + context 'setting `allow_collaboration`' do let(:target_project) { create(:project, :public) } let(:source_project) { fork_project(target_project) } let(:user) { create(:user) } @@ -556,23 +556,23 @@ describe MergeRequests::UpdateService, :mailer do allow(ProtectedBranch).to receive(:protected?).with(source_project, 'fixes') { false } end - it 'does not allow a maintainer of the target project to set `allow_maintainer_to_push`' do + it 'does not allow a maintainer of the target project to set `allow_collaboration`' do target_project.add_developer(user) - update_merge_request(allow_maintainer_to_push: true, title: 'Updated title') + update_merge_request(allow_collaboration: true, title: 'Updated title') expect(merge_request.title).to eq('Updated title') - expect(merge_request.allow_maintainer_to_push).to be_falsy + expect(merge_request.allow_collaboration).to be_falsy end it 'is allowed by a user that can push to the source and can update the merge request' do merge_request.update!(assignee: user) source_project.add_developer(user) - update_merge_request(allow_maintainer_to_push: true, title: 'Updated title') + update_merge_request(allow_collaboration: true, title: 'Updated title') expect(merge_request.title).to eq('Updated title') - expect(merge_request.allow_maintainer_to_push).to be_truthy + expect(merge_request.allow_collaboration).to be_truthy end end end -- cgit v1.2.1 From 4beeb60255f228dc45dbe8f675a3cc59c0ea7773 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 14:36:52 +0900 Subject: Fix populate_spec --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index bcfa9f0c282..feed7728f5a 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -75,7 +75,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do it 'wastes pipeline iid' do expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0 - end + end end context 'when pipeline has validation errors' do @@ -98,7 +98,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do it 'wastes pipeline iid' do expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0 - end + end end context 'when there is a seed blocks present' do @@ -144,7 +144,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do step.perform rescue nil expect(InternalId.ci_pipelines.where(project_id: project.id).exists?).to be_falsy - end + end end end -- cgit v1.2.1 From f7f60ab54ab69fb4d0c3a43406a9809edab7d762 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 14:53:00 +0900 Subject: Add spec for variables expressions with pipeline iid --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 45 +++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index feed7728f5a..6b18c615430 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -156,22 +156,41 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do end end - context 'when using only/except build policies' do - let(:config) do - { rspec: { script: 'rspec', stage: 'test', only: ['master'] }, - prod: { script: 'cap prod', stage: 'deploy', only: ['tags'] } } - end + context 'when variables policy is specified' do + context 'when using only/except build policies' do + let(:config) do + { rspec: { script: 'rspec', stage: 'test', only: ['master'] }, + prod: { script: 'cap prod', stage: 'deploy', only: ['tags'] } } + end - let(:pipeline) do - build(:ci_pipeline, ref: 'master', config: config) - end + let(:pipeline) do + build(:ci_pipeline, ref: 'master', config: config) + end - it 'populates pipeline according to used policies' do - step.perform! + it 'populates pipeline according to used policies' do + step.perform! - expect(pipeline.stages.size).to eq 1 - expect(pipeline.stages.first.builds.size).to eq 1 - expect(pipeline.stages.first.builds.first.name).to eq 'rspec' + expect(pipeline.stages.size).to eq 1 + expect(pipeline.stages.first.builds.size).to eq 1 + expect(pipeline.stages.first.builds.first.name).to eq 'rspec' + end + + context 'when variables expression is specified' do + let(:config) do + { rspec: { script: 'rspec', only: { variables: ["$CI_PIPELINE_IID == '1'"] } }, + prod: { script: 'cap prod', only: { variables: ["$CI_PIPELINE_IID == '1000'"] } } } + end + + context 'when pipeline iid is the subject' do + it 'populates pipeline according to used policies' do + step.perform! + + expect(pipeline.stages.size).to eq 1 + expect(pipeline.stages.first.builds.size).to eq 1 + expect(pipeline.stages.first.builds.first.name).to eq 'rspec' + end + end + end end end end -- cgit v1.2.1 From c754b6937c8077304386b3a6b37233e52eacdb3e Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 15:37:36 +0900 Subject: Clean up presence validation spec --- spec/models/ci/pipeline_spec.rb | 3 +-- .../models/atomic_internal_id_spec.rb | 28 +++++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 7d28f2eb86b..e03c068b88e 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -36,13 +36,12 @@ describe Ci::Pipeline, :mailer do end describe 'modules' do - it_behaves_like 'AtomicInternalId' do + it_behaves_like 'AtomicInternalId', validate_presence: false do let(:internal_id_attribute) { :iid } let(:instance) { build(:ci_pipeline) } let(:scope) { :project } let(:scope_attrs) { { project: instance.project } } let(:usage) { :ci_pipelines } - let(:allow_nil) { true } end end diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index a05279364f2..d0cd8da67e1 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -1,8 +1,6 @@ require 'spec_helper' -shared_examples_for 'AtomicInternalId' do - let(:allow_nil) { false } - +shared_examples_for 'AtomicInternalId' do |validate_presence: true| describe '.has_internal_id' do describe 'Module inclusion' do subject { described_class } @@ -12,18 +10,30 @@ shared_examples_for 'AtomicInternalId' do describe 'Validation' do before do - allow_any_instance_of(described_class).to receive(:"ensure_#{scope}_#{internal_id_attribute}!") {} - end + allow_any_instance_of(described_class).to receive(:"ensure_#{scope}_#{internal_id_attribute}!") - it 'validates presence' do instance.valid? + end - if allow_nil - expect(instance.errors[internal_id_attribute]).to be_empty - else + context 'when presence validattion is required' do + before do + skip unless validate_presence + end + + it 'validates presence' do expect(instance.errors[internal_id_attribute]).to include("can't be blank") end end + + context 'when presence validattion is not required' do + before do + skip if validate_presence + end + + it 'does not validate presence' do + expect(instance.errors[internal_id_attribute]).to be_empty + end + end end describe 'Creating an instance' do -- cgit v1.2.1 From c418d68765eb09c468419ec8f438100cda64a0d4 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 15:41:33 +0900 Subject: Remove unneccesary spec --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 2 +- spec/models/ci/pipeline_spec.rb | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 6b18c615430..ffb2c1d5b0c 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -188,7 +188,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do expect(pipeline.stages.size).to eq 1 expect(pipeline.stages.first.builds.size).to eq 1 expect(pipeline.stages.first.builds.first.name).to eq 'rspec' - end + end end end end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index e03c068b88e..2b9c232743d 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -396,20 +396,6 @@ describe Ci::Pipeline, :mailer do expect(seeds.size).to eq 1 expect(seeds.dig(0, 0, :name)).to eq 'unit' end - - context "when pipeline iid is used for 'only' keyword" do - let(:config) do - { rspec: { script: 'rspec', only: { variables: ['$CI_PIPELINE_IID == 2'] } }, - prod: { script: 'cap prod', only: { variables: ['$CI_PIPELINE_IID == 1'] } } } - end - - it 'returns stage seeds only when variables expression is truthy' do - seeds = pipeline.stage_seeds - - expect(seeds.size).to eq 1 - expect(seeds.dig(0, 0, :name)).to eq 'prod' - end - end end end -- cgit v1.2.1 From c89e57842ebf7f395363bcddaeff76bc7b3f7890 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 15:46:15 +0900 Subject: Use shared examples for populate spec --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 34 ++++++++++------------ .../models/atomic_internal_id_spec.rb | 4 +-- 2 files changed, 18 insertions(+), 20 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index ffb2c1d5b0c..7088233f237 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -157,6 +157,16 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do end context 'when variables policy is specified' do + shared_examples_for 'populates pipeline according to used policies' do + it 'populates pipeline according to used policies' do + step.perform! + + expect(pipeline.stages.size).to eq 1 + expect(pipeline.stages.first.builds.size).to eq 1 + expect(pipeline.stages.first.builds.first.name).to eq 'rspec' + end + end + context 'when using only/except build policies' do let(:config) do { rspec: { script: 'rspec', stage: 'test', only: ['master'] }, @@ -167,28 +177,16 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do build(:ci_pipeline, ref: 'master', config: config) end - it 'populates pipeline according to used policies' do - step.perform! - - expect(pipeline.stages.size).to eq 1 - expect(pipeline.stages.first.builds.size).to eq 1 - expect(pipeline.stages.first.builds.first.name).to eq 'rspec' - end + it_behaves_like 'populates pipeline according to used policies' context 'when variables expression is specified' do - let(:config) do - { rspec: { script: 'rspec', only: { variables: ["$CI_PIPELINE_IID == '1'"] } }, - prod: { script: 'cap prod', only: { variables: ["$CI_PIPELINE_IID == '1000'"] } } } - end - context 'when pipeline iid is the subject' do - it 'populates pipeline according to used policies' do - step.perform! - - expect(pipeline.stages.size).to eq 1 - expect(pipeline.stages.first.builds.size).to eq 1 - expect(pipeline.stages.first.builds.first.name).to eq 'rspec' + let(:config) do + { rspec: { script: 'rspec', only: { variables: ["$CI_PIPELINE_IID == '1'"] } }, + prod: { script: 'cap prod', only: { variables: ["$CI_PIPELINE_IID == '1000'"] } } } end + + it_behaves_like 'populates pipeline according to used policies' end end end diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb index d0cd8da67e1..7ab1041d17c 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -15,7 +15,7 @@ shared_examples_for 'AtomicInternalId' do |validate_presence: true| instance.valid? end - context 'when presence validattion is required' do + context 'when presence validation is required' do before do skip unless validate_presence end @@ -25,7 +25,7 @@ shared_examples_for 'AtomicInternalId' do |validate_presence: true| end end - context 'when presence validattion is not required' do + context 'when presence validation is not required' do before do skip if validate_presence end -- cgit v1.2.1 From 9b2e19fe37a5d1389e9f83531bb6ba4b06a66de0 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Thu, 24 May 2018 08:55:47 +0200 Subject: Adds variables to POST api/v4/projects/:id/pipeline --- spec/requests/api/pipelines_spec.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index 0736329f9fd..f20c2275152 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -294,13 +294,28 @@ describe API::Pipelines do it 'creates and returns a new pipeline' do expect do post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch - end.to change { Ci::Pipeline.count }.by(1) + end.to change { project.pipelines.count }.by(1) expect(response).to have_gitlab_http_status(201) expect(json_response).to be_a Hash expect(json_response['sha']).to eq project.commit.id end + context 'variables given' do + let(:variables_attributes) { [{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] } + + it 'creates and returns a new pipeline using the given variables' do + expect do + post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch, variables_attributes: variables_attributes + end.to change { project.pipelines.count }.by(1) + + expect(response).to have_gitlab_http_status(201) + expect(json_response).to be_a Hash + expect(json_response['sha']).to eq project.commit.id + expect(json_response['variables']).to eq variables_attributes + end + end + it 'fails when using an invalid ref' do post api("/projects/#{project.id}/pipeline", user), ref: 'invalid_ref' -- cgit v1.2.1 From 6ae16b6d4d9fb79b715875073bb78efd3f56929b Mon Sep 17 00:00:00 2001 From: Jacopo Date: Thu, 31 May 2018 09:47:53 +0200 Subject: Rename variables_attributes => variables and adds spec for exclude/only option --- spec/requests/api/pipelines_spec.rb | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index f20c2275152..0c6bb56e11d 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -302,17 +302,32 @@ describe API::Pipelines do end context 'variables given' do - let(:variables_attributes) { [{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] } + let(:variables) { [{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] } it 'creates and returns a new pipeline using the given variables' do expect do - post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch, variables_attributes: variables_attributes + post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch, variables: variables end.to change { project.pipelines.count }.by(1) expect(response).to have_gitlab_http_status(201) expect(json_response).to be_a Hash expect(json_response['sha']).to eq project.commit.id - expect(json_response['variables']).to eq variables_attributes + expect(json_response['variables']).to eq variables + end + end + + context 'when excluding a ref' do + before do + config = YAML.dump(test: { script: 'test', except: [project.default_branch] }) + stub_ci_pipeline_yaml_file(config) + end + + it "doesn't not create a job for the exluded ref" do + expect do + post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch + end.not_to change { project.pipelines.count } + + expect(response).to have_gitlab_http_status(400) end end -- cgit v1.2.1 From 3871ea33ca524ef2d420a4ff311d8535622bd150 Mon Sep 17 00:00:00 2001 From: Jacopo Date: Fri, 1 Jun 2018 15:52:24 +0200 Subject: Review 1 --- spec/requests/api/pipelines_spec.rb | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index 0c6bb56e11d..0a64c46bb92 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -316,18 +316,35 @@ describe API::Pipelines do end end - context 'when excluding a ref' do + describe 'using variables conditions' do + let(:variables) { [{ 'key' => 'STAGING', 'value' => 'true' }] } + before do - config = YAML.dump(test: { script: 'test', except: [project.default_branch] }) + config = YAML.dump(test: { script: 'test', only: { variables: ['$STAGING'] } }) stub_ci_pipeline_yaml_file(config) end - it "doesn't not create a job for the exluded ref" do + it 'creates and returns a new pipeline using the given variables' do expect do - post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch - end.not_to change { project.pipelines.count } + post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch, variables: variables + end.to change { project.pipelines.count }.by(1) - expect(response).to have_gitlab_http_status(400) + expect(response).to have_gitlab_http_status(201) + expect(json_response).to be_a Hash + expect(json_response['sha']).to eq project.commit.id + expect(json_response['variables']).to eq variables + end + + context 'condition unmatch' do + let(:variables) { [{ 'key' => 'STAGING', 'value' => 'false' }] } + + it "doesn't create a job" do + expect do + post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch + end.not_to change { project.pipelines.count } + + expect(response).to have_gitlab_http_status(400) + end end end -- cgit v1.2.1 From 5e9687a198f205131d8e65c747f6b4ac2ae092f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Fri, 1 Jun 2018 23:39:24 +0200 Subject: Add deploy_strategy to ProjectAutoDevops --- spec/models/project_auto_devops_spec.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'spec') diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 7545c0797e9..1bfc6befb4b 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -5,6 +5,8 @@ describe ProjectAutoDevops do it { is_expected.to belong_to(:project) } + it { is_expected.to define_enum_for(:deploy_strategy) } + it { is_expected.to respond_to(:created_at) } it { is_expected.to respond_to(:updated_at) } -- cgit v1.2.1 From 39412d0a163625bd91632267a1dd36c1ebb23906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Fri, 1 Jun 2018 23:47:38 +0200 Subject: Add deploy strategy related predefined variables --- spec/factories/project_auto_devops.rb | 1 + spec/models/project_auto_devops_spec.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'spec') diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index 5ce1988c76f..a59087cd7eb 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -3,5 +3,6 @@ FactoryBot.define do project enabled true domain "example.com" + deploy_strategy :continuous end end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 1bfc6befb4b..37cd1f571e5 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -69,6 +69,19 @@ describe ProjectAutoDevops do end end + context 'when deploy_strategy is continuous' do + let(:domain) { 'example.com' } + + before do + auto_devops.deploy_strategy = 'continuous' + end + + it do + expect(auto_devops.predefined_variables.map { |var| var[:key] }) + .to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED") + end + end + def domain_variable { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } end -- cgit v1.2.1 From e8ecae7e0be12e6a1fe0e999d724ab5db9bb8b69 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 2 Jun 2018 11:55:42 +0900 Subject: Reveert build_relations and simply add a line for creating iid --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 7088233f237..e1766fc0ec9 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -140,10 +140,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved) end - it 'does not waste pipeline iid' do - step.perform rescue nil + it 'wastes pipeline iid' do + expect { step.perform! }.to raise_error - expect(InternalId.ci_pipelines.where(project_id: project.id).exists?).to be_falsy + expect(InternalId.ci_pipelines.where(project_id: project.id).exists?).to be_truthy end end end -- cgit v1.2.1 From 5403a4a07ed86770f1ab11108ab5a86fa2b90e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Sat, 2 Jun 2018 11:46:55 +0200 Subject: Add deploy_strategy to safe model attributes --- spec/lib/gitlab/import_export/safe_model_attributes.yml | 1 + 1 file changed, 1 insertion(+) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 74e7a45fd6c..5e6311441f8 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -539,6 +539,7 @@ ProjectAutoDevops: - id - enabled - domain +- deploy_strategy - project_id - created_at - updated_at -- cgit v1.2.1 From d65cdd441627cf73ad9e2554ca8d6912e851672d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 2 Jun 2018 00:28:57 -0700 Subject: Fix intermittent failing spec in spec/support/helpers/cycle_analytics_helpers.rb There was a race condition in the spec where if the commit is created on disk within a second of the frozen `Timecop` time, the test fails. Closes #43981 --- spec/lib/gitlab/cycle_analytics/usage_data_spec.rb | 19 ++++++++--------- spec/support/helpers/cycle_analytics_helpers.rb | 24 +++++++++++----------- 2 files changed, 21 insertions(+), 22 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb index 56a316318cb..a785b17f682 100644 --- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb @@ -3,7 +3,12 @@ require 'spec_helper' describe Gitlab::CycleAnalytics::UsageData do describe '#to_json' do before do - Timecop.freeze do + # Since git commits only have second precision, round up to the + # nearest second to ensure we have accurate median and standard + # deviation calculations. + current_time = Time.at(Time.now.to_i) + + Timecop.freeze(current_time) do user = create(:user, :admin) projects = create_list(:project, 2, :repository) @@ -37,13 +42,7 @@ describe Gitlab::CycleAnalytics::UsageData do expected_values.each_pair do |op, value| expect(stage_values).to have_key(op) - - if op == :missing - expect(stage_values[op]).to eq(value) - else - # delta is used because of git timings that Timecop does not stub - expect(stage_values[op].to_i).to be_within(5).of(value.to_i) - end + expect(stage_values[op]).to eq(value) end end end @@ -58,8 +57,8 @@ describe Gitlab::CycleAnalytics::UsageData do missing: 0 }, plan: { - average: 2, - sd: 2, + average: 1, + sd: 0, missing: 0 }, code: { diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index 55359d36597..06a76d53354 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -4,12 +4,12 @@ module CycleAnalyticsHelpers create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name) end - def create_commit(message, project, user, branch_name, count: 1) + def create_commit(message, project, user, branch_name, count: 1, commit_time: nil, skip_push_handler: false) repository = project.repository - oldrev = repository.commit(branch_name).sha + oldrev = repository.commit(branch_name)&.sha || Gitlab::Git::BLANK_SHA if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files) - mock_gitaly_multi_action_dates(repository.raw) + mock_gitaly_multi_action_dates(repository.raw, commit_time) end commit_shas = Array.new(count) do |index| @@ -19,6 +19,8 @@ module CycleAnalyticsHelpers commit_sha end + return if skip_push_handler + GitPushService.new(project, user, oldrev: oldrev, @@ -44,13 +46,11 @@ module CycleAnalyticsHelpers project.repository.add_branch(user, source_branch, 'master') end - sha = project.repository.create_file( - user, - generate(:branch), - 'content', - message: commit_message, - branch_name: source_branch) - project.repository.commit(sha) + # Cycle analytic specs often test with frozen times, which causes metrics to be + # pinned to the current time. For example, in the plan stage, we assume that an issue + # milestone has been created before any code has been written. We add a second + # to ensure that the plan time is positive. + create_commit(commit_message, project, user, source_branch, commit_time: Time.now + 1.second, skip_push_handler: true) opts = { title: 'Awesome merge_request', @@ -116,9 +116,9 @@ module CycleAnalyticsHelpers protected: false) end - def mock_gitaly_multi_action_dates(raw_repository) + def mock_gitaly_multi_action_dates(raw_repository, commit_time) allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args| - new_date = Time.now + new_date = commit_time || Time.now branch_update = m.call(*args) if branch_update.newrev -- cgit v1.2.1 From caadfcdee31237bbfbcfb304346fc4a5994295dd Mon Sep 17 00:00:00 2001 From: Mario de la Ossa Date: Mon, 21 May 2018 20:24:37 -0600 Subject: Backport changes in app/services/test_hooks/project_service.rb --- spec/services/test_hooks/project_service_spec.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/services/test_hooks/project_service_spec.rb b/spec/services/test_hooks/project_service_spec.rb index 962b9f40c4f..19e1c5ff3b2 100644 --- a/spec/services/test_hooks/project_service_spec.rb +++ b/spec/services/test_hooks/project_service_spec.rb @@ -6,13 +6,19 @@ describe TestHooks::ProjectService do describe '#execute' do let(:project) { create(:project, :repository) } let(:hook) { create(:project_hook, project: project) } + let(:trigger) { 'not_implemented_events' } let(:service) { described_class.new(hook, current_user, trigger) } let(:sample_data) { { data: 'sample' } } let(:success_result) { { status: :success, http_status: 200, message: 'ok' } } - context 'hook with not implemented test' do - let(:trigger) { 'not_implemented_events' } + it 'allows to set a custom project' do + project = double + service.project = project + + expect(service.project).to eq(project) + end + context 'hook with not implemented test' do it 'returns error message' do expect(hook).not_to receive(:execute) expect(service.execute).to include({ status: :error, message: 'Testing not available for this hook' }) -- cgit v1.2.1 From 3579ed016acbc3a512217d896a112747fcfd33bc Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sun, 3 Jun 2018 22:17:36 +0900 Subject: Revert "Add a new have_html_escaped_body_text that match an HTML-escaped text" This reverts commit 517598ba10793efa02cb90379f78ab97c9c5b25d. --- spec/mailers/notify_spec.rb | 18 +++++++++--------- spec/support/matchers/email_matchers.rb | 5 ----- 2 files changed, 9 insertions(+), 14 deletions(-) delete mode 100644 spec/support/matchers/email_matchers.rb (limited to 'spec') diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 69eafbe4bbe..ecd3d309327 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -660,7 +660,7 @@ describe Notify do is_expected.to have_html_escaped_body_text project.full_name is_expected.to have_body_text project.web_url is_expected.to have_body_text project_member.invite_email - is_expected.to have_html_escaped_body_text invited_user.name + is_expected.to have_body_text invited_user.name end end @@ -932,7 +932,7 @@ describe Notify do end it 'contains the message from the note' do - is_expected.to have_html_escaped_body_text note.note + is_expected.to have_body_text note.note end it 'contains an introduction' do @@ -991,7 +991,7 @@ describe Notify do expect(to_emails).to eq([recipient.notification_email]) is_expected.to have_subject "Request to join the #{group.name} group" - is_expected.to have_html_escaped_body_text group.name + is_expected.to have_body_text group.name is_expected.to have_body_text group_group_members_url(group) is_expected.to have_body_text group_member.human_access end @@ -1010,7 +1010,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject "Access to the #{group.name} group was denied" - is_expected.to have_html_escaped_body_text group.name + is_expected.to have_body_text group.name is_expected.to have_body_text group.web_url end end @@ -1026,7 +1026,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject "Access to the #{group.name} group was granted" - is_expected.to have_html_escaped_body_text group.name + is_expected.to have_body_text group.name is_expected.to have_body_text group.web_url is_expected.to have_body_text group_member.human_access end @@ -1056,7 +1056,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject "Invitation to join the #{group.name} group" - is_expected.to have_html_escaped_body_text group.name + is_expected.to have_body_text group.name is_expected.to have_body_text group.web_url is_expected.to have_body_text group_member.human_access is_expected.to have_body_text group_member.invite_token @@ -1080,10 +1080,10 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject 'Invitation accepted' - is_expected.to have_html_escaped_body_text group.name + is_expected.to have_body_text group.name is_expected.to have_body_text group.web_url is_expected.to have_body_text group_member.invite_email - is_expected.to have_html_escaped_body_text invited_user.name + is_expected.to have_body_text invited_user.name end end @@ -1103,7 +1103,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject 'Invitation declined' - is_expected.to have_html_escaped_body_text group.name + is_expected.to have_body_text group.name is_expected.to have_body_text group.web_url is_expected.to have_body_text group_member.invite_email end diff --git a/spec/support/matchers/email_matchers.rb b/spec/support/matchers/email_matchers.rb deleted file mode 100644 index d9d59ec12ec..00000000000 --- a/spec/support/matchers/email_matchers.rb +++ /dev/null @@ -1,5 +0,0 @@ -RSpec::Matchers.define :have_html_escaped_body_text do |expected| - match do |actual| - expect(actual).to have_body_text(ERB::Util.html_escape(expected)) - end -end -- cgit v1.2.1 From d46c12290d74e9faac888bf5de5cc1d84a00b675 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sun, 3 Jun 2018 22:22:50 +0900 Subject: Replace have_html_espaced_body_text after 517598ba --- spec/mailers/notify_spec.rb | 38 +++++++++++----------- .../shared_examples/notify_shared_examples.rb | 4 +-- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index ecd3d309327..775ca4ba0eb 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -68,7 +68,7 @@ describe Notify do end it 'contains the description' do - is_expected.to have_html_escaped_body_text issue.description + is_expected.to have_body_text issue.description end it 'does not add a reason header' do @@ -89,7 +89,7 @@ describe Notify do end it 'contains a link to note author' do - is_expected.to have_html_escaped_body_text(issue.author_name) + is_expected.to have_body_text(issue.author_name) is_expected.to have_body_text 'created an issue:' end end @@ -115,8 +115,8 @@ describe Notify do it 'has the correct subject and body' do aggregate_failures do is_expected.to have_referable_subject(issue, reply: true) - is_expected.to have_html_escaped_body_text(previous_assignee.name) - is_expected.to have_html_escaped_body_text(assignee.name) + is_expected.to have_body_text(previous_assignee.name) + is_expected.to have_body_text(assignee.name) is_expected.to have_body_text(project_issue_path(project, issue)) end end @@ -190,7 +190,7 @@ describe Notify do aggregate_failures do is_expected.to have_referable_subject(issue, reply: true) is_expected.to have_body_text(status) - is_expected.to have_html_escaped_body_text(current_user.name) + is_expected.to have_body_text(current_user.name) is_expected.to have_body_text(project_issue_path project, issue) end end @@ -243,7 +243,7 @@ describe Notify do end it 'contains the description' do - is_expected.to have_html_escaped_body_text merge_request.description + is_expected.to have_body_text merge_request.description end context 'when sent with a reason' do @@ -260,7 +260,7 @@ describe Notify do end it 'contains a link to note author' do - is_expected.to have_html_escaped_body_text merge_request.author_name + is_expected.to have_body_text merge_request.author_name is_expected.to have_body_text 'created a merge request:' end end @@ -286,9 +286,9 @@ describe Notify do it 'has the correct subject and body' do aggregate_failures do is_expected.to have_referable_subject(merge_request, reply: true) - is_expected.to have_html_escaped_body_text(previous_assignee.name) + is_expected.to have_body_text(previous_assignee.name) is_expected.to have_body_text(project_merge_request_path(project, merge_request)) - is_expected.to have_html_escaped_body_text(assignee.name) + is_expected.to have_body_text(assignee.name) end end @@ -358,7 +358,7 @@ describe Notify do aggregate_failures do is_expected.to have_referable_subject(merge_request, reply: true) is_expected.to have_body_text(status) - is_expected.to have_html_escaped_body_text(current_user.name) + is_expected.to have_body_text(current_user.name) is_expected.to have_body_text(project_merge_request_path(project, merge_request)) end end @@ -526,7 +526,7 @@ describe Notify do it 'has the correct subject and body' do is_expected.to have_referable_subject(project_snippet, reply: true) - is_expected.to have_html_escaped_body_text project_snippet_note.note + is_expected.to have_body_text project_snippet_note.note end end @@ -539,7 +539,7 @@ describe Notify do it 'has the correct subject and body' do is_expected.to have_subject("#{project.name} | Project was moved") - is_expected.to have_html_escaped_body_text project.full_name + is_expected.to have_body_text project.full_name is_expected.to have_body_text(project.ssh_url_to_repo) end end @@ -566,7 +566,7 @@ describe Notify do expect(to_emails).to eq([recipient.notification_email]) is_expected.to have_subject "Request to join the #{project.full_name} project" - is_expected.to have_html_escaped_body_text project.full_name + is_expected.to have_body_text project.full_name is_expected.to have_body_text project_project_members_url(project) is_expected.to have_body_text project_member.human_access end @@ -586,7 +586,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject "Access to the #{project.full_name} project was denied" - is_expected.to have_html_escaped_body_text project.full_name + is_expected.to have_body_text project.full_name is_expected.to have_body_text project.web_url end end @@ -603,7 +603,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject "Access to the #{project.full_name} project was granted" - is_expected.to have_html_escaped_body_text project.full_name + is_expected.to have_body_text project.full_name is_expected.to have_body_text project.web_url is_expected.to have_body_text project_member.human_access end @@ -633,7 +633,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject "Invitation to join the #{project.full_name} project" - is_expected.to have_html_escaped_body_text project.full_name + is_expected.to have_body_text project.full_name is_expected.to have_body_text project.full_name is_expected.to have_body_text project_member.human_access is_expected.to have_body_text project_member.invite_token @@ -657,7 +657,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject 'Invitation accepted' - is_expected.to have_html_escaped_body_text project.full_name + is_expected.to have_body_text project.full_name is_expected.to have_body_text project.web_url is_expected.to have_body_text project_member.invite_email is_expected.to have_body_text invited_user.name @@ -680,7 +680,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject 'Invitation declined' - is_expected.to have_html_escaped_body_text project.full_name + is_expected.to have_body_text project.full_name is_expected.to have_body_text project.web_url is_expected.to have_body_text project_member.invite_email end @@ -1396,7 +1396,7 @@ describe Notify do it 'has the correct subject and body' do is_expected.to have_referable_subject(personal_snippet, reply: true) - is_expected.to have_html_escaped_body_text personal_snippet_note.note + is_expected.to have_body_text personal_snippet_note.note end end end diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb index 43fdaddf545..d176d3fa425 100644 --- a/spec/support/shared_examples/notify_shared_examples.rb +++ b/spec/support/shared_examples/notify_shared_examples.rb @@ -212,7 +212,7 @@ shared_examples 'a note email' do end it 'contains the message from the note' do - is_expected.to have_html_escaped_body_text note.note + is_expected.to have_body_text note.note end it 'does not contain note author' do @@ -225,7 +225,7 @@ shared_examples 'a note email' do end it 'contains a link to note author' do - is_expected.to have_html_escaped_body_text note.author_name + is_expected.to have_body_text note.author_name end end end -- cgit v1.2.1 From 3f55e0b29fe39c5fd31c703911c96808bd17335d Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sun, 3 Jun 2018 23:45:26 +0900 Subject: Use the default strings of timeago.js for timeago --- spec/features/admin/admin_uses_repository_checks_spec.rb | 2 +- spec/features/merge_request/user_posts_notes_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 90cf5a53787..7371a494d36 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -28,7 +28,7 @@ feature 'Admin uses repository checks' do visit_admin_project_page(project) page.within('.alert') do - expect(page.text).to match(/Last repository check \(.* ago\) failed/) + expect(page.text).to match(/Last repository check \(just now\) failed/) end end diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index b54addce993..3bd9f5e2298 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -139,7 +139,7 @@ describe 'Merge request > User posts notes', :js do page.within("#note_#{note.id}") do is_expected.to have_css('.note_edited_ago') expect(find('.note_edited_ago').text) - .to match(/less than a minute ago/) + .to match(/just now/) end end end -- cgit v1.2.1 From eb05d475b7e82b943f5a72b8adf41b9bce519382 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Jun 2018 12:12:02 +0900 Subject: Fix wording in spec. Add PIPELINE_IID in examples of debugged variables in documants. --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index e1766fc0ec9..c5a4d9b4778 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -143,7 +143,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do it 'wastes pipeline iid' do expect { step.perform! }.to raise_error - expect(InternalId.ci_pipelines.where(project_id: project.id).exists?).to be_truthy + expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0 end end end @@ -157,7 +157,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do end context 'when variables policy is specified' do - shared_examples_for 'populates pipeline according to used policies' do + shared_examples_for 'a correct pipeline' do it 'populates pipeline according to used policies' do step.perform! @@ -177,7 +177,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do build(:ci_pipeline, ref: 'master', config: config) end - it_behaves_like 'populates pipeline according to used policies' + it_behaves_like 'a correct pipeline' context 'when variables expression is specified' do context 'when pipeline iid is the subject' do @@ -186,7 +186,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do prod: { script: 'cap prod', only: { variables: ["$CI_PIPELINE_IID == '1000'"] } } } end - it_behaves_like 'populates pipeline according to used policies' + it_behaves_like 'a correct pipeline' end end end -- cgit v1.2.1 From 89b4304f12cd37d8715c274cdee080e95f2d3bad Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 29 May 2018 17:06:14 +0900 Subject: Add background migrations to arhive legacy traces --- .../archive_legacy_traces_spec.rb | 60 ++++++++++++++++++++++ spec/migrations/archive_legacy_traces_spec.rb | 45 ++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb create mode 100644 spec/migrations/archive_legacy_traces_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb new file mode 100644 index 00000000000..ecc6eea9284 --- /dev/null +++ b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, :migration, schema: 20180529152628 do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:builds) { table(:ci_builds) } + let(:job_artifacts) { table(:ci_job_artifacts) } + + before do + namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1') + projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123) + build = builds.create!(id: 1, project_id: 123, status: 'success') + + @legacy_trace_dir = File.join(Settings.gitlab_ci.builds_path, + build.created_at.utc.strftime("%Y_%m"), + build.project_id.to_s) + + FileUtils.mkdir_p(@legacy_trace_dir) + + @legacy_trace_path = File.join(@legacy_trace_dir, "#{build.id}.log") + end + + context 'when trace file exsits at the right place' do + before do + File.open(@legacy_trace_path, 'wb') { |stream| stream.write('aiueo') } + end + + it 'correctly archive legacy traces' do + expect(job_artifacts.count).to eq(0) + expect(File.exist?(@legacy_trace_path)).to be_truthy + + described_class.new.perform(1, 1) + + expect(job_artifacts.count).to eq(1) + expect(File.exist?(@legacy_trace_path)).to be_falsy + expect(File.read(new_trace_path)).to eq('aiueo') + end + end + + context 'when trace file does not exsits at the right place' do + it 'correctly archive legacy traces' do + expect(job_artifacts.count).to eq(0) + expect(File.exist?(@legacy_trace_path)).to be_falsy + + described_class.new.perform(1, 1) + + expect(job_artifacts.count).to eq(0) + end + end + + def new_trace_path + job_artifact = job_artifacts.first + + disk_hash = Digest::SHA2.hexdigest(job_artifact.project_id.to_s) + creation_date = job_artifact.created_at.utc.strftime('%Y_%m_%d') + + File.join(Gitlab.config.artifacts.path, disk_hash[0..1], disk_hash[2..3], disk_hash, + creation_date, job_artifact.job_id.to_s, job_artifact.id.to_s, 'job.log') + end +end diff --git a/spec/migrations/archive_legacy_traces_spec.rb b/spec/migrations/archive_legacy_traces_spec.rb new file mode 100644 index 00000000000..fc61c4bec17 --- /dev/null +++ b/spec/migrations/archive_legacy_traces_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20180529152628_archive_legacy_traces') + +describe ArchiveLegacyTraces, :migration do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:builds) { table(:ci_builds) } + let(:job_artifacts) { table(:ci_job_artifacts) } + + before do + namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1') + projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123) + build = builds.create!(id: 1) + + @legacy_trace_path = File.join( + Settings.gitlab_ci.builds_path, + build.created_at.utc.strftime("%Y_%m"), + build.project_id.to_s, + "#{job.id}.log" + ) + + File.open(@legacy_trace_path, 'wb') { |stream| stream.write('aiueo') } + end + + it 'correctly archive legacy traces' do + expect(job_artifacts.count).to eq(0) + expect(File.exist?(@legacy_trace_path)).to be_truthy + + migrate! + + expect(job_artifacts.count).to eq(1) + expect(File.exist?(@legacy_trace_path)).to be_falsy + expect(File.exist?(new_trace_path)).to be_truthy + end + + def new_trace_path + job_artifact = job_artifacts.first + + disk_hash = Digest::SHA2.hexdigest(job_artifact.project_id.to_s) + creation_date = job_artifact.created_at.utc.strftime('%Y_%m_%d') + + File.join(disk_hash[0..1], disk_hash[2..3], disk_hash, + creation_date, job_artifact.job_id.to_s, job_artifact.id.to_s) + end +end -- cgit v1.2.1 From 0d00d02e842a0c4b22d213e00143a08d97597000 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sun, 3 Jun 2018 14:21:50 +0900 Subject: Directly refer application code from migration code --- spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb index ecc6eea9284..1a62b30ce81 100644 --- a/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb +++ b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb @@ -14,7 +14,7 @@ describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, :migration, schema: 2 @legacy_trace_dir = File.join(Settings.gitlab_ci.builds_path, build.created_at.utc.strftime("%Y_%m"), build.project_id.to_s) - + FileUtils.mkdir_p(@legacy_trace_dir) @legacy_trace_path = File.join(@legacy_trace_dir, "#{build.id}.log") -- cgit v1.2.1 From bcd664f53a4009bc752fbc47e1c4d6f76c0b8cc2 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sun, 3 Jun 2018 15:04:47 +0900 Subject: Fix specs. Rename migration file name which was conflicted with background migration's. --- .../archive_legacy_traces_spec.rb | 35 ++++------------- spec/migrations/archive_legacy_traces_spec.rb | 45 ---------------------- .../schedule_to_archive_legacy_traces_spec.rb | 45 ++++++++++++++++++++++ spec/support/trace/trace_helpers.rb | 23 +++++++++++ 4 files changed, 76 insertions(+), 72 deletions(-) delete mode 100644 spec/migrations/archive_legacy_traces_spec.rb create mode 100644 spec/migrations/schedule_to_archive_legacy_traces_spec.rb create mode 100644 spec/support/trace/trace_helpers.rb (limited to 'spec') diff --git a/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb index 1a62b30ce81..0765f4149f9 100644 --- a/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb +++ b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, :migration, schema: 20180529152628 do + include TraceHelpers + let(:namespaces) { table(:namespaces) } let(:projects) { table(:projects) } let(:builds) { table(:ci_builds) } @@ -9,52 +11,31 @@ describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, :migration, schema: 2 before do namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1') projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123) - build = builds.create!(id: 1, project_id: 123, status: 'success') - - @legacy_trace_dir = File.join(Settings.gitlab_ci.builds_path, - build.created_at.utc.strftime("%Y_%m"), - build.project_id.to_s) - - FileUtils.mkdir_p(@legacy_trace_dir) - - @legacy_trace_path = File.join(@legacy_trace_dir, "#{build.id}.log") + @build = builds.create!(id: 1, project_id: 123, status: 'success', type: 'Ci::Build') end context 'when trace file exsits at the right place' do before do - File.open(@legacy_trace_path, 'wb') { |stream| stream.write('aiueo') } + create_legacy_trace(@build, 'aiueo') end it 'correctly archive legacy traces' do expect(job_artifacts.count).to eq(0) - expect(File.exist?(@legacy_trace_path)).to be_truthy + expect(File.exist?(legacy_trace_path(@build))).to be_truthy described_class.new.perform(1, 1) expect(job_artifacts.count).to eq(1) - expect(File.exist?(@legacy_trace_path)).to be_falsy - expect(File.read(new_trace_path)).to eq('aiueo') + expect(File.exist?(legacy_trace_path(@build))).to be_falsy + expect(File.read(archived_trace_path(job_artifacts.first))).to eq('aiueo') end end context 'when trace file does not exsits at the right place' do - it 'correctly archive legacy traces' do - expect(job_artifacts.count).to eq(0) - expect(File.exist?(@legacy_trace_path)).to be_falsy - + it 'does not raise errors and create job artifact row' do described_class.new.perform(1, 1) expect(job_artifacts.count).to eq(0) end end - - def new_trace_path - job_artifact = job_artifacts.first - - disk_hash = Digest::SHA2.hexdigest(job_artifact.project_id.to_s) - creation_date = job_artifact.created_at.utc.strftime('%Y_%m_%d') - - File.join(Gitlab.config.artifacts.path, disk_hash[0..1], disk_hash[2..3], disk_hash, - creation_date, job_artifact.job_id.to_s, job_artifact.id.to_s, 'job.log') - end end diff --git a/spec/migrations/archive_legacy_traces_spec.rb b/spec/migrations/archive_legacy_traces_spec.rb deleted file mode 100644 index fc61c4bec17..00000000000 --- a/spec/migrations/archive_legacy_traces_spec.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'spec_helper' -require Rails.root.join('db', 'post_migrate', '20180529152628_archive_legacy_traces') - -describe ArchiveLegacyTraces, :migration do - let(:namespaces) { table(:namespaces) } - let(:projects) { table(:projects) } - let(:builds) { table(:ci_builds) } - let(:job_artifacts) { table(:ci_job_artifacts) } - - before do - namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1') - projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123) - build = builds.create!(id: 1) - - @legacy_trace_path = File.join( - Settings.gitlab_ci.builds_path, - build.created_at.utc.strftime("%Y_%m"), - build.project_id.to_s, - "#{job.id}.log" - ) - - File.open(@legacy_trace_path, 'wb') { |stream| stream.write('aiueo') } - end - - it 'correctly archive legacy traces' do - expect(job_artifacts.count).to eq(0) - expect(File.exist?(@legacy_trace_path)).to be_truthy - - migrate! - - expect(job_artifacts.count).to eq(1) - expect(File.exist?(@legacy_trace_path)).to be_falsy - expect(File.exist?(new_trace_path)).to be_truthy - end - - def new_trace_path - job_artifact = job_artifacts.first - - disk_hash = Digest::SHA2.hexdigest(job_artifact.project_id.to_s) - creation_date = job_artifact.created_at.utc.strftime('%Y_%m_%d') - - File.join(disk_hash[0..1], disk_hash[2..3], disk_hash, - creation_date, job_artifact.job_id.to_s, job_artifact.id.to_s) - end -end diff --git a/spec/migrations/schedule_to_archive_legacy_traces_spec.rb b/spec/migrations/schedule_to_archive_legacy_traces_spec.rb new file mode 100644 index 00000000000..d3eac3c45ea --- /dev/null +++ b/spec/migrations/schedule_to_archive_legacy_traces_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20180529152628_schedule_to_archive_legacy_traces') + +describe ScheduleToArchiveLegacyTraces, :migration do + include TraceHelpers + + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:builds) { table(:ci_builds) } + let(:job_artifacts) { table(:ci_job_artifacts) } + + before do + namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1') + projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123) + @build_success = builds.create!(id: 1, project_id: 123, status: 'success', type: 'Ci::Build') + @build_failed = builds.create!(id: 2, project_id: 123, status: 'failed', type: 'Ci::Build') + @builds_canceled = builds.create!(id: 3, project_id: 123, status: 'canceled', type: 'Ci::Build') + @build_running = builds.create!(id: 4, project_id: 123, status: 'running', type: 'Ci::Build') + + create_legacy_trace(@build_success, 'This job is done') + create_legacy_trace(@build_failed, 'This job is done') + create_legacy_trace(@builds_canceled, 'This job is done') + create_legacy_trace(@build_running, 'This job is not done yet') + end + + it 'correctly archive legacy traces' do + expect(job_artifacts.count).to eq(0) + expect(File.exist?(legacy_trace_path(@build_success))).to be_truthy + expect(File.exist?(legacy_trace_path(@build_failed))).to be_truthy + expect(File.exist?(legacy_trace_path(@builds_canceled))).to be_truthy + expect(File.exist?(legacy_trace_path(@build_running))).to be_truthy + + migrate! + + expect(job_artifacts.count).to eq(3) + expect(File.exist?(legacy_trace_path(@build_success))).to be_falsy + expect(File.exist?(legacy_trace_path(@build_failed))).to be_falsy + expect(File.exist?(legacy_trace_path(@builds_canceled))).to be_falsy + expect(File.exist?(legacy_trace_path(@build_running))).to be_truthy + expect(File.exist?(archived_trace_path(job_artifacts.where(job_id: @build_success.id).first))).to be_truthy + expect(File.exist?(archived_trace_path(job_artifacts.where(job_id: @build_failed.id).first))).to be_truthy + expect(File.exist?(archived_trace_path(job_artifacts.where(job_id: @builds_canceled.id).first))).to be_truthy + expect(job_artifacts.where(job_id: @build_running.id)).not_to be_exist + end +end diff --git a/spec/support/trace/trace_helpers.rb b/spec/support/trace/trace_helpers.rb new file mode 100644 index 00000000000..f6d11b61038 --- /dev/null +++ b/spec/support/trace/trace_helpers.rb @@ -0,0 +1,23 @@ +module TraceHelpers + def create_legacy_trace(build, content) + File.open(legacy_trace_path(build), 'wb') { |stream| stream.write(content) } + end + + def legacy_trace_path(build) + legacy_trace_dir = File.join(Settings.gitlab_ci.builds_path, + build.created_at.utc.strftime("%Y_%m"), + build.project_id.to_s) + + FileUtils.mkdir_p(legacy_trace_dir) + + File.join(legacy_trace_dir, "#{build.id}.log") + end + + def archived_trace_path(job_artifact) + disk_hash = Digest::SHA2.hexdigest(job_artifact.project_id.to_s) + creation_date = job_artifact.created_at.utc.strftime('%Y_%m_%d') + + File.join(Gitlab.config.artifacts.path, disk_hash[0..1], disk_hash[2..3], disk_hash, + creation_date, job_artifact.job_id.to_s, job_artifact.id.to_s, 'job.log') + end +end -- cgit v1.2.1 From 8f1f73d4e3ca82e3d449e478606f133d19ead7b1 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Jun 2018 14:28:21 +0900 Subject: Fix typo in spec. Add a test for the case of when trace is stored in database --- .../archive_legacy_traces_spec.rb | 26 ++++++++++++++++++---- spec/support/trace/trace_helpers.rb | 4 ++++ 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb index 0765f4149f9..877c061d11b 100644 --- a/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb +++ b/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, :migration, schema: 2 context 'when trace file exsits at the right place' do before do - create_legacy_trace(@build, 'aiueo') + create_legacy_trace(@build, 'trace in file') end it 'correctly archive legacy traces' do @@ -27,15 +27,33 @@ describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, :migration, schema: 2 expect(job_artifacts.count).to eq(1) expect(File.exist?(legacy_trace_path(@build))).to be_falsy - expect(File.read(archived_trace_path(job_artifacts.first))).to eq('aiueo') + expect(File.read(archived_trace_path(job_artifacts.first))).to eq('trace in file') end end context 'when trace file does not exsits at the right place' do - it 'does not raise errors and create job artifact row' do - described_class.new.perform(1, 1) + it 'does not raise errors nor create job artifact' do + expect { described_class.new.perform(1, 1) }.not_to raise_error expect(job_artifacts.count).to eq(0) end end + + context 'when trace data exsits in database' do + before do + create_legacy_trace_in_db(@build, 'trace in db') + end + + it 'correctly archive legacy traces' do + expect(job_artifacts.count).to eq(0) + expect(@build.read_attribute(:trace)).not_to be_empty + + described_class.new.perform(1, 1) + + @build.reload + expect(job_artifacts.count).to eq(1) + expect(@build.read_attribute(:trace)).to be_nil + expect(File.read(archived_trace_path(job_artifacts.first))).to eq('trace in db') + end + end end diff --git a/spec/support/trace/trace_helpers.rb b/spec/support/trace/trace_helpers.rb index f6d11b61038..c7802bbcb94 100644 --- a/spec/support/trace/trace_helpers.rb +++ b/spec/support/trace/trace_helpers.rb @@ -3,6 +3,10 @@ module TraceHelpers File.open(legacy_trace_path(build), 'wb') { |stream| stream.write(content) } end + def create_legacy_trace_in_db(build, content) + build.update_column(:trace, content) + end + def legacy_trace_path(build) legacy_trace_dir = File.join(Settings.gitlab_ci.builds_path, build.created_at.utc.strftime("%Y_%m"), -- cgit v1.2.1 From 114c26ccf0f10788271c6108774e72809a7f93e1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 4 Jun 2018 11:29:39 +0200 Subject: Raise error if pipeline / stage hits unknown status --- spec/models/ci/pipeline_spec.rb | 37 +++++++++++++++++++++++++++++++++++++ spec/models/ci/stage_spec.rb | 13 +++++++++++++ 2 files changed, 50 insertions(+) (limited to 'spec') diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index a29fa0c4cae..decfef6c8cc 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1251,6 +1251,43 @@ describe Ci::Pipeline, :mailer do end end + describe '#update_status' do + context 'when pipeline is empty' do + it 'updates does not change pipeline status' do + expect(pipeline.statuses.latest.status).to be_nil + + expect { pipeline.update_status } + .to change { pipeline.reload.status }.to 'skipped' + end + end + + context 'when updating status to pending' do + before do + allow(pipeline) + .to receive_message_chain(:statuses, :latest, :status) + .and_return(:running) + end + + it 'updates pipeline status to running' do + expect { pipeline.update_status } + .to change { pipeline.reload.status }.to 'running' + end + end + + context 'when statuses status was not recognized' do + before do + allow(pipeline) + .to receive(:latest_builds_status) + .and_return(:unknown) + end + + it 'raises an exception' do + expect { pipeline.update_status } + .to raise_error(HasStatus::UnknownStatusError) + end + end + end + describe '#detailed_status' do subject { pipeline.detailed_status(user) } diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb index b40496252b4..22a4556c10c 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/stage_spec.rb @@ -110,6 +110,19 @@ describe Ci::Stage, :models do expect(stage.reload).to be_failed end end + + context 'when statuses status was not recognized' do + before do + allow(stage) + .to receive_message_chain(:statuses, :latest, :status) + .and_return(:unknown) + end + + it 'raises an exception' do + expect { stage.update_status } + .to raise_error(HasStatus::UnknownStatusError) + end + end end describe '#detailed_status' do -- cgit v1.2.1 From b8370c9f55843351b49073dafe84a2e9858c8c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Wed, 9 May 2018 17:27:38 +0200 Subject: Support presigned multipart uploads --- .../artifacts_direct_upload_support_spec.rb | 71 -------- spec/initializers/direct_upload_support_spec.rb | 89 ++++++++++ spec/lib/object_storage/direct_upload_spec.rb | 188 +++++++++++++++++++++ spec/requests/api/runner_spec.rb | 1 + spec/requests/lfs_http_spec.rb | 1 + spec/support/helpers/stub_object_storage.rb | 12 ++ spec/uploaders/object_storage_spec.rb | 134 ++++++++++++--- 7 files changed, 398 insertions(+), 98 deletions(-) delete mode 100644 spec/initializers/artifacts_direct_upload_support_spec.rb create mode 100644 spec/initializers/direct_upload_support_spec.rb create mode 100644 spec/lib/object_storage/direct_upload_spec.rb (limited to 'spec') diff --git a/spec/initializers/artifacts_direct_upload_support_spec.rb b/spec/initializers/artifacts_direct_upload_support_spec.rb deleted file mode 100644 index bfb71da3388..00000000000 --- a/spec/initializers/artifacts_direct_upload_support_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -require 'spec_helper' - -describe 'Artifacts direct upload support' do - subject do - load Rails.root.join('config/initializers/artifacts_direct_upload_support.rb') - end - - let(:connection) do - { provider: provider } - end - - before do - stub_artifacts_setting( - object_store: { - enabled: enabled, - direct_upload: direct_upload, - connection: connection - }) - end - - context 'when object storage is enabled' do - let(:enabled) { true } - - context 'when direct upload is enabled' do - let(:direct_upload) { true } - - context 'when provider is Google' do - let(:provider) { 'Google' } - - it 'succeeds' do - expect { subject }.not_to raise_error - end - end - - context 'when connection is empty' do - let(:connection) { nil } - - it 'raises an error' do - expect { subject }.to raise_error /object storage provider when 'direct_upload' of artifacts is used/ - end - end - - context 'when other provider is used' do - let(:provider) { 'AWS' } - - it 'raises an error' do - expect { subject }.to raise_error /object storage provider when 'direct_upload' of artifacts is used/ - end - end - end - - context 'when direct upload is disabled' do - let(:direct_upload) { false } - let(:provider) { 'AWS' } - - it 'succeeds' do - expect { subject }.not_to raise_error - end - end - end - - context 'when object storage is disabled' do - let(:enabled) { false } - let(:direct_upload) { false } - let(:provider) { 'AWS' } - - it 'succeeds' do - expect { subject }.not_to raise_error - end - end -end diff --git a/spec/initializers/direct_upload_support_spec.rb b/spec/initializers/direct_upload_support_spec.rb new file mode 100644 index 00000000000..f124e726bac --- /dev/null +++ b/spec/initializers/direct_upload_support_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe 'Direct upload support' do + subject do + load Rails.root.join('config/initializers/direct_upload_support.rb') + end + + where(:config_name) do + ['lfs', 'artifacts', 'uploads'] + end + + with_them do + let(:connection) do + { provider: provider } + end + + let(:object_store) do + { + enabled: enabled, + direct_upload: direct_upload, + connection: connection + } + end + + before do + allow(Gitlab.config).to receive_messages(to_settings( + config_name => { object_store: object_store })) + end + + context 'when object storage is enabled' do + let(:enabled) { true } + + context 'when direct upload is enabled' do + let(:direct_upload) { true } + + context 'when provider is AWS' do + let(:provider) { 'AWS' } + + it 'succeeds' do + expect { subject }.not_to raise_error + end + end + + context 'when provider is Google' do + let(:provider) { 'Google' } + + it 'succeeds' do + expect { subject }.not_to raise_error + end + end + + context 'when connection is empty' do + let(:connection) { nil } + + it 'raises an error' do + expect { subject }.to raise_error /are supported as a object storage provider when 'direct_upload' is used/ + end + end + + context 'when other provider is used' do + let(:provider) { 'Rackspace' } + + it 'raises an error' do + expect { subject }.to raise_error /are supported as a object storage provider when 'direct_upload' is used/ + end + end + end + + context 'when direct upload is disabled' do + let(:direct_upload) { false } + let(:provider) { 'AWS' } + + it 'succeeds' do + expect { subject }.not_to raise_error + end + end + end + + context 'when object storage is disabled' do + let(:enabled) { false } + let(:direct_upload) { false } + let(:provider) { 'Rackspace' } + + it 'succeeds' do + expect { subject }.not_to raise_error + end + end + end +end diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb new file mode 100644 index 00000000000..0f86d10b881 --- /dev/null +++ b/spec/lib/object_storage/direct_upload_spec.rb @@ -0,0 +1,188 @@ +require 'spec_helper' + +describe ObjectStorage::DirectUpload do + let(:credentials) do + { + provider: 'AWS', + aws_access_key_id: 'AWS_ACCESS_KEY_ID', + aws_secret_access_key: 'AWS_SECRET_ACCESS_KEY' + } + end + + let(:storage_url) { 'https://uploads.s3.amazonaws.com/' } + + let(:bucket_name) { 'uploads' } + let(:object_name) { 'tmp/uploads/my-file' } + let(:maximum_size) { 1.gigabyte } + + let(:direct_upload) { described_class.new(credentials, bucket_name, object_name, has_length: has_length, maximum_size: maximum_size) } + + describe '#has_length' do + context 'is known' do + let(:has_length) { true } + let(:maximum_size) { nil } + + it "maximum size is not required" do + expect { direct_upload }.not_to raise_error + end + end + + context 'is unknown' do + let(:has_length) { false } + + context 'and maximum size is specified' do + let(:maximum_size) { 1.gigabyte } + + it "does not raise an error" do + expect { direct_upload }.not_to raise_error + end + end + + context 'and maximum size is not specified' do + let(:maximum_size) { nil } + + it "raises an error" do + expect { direct_upload }.to raise_error /maximum_size has to be specified if length is unknown/ + end + end + end + end + + describe '#to_hash' do + subject { direct_upload.to_hash } + + shared_examples 'a valid upload' do + it "returns valid structure" do + expect(subject).to have_key(:Timeout) + expect(subject[:GetURL]).to start_with(storage_url) + expect(subject[:StoreURL]).to start_with(storage_url) + expect(subject[:DeleteURL]).to start_with(storage_url) + end + end + + shared_examples 'a valid upload with multipart data' do + before do + stub_object_storage_multipart_init(storage_url, "myUpload") + end + + it_behaves_like 'a valid upload' + + it "returns valid structure" do + expect(subject).to have_key(:MultipartUpload) + expect(subject[:MultipartUpload]).to have_key(:PartSize) + expect(subject[:MultipartUpload][:PartURLs]).to all(start_with(storage_url)) + expect(subject[:MultipartUpload][:PartURLs]).to all(include('uploadId=myUpload')) + expect(subject[:MultipartUpload][:CompleteURL]).to start_with(storage_url) + expect(subject[:MultipartUpload][:CompleteURL]).to include('uploadId=myUpload') + expect(subject[:MultipartUpload][:AbortURL]).to start_with(storage_url) + expect(subject[:MultipartUpload][:AbortURL]).to include('uploadId=myUpload') + end + end + + shared_examples 'a valid upload without multipart data' do + it_behaves_like 'a valid upload' + + it "returns valid structure" do + expect(subject).not_to have_key(:MultipartUpload) + end + end + + context 'when AWS is used' do + context 'when length is known' do + let(:has_length) { true } + + it_behaves_like 'a valid upload without multipart data' + end + + context 'when length is unknown' do + let(:has_length) { false } + + it_behaves_like 'a valid upload with multipart data' do + context 'when maximum upload size is 10MB' do + let(:maximum_size) { 10.megabyte } + + it 'returns only 2 parts' do + expect(subject[:MultipartUpload][:PartURLs].length).to eq(2) + end + + it 'part size is mimimum, 5MB' do + expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte) + end + end + + context 'when maximum upload size is 12MB' do + let(:maximum_size) { 12.megabyte } + + it 'returns only 3 parts' do + expect(subject[:MultipartUpload][:PartURLs].length).to eq(3) + end + + it 'part size is rounded-up to 5MB' do + expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte) + end + end + + context 'when maximum upload size is 49GB' do + let(:maximum_size) { 49.gigabyte } + + it 'returns maximum, 100 parts' do + expect(subject[:MultipartUpload][:PartURLs].length).to eq(100) + end + + it 'part size is rounded-up to 5MB' do + expect(subject[:MultipartUpload][:PartSize]).to eq(505.megabyte) + end + end + end + end + end + + context 'when Google is used' do + let(:credentials) do + { + provider: 'Google', + google_storage_access_key_id: 'GOOGLE_ACCESS_KEY_ID', + google_storage_secret_access_key: 'GOOGLE_SECRET_ACCESS_KEY' + } + end + + let(:storage_url) { 'https://storage.googleapis.com/uploads/' } + + context 'when length is known' do + let(:has_length) { true } + + it_behaves_like 'a valid upload without multipart data' + end + + context 'when length is unknown' do + let(:has_length) { false } + + it_behaves_like 'a valid upload without multipart data' + end + end + end + + describe '#get_url' do + # this method can only be tested with integration tests + end + + describe '#delete_url' do + # this method can only be tested with integration tests + end + + describe '#store_url' do + # this method can only be tested with integration tests + end + + describe '#multipart_part_upload_url' do + # this method can only be tested with integration tests + end + + describe '#multipart_complete_url' do + # this method can only be tested with integration tests + end + + describe '#multipart_abort_url' do + # this method can only be tested with integration tests + end +end diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 319ac389083..c981a10ac38 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1101,6 +1101,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(json_response['RemoteObject']).to have_key('GetURL') expect(json_response['RemoteObject']).to have_key('StoreURL') expect(json_response['RemoteObject']).to have_key('DeleteURL') + expect(json_response['RemoteObject']).to have_key('MultipartUpload') end end diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 79672fe1cc5..4d30b99262e 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -1021,6 +1021,7 @@ describe 'Git LFS API and storage' do expect(json_response['RemoteObject']).to have_key('GetURL') expect(json_response['RemoteObject']).to have_key('StoreURL') expect(json_response['RemoteObject']).to have_key('DeleteURL') + expect(json_response['RemoteObject']).not_to have_key('MultipartUpload') expect(json_response['LfsOid']).to eq(sample_oid) expect(json_response['LfsSize']).to eq(sample_size) end diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index 19d744b959a..80204bdab8b 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -45,4 +45,16 @@ module StubObjectStorage remote_directory: 'uploads', **params) end + + def stub_object_storage_multipart_init(endpoint, upload_id = "upload_id") + stub_request(:post, %r{\A#{endpoint}tmp/uploads/[a-z0-9-]*\?uploads\z}). + to_return status: 200, body: <<-EOS.strip_heredoc + + + example-bucket + example-object + #{upload_id} + + EOS + end end diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 2dd0925a8e6..01166865e88 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -355,7 +355,10 @@ describe ObjectStorage do end describe '.workhorse_authorize' do - subject { uploader_class.workhorse_authorize } + let(:has_length) { true } + let(:maximum_size) { nil } + + subject { uploader_class.workhorse_authorize(has_length: has_length, maximum_size: maximum_size) } before do # ensure that we use regular Fog libraries @@ -371,10 +374,6 @@ describe ObjectStorage do expect(subject[:TempPath]).to start_with(uploader_class.root) expect(subject[:TempPath]).to include(described_class::TMP_UPLOAD_PATH) end - - it "does not return remote store" do - is_expected.not_to have_key('RemoteObject') - end end shared_examples 'uses remote storage' do @@ -383,7 +382,7 @@ describe ObjectStorage do expect(subject[:RemoteObject]).to have_key(:ID) expect(subject[:RemoteObject]).to include(Timeout: a_kind_of(Integer)) - expect(subject[:RemoteObject][:Timeout]).to be(ObjectStorage::DIRECT_UPLOAD_TIMEOUT) + expect(subject[:RemoteObject][:Timeout]).to be(ObjectStorage::DirectUpload::TIMEOUT) expect(subject[:RemoteObject]).to have_key(:GetURL) expect(subject[:RemoteObject]).to have_key(:DeleteURL) expect(subject[:RemoteObject]).to have_key(:StoreURL) @@ -391,9 +390,31 @@ describe ObjectStorage do expect(subject[:RemoteObject][:DeleteURL]).to include(described_class::TMP_UPLOAD_PATH) expect(subject[:RemoteObject][:StoreURL]).to include(described_class::TMP_UPLOAD_PATH) end + end - it "does not return local store" do - is_expected.not_to have_key('TempPath') + shared_examples 'uses remote storage with multipart uploads' do + it_behaves_like 'uses remote storage' do + it "returns multipart upload" do + is_expected.to have_key(:RemoteObject) + + expect(subject[:RemoteObject]).to have_key(:MultipartUpload) + expect(subject[:RemoteObject][:MultipartUpload]).to have_key(:PartSize) + expect(subject[:RemoteObject][:MultipartUpload]).to have_key(:PartURLs) + expect(subject[:RemoteObject][:MultipartUpload]).to have_key(:CompleteURL) + expect(subject[:RemoteObject][:MultipartUpload]).to have_key(:AbortURL) + expect(subject[:RemoteObject][:MultipartUpload][:PartURLs]).to all(include(described_class::TMP_UPLOAD_PATH)) + expect(subject[:RemoteObject][:MultipartUpload][:CompleteURL]).to include(described_class::TMP_UPLOAD_PATH) + expect(subject[:RemoteObject][:MultipartUpload][:AbortURL]).to include(described_class::TMP_UPLOAD_PATH) + end + end + end + + shared_examples 'uses remote storage without multipart uploads' do + it_behaves_like 'uses remote storage' do + it "does not return multipart upload" do + is_expected.to have_key(:RemoteObject) + expect(subject[:RemoteObject]).not_to have_key(:MultipartUpload) + end end end @@ -416,6 +437,8 @@ describe ObjectStorage do end context 'uses AWS' do + let(:storage_url) { "https://uploads.s3-eu-central-1.amazonaws.com/" } + before do expect(uploader_class).to receive(:object_store_credentials) do { provider: "AWS", @@ -425,18 +448,40 @@ describe ObjectStorage do end end - it_behaves_like 'uses remote storage' do - let(:storage_url) { "https://uploads.s3-eu-central-1.amazonaws.com/" } + context 'for known length' do + it_behaves_like 'uses remote storage without multipart uploads' do + it 'returns links for S3' do + expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + end + end + end + + context 'for unknown length' do + let(:has_length) { false } + let(:maximum_size) { 1.gigabyte } - it 'returns links for S3' do - expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) - expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) - expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + before do + stub_object_storage_multipart_init(storage_url) + end + + it_behaves_like 'uses remote storage with multipart uploads' do + it 'returns links for S3' do + expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:MultipartUpload][:PartURLs]).to all(start_with(storage_url)) + expect(subject[:RemoteObject][:MultipartUpload][:CompleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:MultipartUpload][:AbortURL]).to start_with(storage_url) + end end end end context 'uses Google' do + let(:storage_url) { "https://storage.googleapis.com/uploads/" } + before do expect(uploader_class).to receive(:object_store_credentials) do { provider: "Google", @@ -445,36 +490,71 @@ describe ObjectStorage do end end - it_behaves_like 'uses remote storage' do - let(:storage_url) { "https://storage.googleapis.com/uploads/" } + context 'for known length' do + it_behaves_like 'uses remote storage without multipart uploads' do + it 'returns links for Google Cloud' do + expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + end + end + end + + context 'for unknown length' do + let(:has_length) { false } + let(:maximum_size) { 1.gigabyte } - it 'returns links for Google Cloud' do - expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) - expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) - expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + it_behaves_like 'uses remote storage without multipart uploads' do + it 'returns links for Google Cloud' do + expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + end end end end context 'uses GDK/minio' do + let(:storage_url) { "http://minio:9000/uploads/" } + before do expect(uploader_class).to receive(:object_store_credentials) do { provider: "AWS", aws_access_key_id: "AWS_ACCESS_KEY_ID", aws_secret_access_key: "AWS_SECRET_ACCESS_KEY", - endpoint: 'http://127.0.0.1:9000', + endpoint: 'http://minio:9000', path_style: true, region: "gdk" } end end - it_behaves_like 'uses remote storage' do - let(:storage_url) { "http://127.0.0.1:9000/uploads/" } + context 'for known length' do + it_behaves_like 'uses remote storage without multipart uploads' do + it 'returns links for S3' do + expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + end + end + end + + context 'for unknown length' do + let(:has_length) { false } + let(:maximum_size) { 1.gigabyte } - it 'returns links for S3' do - expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) - expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) - expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + before do + stub_object_storage_multipart_init(storage_url) + end + + it_behaves_like 'uses remote storage with multipart uploads' do + it 'returns links for S3' do + expect(subject[:RemoteObject][:GetURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:DeleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:StoreURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:MultipartUpload][:PartURLs]).to all(start_with(storage_url)) + expect(subject[:RemoteObject][:MultipartUpload][:CompleteURL]).to start_with(storage_url) + expect(subject[:RemoteObject][:MultipartUpload][:AbortURL]).to start_with(storage_url) + end end end end -- cgit v1.2.1 From 7350eb1fa83662d4aaa7541acb387b3742ba9788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Mon, 4 Jun 2018 11:41:37 +0000 Subject: Add ability to search wiki titles --- spec/lib/gitlab/file_finder_spec.rb | 24 ++------ spec/lib/gitlab/project_search_results_spec.rb | 82 +++++++++++--------------- spec/lib/gitlab/wiki_file_finder_spec.rb | 20 +++++++ spec/support/shared_examples/file_finder.rb | 21 +++++++ 4 files changed, 80 insertions(+), 67 deletions(-) create mode 100644 spec/lib/gitlab/wiki_file_finder_spec.rb create mode 100644 spec/support/shared_examples/file_finder.rb (limited to 'spec') diff --git a/spec/lib/gitlab/file_finder_spec.rb b/spec/lib/gitlab/file_finder_spec.rb index 07cb10e563e..d6d9e4001a3 100644 --- a/spec/lib/gitlab/file_finder_spec.rb +++ b/spec/lib/gitlab/file_finder_spec.rb @@ -3,27 +3,11 @@ require 'spec_helper' describe Gitlab::FileFinder do describe '#find' do let(:project) { create(:project, :public, :repository) } - let(:finder) { described_class.new(project, project.default_branch) } - it 'finds by name' do - results = finder.find('files') - - filename, blob = results.find { |_, blob| blob.filename == 'files/images/wm.svg' } - expect(filename).to eq('files/images/wm.svg') - expect(blob).to be_a(Gitlab::SearchResults::FoundBlob) - expect(blob.ref).to eq(finder.ref) - expect(blob.data).not_to be_empty - end - - it 'finds by content' do - results = finder.find('files') - - filename, blob = results.find { |_, blob| blob.filename == 'CHANGELOG' } - - expect(filename).to eq('CHANGELOG') - expect(blob).to be_a(Gitlab::SearchResults::FoundBlob) - expect(blob.ref).to eq(finder.ref) - expect(blob.data).not_to be_empty + it_behaves_like 'file finder' do + subject { described_class.new(project, project.default_branch) } + let(:expected_file_by_name) { 'files/images/wm.svg' } + let(:expected_file_by_content) { 'CHANGELOG' } end end end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index e3f705d2299..50224bde722 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -22,47 +22,57 @@ describe Gitlab::ProjectSearchResults do it { expect(results.query).to eq('hello world') } end - describe 'blob search' do - let(:project) { create(:project, :public, :repository) } - - subject(:results) { described_class.new(user, project, 'files').objects('blobs') } - - context 'when repository is disabled' do - let(:project) { create(:project, :public, :repository, :repository_disabled) } + shared_examples 'general blob search' do |entity_type, blob_kind| + let(:query) { 'files' } + subject(:results) { described_class.new(user, project, query).objects(blob_type) } - it 'hides blobs from members' do + context "when #{entity_type} is disabled" do + let(:project) { disabled_project } + it "hides #{blob_kind} from members" do project.add_reporter(user) is_expected.to be_empty end - it 'hides blobs from non-members' do + it "hides #{blob_kind} from non-members" do is_expected.to be_empty end end - context 'when repository is internal' do - let(:project) { create(:project, :public, :repository, :repository_private) } + context "when #{entity_type} is internal" do + let(:project) { private_project } - it 'finds blobs for members' do + it "finds #{blob_kind} for members" do project.add_reporter(user) is_expected.not_to be_empty end - it 'hides blobs from non-members' do + it "hides #{blob_kind} from non-members" do is_expected.to be_empty end end it 'finds by name' do - expect(results.map(&:first)).to include('files/images/wm.svg') + expect(results.map(&:first)).to include(expected_file_by_name) end it 'finds by content' do - blob = results.select { |result| result.first == "CHANGELOG" }.flatten.last + blob = results.select { |result| result.first == expected_file_by_content }.flatten.last - expect(blob.filename).to eq("CHANGELOG") + expect(blob.filename).to eq(expected_file_by_content) + end + end + + describe 'blob search' do + let(:project) { create(:project, :public, :repository) } + + it_behaves_like 'general blob search', 'repository', 'blobs' do + let(:blob_type) { 'blobs' } + let(:disabled_project) { create(:project, :public, :repository, :repository_disabled) } + let(:private_project) { create(:project, :public, :repository, :repository_private) } + let(:expected_file_by_name) { 'files/images/wm.svg' } + let(:expected_file_by_content) { 'CHANGELOG' } end describe 'parsing results' do @@ -189,40 +199,18 @@ describe Gitlab::ProjectSearchResults do describe 'wiki search' do let(:project) { create(:project, :public, :wiki_repo) } let(:wiki) { build(:project_wiki, project: project) } - let!(:wiki_page) { wiki.create_page('Title', 'Content') } - - subject(:results) { described_class.new(user, project, 'Content').objects('wiki_blobs') } - - context 'when wiki is disabled' do - let(:project) { create(:project, :public, :wiki_repo, :wiki_disabled) } - it 'hides wiki blobs from members' do - project.add_reporter(user) - - is_expected.to be_empty - end - - it 'hides wiki blobs from non-members' do - is_expected.to be_empty - end - end - - context 'when wiki is internal' do - let(:project) { create(:project, :public, :wiki_repo, :wiki_private) } - - it 'finds wiki blobs for guest' do - project.add_guest(user) - - is_expected.not_to be_empty - end - - it 'hides wiki blobs from non-members' do - is_expected.to be_empty - end + before do + wiki.create_page('Files/Title', 'Content') + wiki.create_page('CHANGELOG', 'Files example') end - it 'finds by content' do - expect(results).to include("master:Title.md\x001\x00Content\n") + it_behaves_like 'general blob search', 'wiki', 'wiki blobs' do + let(:blob_type) { 'wiki_blobs' } + let(:disabled_project) { create(:project, :public, :wiki_repo, :wiki_disabled) } + let(:private_project) { create(:project, :public, :wiki_repo, :wiki_private) } + let(:expected_file_by_name) { 'Files/Title.md' } + let(:expected_file_by_content) { 'CHANGELOG.md' } end end diff --git a/spec/lib/gitlab/wiki_file_finder_spec.rb b/spec/lib/gitlab/wiki_file_finder_spec.rb new file mode 100644 index 00000000000..025d1203dc5 --- /dev/null +++ b/spec/lib/gitlab/wiki_file_finder_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::WikiFileFinder do + describe '#find' do + let(:project) { create(:project, :public, :wiki_repo) } + let(:wiki) { build(:project_wiki, project: project) } + + before do + wiki.create_page('Files/Title', 'Content') + wiki.create_page('CHANGELOG', 'Files example') + end + + it_behaves_like 'file finder' do + subject { described_class.new(project, project.wiki.default_branch) } + + let(:expected_file_by_name) { 'Files/Title.md' } + let(:expected_file_by_content) { 'CHANGELOG.md' } + end + end +end diff --git a/spec/support/shared_examples/file_finder.rb b/spec/support/shared_examples/file_finder.rb new file mode 100644 index 00000000000..ef144bdf61c --- /dev/null +++ b/spec/support/shared_examples/file_finder.rb @@ -0,0 +1,21 @@ +shared_examples 'file finder' do + let(:query) { 'files' } + let(:search_results) { subject.find(query) } + + it 'finds by name' do + filename, blob = search_results.find { |_, blob| blob.filename == expected_file_by_name } + expect(filename).to eq(expected_file_by_name) + expect(blob).to be_a(Gitlab::SearchResults::FoundBlob) + expect(blob.ref).to eq(subject.ref) + expect(blob.data).not_to be_empty + end + + it 'finds by content' do + filename, blob = search_results.find { |_, blob| blob.filename == expected_file_by_content } + + expect(filename).to eq(expected_file_by_content) + expect(blob).to be_a(Gitlab::SearchResults::FoundBlob) + expect(blob.ref).to eq(subject.ref) + expect(blob.data).not_to be_empty + end +end -- cgit v1.2.1 From 2f50b206f2921faf47637af526d810bc10ffb3ef Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 4 Jun 2018 10:41:56 +0200 Subject: Hide archived projects from `shared_projects` Since we don't show the archived projects, we shouldnot load them and pass them to the fronted to be filtered out again. --- spec/controllers/groups/shared_projects_controller_spec.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/controllers/groups/shared_projects_controller_spec.rb b/spec/controllers/groups/shared_projects_controller_spec.rb index d8fa41abb18..003c8c262e7 100644 --- a/spec/controllers/groups/shared_projects_controller_spec.rb +++ b/spec/controllers/groups/shared_projects_controller_spec.rb @@ -38,7 +38,7 @@ describe Groups::SharedProjectsController do end it 'allows filtering shared projects' do - project = create(:project, :archived, namespace: user.namespace, name: "Searching for") + project = create(:project, namespace: user.namespace, name: "Searching for") share_project(project) get_shared_projects(filter: 'search') @@ -55,5 +55,14 @@ describe Groups::SharedProjectsController do expect(json_project_ids).to eq([second_project.id, shared_project.id]) end + + it 'does not include archived projects' do + archived_project = create(:project, :archived, namespace: user.namespace) + share_project(archived_project) + + get_shared_projects + + expect(json_project_ids).to contain_exactly(shared_project.id) + end end end -- cgit v1.2.1 From fb71c46805e14419897912f6338f635a21d1d097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarka=20Kadlecov=C3=A1?= Date: Mon, 4 Jun 2018 10:38:18 +0200 Subject: Fix repository_storage spec for Rails5 --- spec/models/application_setting_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 968267a6d24..3e6656e0f12 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -110,10 +110,9 @@ describe ApplicationSetting do # Upgraded databases will have this sort of content context 'repository_storages is a String, not an Array' do before do - setting.__send__(:raw_write_attribute, :repository_storages, 'default') + described_class.where(id: setting.id).update_all(repository_storages: 'default') end - it { expect(setting.repository_storages_before_type_cast).to eq('default') } it { expect(setting.repository_storages).to eq(['default']) } end -- cgit v1.2.1 From 6426b507244b7f1cec4a7308ab29978d20c8ec98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarka=20Kadlecov=C3=A1?= Date: Fri, 1 Jun 2018 09:52:55 +0200 Subject: Include all ancestors milestones in json list & autocomplete --- .../projects/milestones_controller_spec.rb | 35 ++++++++++++++++------ .../services/projects/autocomplete_service_spec.rb | 15 ++++++++++ 2 files changed, 41 insertions(+), 9 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 548c5ef36e7..02b30f9bc6d 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -57,19 +57,36 @@ describe Projects::MilestonesController do context "as json" do let!(:group) { create(:group, :public) } let!(:group_milestone) { create(:milestone, group: group) } - let!(:group_member) { create(:group_member, group: group, user: user) } - before do - project.update(namespace: group) - get :index, namespace_id: project.namespace.id, project_id: project.id, format: :json + context 'with a single group ancestor' do + before do + project.update(namespace: group) + get :index, namespace_id: project.namespace.id, project_id: project.id, format: :json + end + + it "queries projects milestones and groups milestones" do + milestones = assigns(:milestones) + + expect(milestones.count).to eq(2) + expect(milestones).to match_array([milestone, group_milestone]) + end end - it "queries projects milestones and groups milestones" do - milestones = assigns(:milestones) + context 'with nested groups', :nested_groups do + let!(:subgroup) { create(:group, :public, parent: group) } + let!(:subgroup_milestone) { create(:milestone, group: subgroup) } + + before do + project.update(namespace: subgroup) + get :index, namespace_id: project.namespace.id, project_id: project.id, format: :json + end + + it "queries projects milestones and all ancestors milestones" do + milestones = assigns(:milestones) - expect(milestones.count).to eq(2) - expect(milestones.where(project_id: nil).first).to eq(group_milestone) - expect(milestones.where(group_id: nil).first).to eq(milestone) + expect(milestones.count).to eq(3) + expect(milestones).to match_array([milestone, group_milestone, subgroup_milestone]) + end end end end diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb index f7ff8b80bd7..6fd73a50511 100644 --- a/spec/services/projects/autocomplete_service_spec.rb +++ b/spec/services/projects/autocomplete_service_spec.rb @@ -115,5 +115,20 @@ describe Projects::AutocompleteService do expect(milestone_titles).to eq([group_milestone2.title, group_milestone1.title]) end + + context 'with nested groups', :nested_groups do + let(:subgroup) { create(:group, :public, parent: group) } + let!(:subgroup_milestone) { create(:milestone, group: subgroup) } + + before do + project.update(namespace: subgroup) + end + + it 'includes project milestones and all acestors milestones' do + expect(milestone_titles).to match_array( + [project_milestone.title, group_milestone2.title, group_milestone1.title, subgroup_milestone.title] + ) + end + end end end -- cgit v1.2.1 From 71ed7987d3a7ab16677cb2b87721f391e8daaf13 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 4 Jun 2018 16:20:01 +0200 Subject: Perform pull request IO work outside a transaction When importing a GitHub pull request we would perform all work in a single database transaction. This is less than ideal, because we perform various slow Git operations when creating a merge request. This in turn can lead to many DB connections being used, while just waiting for an IO operation to complete. To work around this, we now move most of the heavy lifting out of the database transaction. Some extra error handling is added to ensure we can resume importing a partially imported pull request, instead of just throwing an error. This commit also changes the specs for IssueImporter so they don't rely on deprecated RSpec methods. --- .../github_import/importer/issue_importer_spec.rb | 4 +- .../importer/pull_request_importer_spec.rb | 85 ++++++++++++++++++---- 2 files changed, 74 insertions(+), 15 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb index d34ca0b76b8..81fe97c1e49 100644 --- a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb @@ -180,12 +180,12 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach allow(importer.user_finder) .to receive(:user_id_for) - .ordered.with(issue.assignees[0]) + .with(issue.assignees[0]) .and_return(4) allow(importer.user_finder) .to receive(:user_id_for) - .ordered.with(issue.assignees[1]) + .with(issue.assignees[1]) .and_return(5) expect(Gitlab::Database) diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb index 35f3fdf8304..6686b7ce0b5 100644 --- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb @@ -40,13 +40,19 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi describe '#execute' do it 'imports the pull request' do + mr = double(:merge_request, id: 10) + expect(importer) .to receive(:create_merge_request) - .and_return(10) + .and_return([mr, false]) + + expect(importer) + .to receive(:insert_git_data) + .with(mr, false) expect_any_instance_of(Gitlab::GithubImport::IssuableFinder) .to receive(:cache_database_id) - .with(10) + .with(mr.id) importer.execute end @@ -99,18 +105,11 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi importer.create_merge_request end - it 'returns the ID of the created merge request' do - id = importer.create_merge_request - - expect(id).to be_a_kind_of(Numeric) - end - - it 'creates the merge request diffs' do - importer.create_merge_request - - mr = project.merge_requests.take + it 'returns the created merge request' do + mr, exists = importer.create_merge_request - expect(mr.merge_request_diffs.exists?).to eq(true) + expect(mr).to be_instance_of(MergeRequest) + expect(exists).to eq(false) end end @@ -217,5 +216,65 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi expect { importer.create_merge_request }.not_to raise_error end end + + context 'when the merge request already exists' do + before do + allow(importer.user_finder) + .to receive(:author_id_for) + .with(pull_request) + .and_return([user.id, true]) + + allow(importer.user_finder) + .to receive(:assignee_id_for) + .with(pull_request) + .and_return(user.id) + end + + it 'returns the existing merge request' do + mr1, exists1 = importer.create_merge_request + mr2, exists2 = importer.create_merge_request + + expect(mr2).to eq(mr1) + expect(exists1).to eq(false) + expect(exists2).to eq(true) + end + end + end + + describe '#insert_git_data' do + before do + allow(importer.milestone_finder) + .to receive(:id_for) + .with(pull_request) + .and_return(milestone.id) + + allow(importer.user_finder) + .to receive(:author_id_for) + .with(pull_request) + .and_return([user.id, true]) + + allow(importer.user_finder) + .to receive(:assignee_id_for) + .with(pull_request) + .and_return(user.id) + end + + it 'creates the merge request diffs' do + mr, exists = importer.create_merge_request + + importer.insert_git_data(mr, exists) + + expect(mr.merge_request_diffs.exists?).to eq(true) + end + + it 'creates the merge request diff commits' do + mr, exists = importer.create_merge_request + + importer.insert_git_data(mr, exists) + + diff = mr.merge_request_diffs.take + + expect(diff.merge_request_diff_commits.exists?).to eq(true) + end end end -- cgit v1.2.1 From f12ee2a2f490e6d126ac6345a5ad7cbf12833791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Mon, 4 Jun 2018 21:34:11 +0200 Subject: Remove unused running_or_pending_build_count --- spec/features/projects/jobs/user_browses_job_spec.rb | 2 -- 1 file changed, 2 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index bff5bbe99af..ce0b38b7239 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -32,8 +32,6 @@ describe 'User browses a job', :js do page.within('.erased') do expect(page).to have_content('Job has been erased') end - - expect(build.project.running_or_pending_build_count).to eq(build.project.builds.running_or_pending.count(:all)) end context 'with a failed job' do -- cgit v1.2.1 From 285ffb223896f2226531be2ba10933414194155c Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Fri, 25 May 2018 16:17:57 -0700 Subject: Messaging on terms page when user already accepted We show a blue flash banner if the user already accepted, and show a button allowing them to continue to the application. --- spec/controllers/users/terms_controller_spec.rb | 22 +++++++++++++-- spec/factories/term_agreements.rb | 8 ++++++ spec/features/users/terms_spec.rb | 16 +++++++++++ spec/models/application_setting/term_spec.rb | 37 +++++++++++++++++++++++++ spec/models/term_agreement_spec.rb | 9 ++++++ 5 files changed, 89 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/controllers/users/terms_controller_spec.rb b/spec/controllers/users/terms_controller_spec.rb index a744463413c..0d77e91a67d 100644 --- a/spec/controllers/users/terms_controller_spec.rb +++ b/spec/controllers/users/terms_controller_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe Users::TermsController do + include TermsHelper let(:user) { create(:user) } let(:term) { create(:term) } @@ -15,10 +16,25 @@ describe Users::TermsController do expect(response).to have_gitlab_http_status(:redirect) end - it 'shows terms when they exist' do - term + context 'when terms exist' do + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + term + end + + it 'shows terms when they exist' do + get :index + + expect(response).to have_gitlab_http_status(:success) + end - expect(response).to have_gitlab_http_status(:success) + it 'shows a message when the user already accepted the terms' do + accept_terms(user) + + get :index + + expect(controller).to set_flash.now[:notice].to(/already accepted/) + end end end diff --git a/spec/factories/term_agreements.rb b/spec/factories/term_agreements.rb index 557599e663d..3c4eebd0196 100644 --- a/spec/factories/term_agreements.rb +++ b/spec/factories/term_agreements.rb @@ -3,4 +3,12 @@ FactoryBot.define do term user end + + trait :declined do + accepted false + end + + trait :accepted do + accepted true + end end diff --git a/spec/features/users/terms_spec.rb b/spec/features/users/terms_spec.rb index 1efa5cd5490..af407c52917 100644 --- a/spec/features/users/terms_spec.rb +++ b/spec/features/users/terms_spec.rb @@ -39,6 +39,22 @@ describe 'Users > Terms' do end end + context 'when the user has already accepted the terms' do + before do + accept_terms(user) + end + + it 'allows the user to continue to the app' do + visit terms_path + + expect(page).to have_content "You have already accepted the Terms of Service as #{user.to_reference}" + + click_link 'Continue' + + expect(current_path).to eq(root_path) + end + end + context 'terms were enforced while session is active', :js do let(:project) { create(:project) } diff --git a/spec/models/application_setting/term_spec.rb b/spec/models/application_setting/term_spec.rb index 1eddf3c56ff..aa49594f4d1 100644 --- a/spec/models/application_setting/term_spec.rb +++ b/spec/models/application_setting/term_spec.rb @@ -12,4 +12,41 @@ describe ApplicationSetting::Term do expect(described_class.latest).to eq(terms) end end + + describe '#accepted_by_user?' do + let(:user) { create(:user) } + let(:term) { create(:term) } + + it 'is true when the user accepted the terms' do + accept_terms(term, user) + + expect(term.accepted_by_user?(user)).to be(true) + end + + it 'is false when the user declined the terms' do + decline_terms(term, user) + + expect(term.accepted_by_user?(user)).to be(false) + end + + it 'does not cause a query when the user accepted the current terms' do + accept_terms(term, user) + + expect { term.accepted_by_user?(user) }.not_to exceed_query_limit(0) + end + + it 'returns false if the currently accepted terms are different' do + accept_terms(create(:term), user) + + expect(term.accepted_by_user?(user)).to be(false) + end + + def accept_terms(term, user) + Users::RespondToTermsService.new(user, term).execute(accepted: true) + end + + def decline_terms(term, user) + Users::RespondToTermsService.new(user, term).execute(accepted: false) + end + end end diff --git a/spec/models/term_agreement_spec.rb b/spec/models/term_agreement_spec.rb index a59bf119692..950dfa09a6a 100644 --- a/spec/models/term_agreement_spec.rb +++ b/spec/models/term_agreement_spec.rb @@ -5,4 +5,13 @@ describe TermAgreement do it { is_expected.to validate_presence_of(:term) } it { is_expected.to validate_presence_of(:user) } end + + describe '.accepted' do + it 'only includes accepted terms' do + accepted = create(:term_agreement, :accepted) + create(:term_agreement, :declined) + + expect(described_class.accepted).to contain_exactly(accepted) + end + end end -- cgit v1.2.1 From eea26a93e7d4ac0c6fefe46592c9baa0d3e6a5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Mon, 4 Jun 2018 14:06:07 +0200 Subject: Update validator --- spec/initializers/direct_upload_support_spec.rb | 7 ++++--- spec/lib/object_storage/direct_upload_spec.rb | 24 ------------------------ spec/support/helpers/stub_object_storage.rb | 4 ++-- 3 files changed, 6 insertions(+), 29 deletions(-) (limited to 'spec') diff --git a/spec/initializers/direct_upload_support_spec.rb b/spec/initializers/direct_upload_support_spec.rb index f124e726bac..e51d404e030 100644 --- a/spec/initializers/direct_upload_support_spec.rb +++ b/spec/initializers/direct_upload_support_spec.rb @@ -6,7 +6,7 @@ describe 'Direct upload support' do end where(:config_name) do - ['lfs', 'artifacts', 'uploads'] + %w(lfs artifacts uploads) end with_them do @@ -23,8 +23,9 @@ describe 'Direct upload support' do end before do - allow(Gitlab.config).to receive_messages(to_settings( - config_name => { object_store: object_store })) + allow(Gitlab.config).to receive_messages(to_settings(config_name => { + object_store: object_store + })) end context 'when object storage is enabled' do diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb index 0f86d10b881..5187821e8f4 100644 --- a/spec/lib/object_storage/direct_upload_spec.rb +++ b/spec/lib/object_storage/direct_upload_spec.rb @@ -161,28 +161,4 @@ describe ObjectStorage::DirectUpload do end end end - - describe '#get_url' do - # this method can only be tested with integration tests - end - - describe '#delete_url' do - # this method can only be tested with integration tests - end - - describe '#store_url' do - # this method can only be tested with integration tests - end - - describe '#multipart_part_upload_url' do - # this method can only be tested with integration tests - end - - describe '#multipart_complete_url' do - # this method can only be tested with integration tests - end - - describe '#multipart_abort_url' do - # this method can only be tested with integration tests - end end diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index 80204bdab8b..bceaf8277ee 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -47,8 +47,8 @@ module StubObjectStorage end def stub_object_storage_multipart_init(endpoint, upload_id = "upload_id") - stub_request(:post, %r{\A#{endpoint}tmp/uploads/[a-z0-9-]*\?uploads\z}). - to_return status: 200, body: <<-EOS.strip_heredoc + stub_request(:post, %r{\A#{endpoint}tmp/uploads/[a-z0-9-]*\?uploads\z}) + .to_return status: 200, body: <<-EOS.strip_heredoc example-bucket -- cgit v1.2.1 From c07ee6415a316a87c7afa71fa881e7cc03b97343 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 4 Jun 2018 16:14:13 -0500 Subject: Improve modal prop interface --- .../vue_shared/components/gl_modal_spec.js | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js index 85cb1b90fc6..23be8d93b81 100644 --- a/spec/javascripts/vue_shared/components/gl_modal_spec.js +++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js @@ -190,4 +190,37 @@ describe('GlModal', () => { }); }); }); + + describe('handling sizes', () => { + it('should render modal-sm', () => { + vm = mountComponent(modalComponent, { + modalSize: 'sm', + }); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-sm')).toEqual(true); + }); + + it('should render modal-lg', () => { + vm = mountComponent(modalComponent, { + modalSize: 'lg', + }); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(true); + }); + + it('should not add modal size classes when md size is passed', () => { + vm = mountComponent(modalComponent, { + modalSize: 'md', + }); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-md')).toEqual(false); + }); + + it('should not add modal size classes by default', () => { + vm = mountComponent(modalComponent, {}); + + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-sm')).toEqual(false); + expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(false); + }); + }); }); -- cgit v1.2.1 From 11a9389c4f6c72509c0c4aa3be6227a972adab78 Mon Sep 17 00:00:00 2001 From: Jose Date: Mon, 4 Jun 2018 16:39:44 -0500 Subject: fix labels showing up with no title --- spec/javascripts/labels_select_spec.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/labels_select_spec.js b/spec/javascripts/labels_select_spec.js index a2b89c0aef5..386e00bfd0c 100644 --- a/spec/javascripts/labels_select_spec.js +++ b/spec/javascripts/labels_select_spec.js @@ -40,5 +40,9 @@ describe('LabelsSelect', () => { it('generated label item template has correct label styles', () => { expect($labelEl.find('span.label').attr('style')).toBe(`background-color: ${label.color}; color: ${label.text_color};`); }); + + it('generated label item has a badge class', () => { + expect($labelEl.find('span').hasClass('badge')).toEqual(true); + }); }); }); -- cgit v1.2.1 From cbc20d2b7f8c73e2892c0c458619df2a9fe0c9ab Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 3 Jun 2018 03:31:41 -0700 Subject: Remove N+1 query for author in issues API This was being masked by the statement cache because only one author was used per issue in the test.. Also adds support for an Rspec matcher `exceed_all_query_limit`. --- spec/requests/api/issues_spec.rb | 8 ++-- spec/support/helpers/query_recorder.rb | 7 ++-- spec/support/matchers/exceed_query_limit.rb | 65 ++++++++++++++++++++++------- 3 files changed, 59 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 4181f4ebbbe..a15d60aafe0 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -630,15 +630,17 @@ describe API::Issues do end it 'avoids N+1 queries' do - control_count = ActiveRecord::QueryRecorder.new do + get api("/projects/#{project.id}/issues", user) + + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do get api("/projects/#{project.id}/issues", user) end.count - create(:issue, author: user, project: project) + create_list(:issue, 3, project: project) expect do get api("/projects/#{project.id}/issues", user) - end.not_to exceed_query_limit(control_count) + end.not_to exceed_all_query_limit(control_count) end it 'returns 404 when project does not exist' do diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb index 28536bbef5e..7ce63375d34 100644 --- a/spec/support/helpers/query_recorder.rb +++ b/spec/support/helpers/query_recorder.rb @@ -1,10 +1,11 @@ module ActiveRecord class QueryRecorder - attr_reader :log, :cached + attr_reader :log, :skip_cached, :cached - def initialize(&block) + def initialize(skip_cached: true, &block) @log = [] @cached = [] + @skip_cached = skip_cached ActiveSupport::Notifications.subscribed(method(:callback), 'sql.active_record', &block) end @@ -16,7 +17,7 @@ module ActiveRecord def callback(name, start, finish, message_id, values) show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG'] - if values[:name]&.include?("CACHE") + if values[:name]&.include?("CACHE") && skip_cached @cached << values[:sql] elsif !values[:name]&.include?("SCHEMA") @log << values[:sql] diff --git a/spec/support/matchers/exceed_query_limit.rb b/spec/support/matchers/exceed_query_limit.rb index 88d22a3ddd9..cd042401f3a 100644 --- a/spec/support/matchers/exceed_query_limit.rb +++ b/spec/support/matchers/exceed_query_limit.rb @@ -1,17 +1,4 @@ -RSpec::Matchers.define :exceed_query_limit do |expected| - supports_block_expectations - - match do |block| - @subject_block = block - actual_count > expected_count + threshold - end - - failure_message_when_negated do |actual| - threshold_message = threshold > 0 ? " (+#{@threshold})" : '' - counts = "#{expected_count}#{threshold_message}" - "Expected a maximum of #{counts} queries, got #{actual_count}:\n\n#{log_message}" - end - +module ExceedQueryLimitHelpers def with_threshold(threshold) @threshold = threshold self @@ -43,7 +30,7 @@ RSpec::Matchers.define :exceed_query_limit do |expected| end def recorder - @recorder ||= ActiveRecord::QueryRecorder.new(&@subject_block) + @recorder ||= ActiveRecord::QueryRecorder.new(skip_cached: skip_cached, &@subject_block) end def count_queries(queries) @@ -61,4 +48,52 @@ RSpec::Matchers.define :exceed_query_limit do |expected| @recorder.log_message end end + + def skip_cached + true + end + + def verify_count(&block) + @subject_block = block + actual_count > expected_count + threshold + end + + def failure_message + threshold_message = threshold > 0 ? " (+#{@threshold})" : '' + counts = "#{expected_count}#{threshold_message}" + "Expected a maximum of #{counts} queries, got #{actual_count}:\n\n#{log_message}" + end +end + +RSpec::Matchers.define :exceed_all_query_limit do |expected| + supports_block_expectations + + include ExceedQueryLimitHelpers + + match do |block| + verify_count(&block) + end + + failure_message_when_negated do |actual| + failure_message + end + + def skip_cached + false + end +end + +# Excludes cached methods from the query count +RSpec::Matchers.define :exceed_query_limit do |expected| + supports_block_expectations + + include ExceedQueryLimitHelpers + + match do |block| + verify_count(&block) + end + + failure_message_when_negated do |actual| + failure_message + end end -- cgit v1.2.1 From c26cbfcdfb6a0c83253f42be016692abc3548e8c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 3 Jun 2018 03:31:41 -0700 Subject: Remove N+1 query for author in issues API This was being masked by the statement cache because only one author was used per issue in the test.. Also adds support for an Rspec matcher `exceed_all_query_limit`. --- spec/requests/api/issues_spec.rb | 8 ++-- spec/support/helpers/query_recorder.rb | 7 ++-- spec/support/matchers/exceed_query_limit.rb | 65 ++++++++++++++++++++++------- 3 files changed, 59 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 4181f4ebbbe..a15d60aafe0 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -630,15 +630,17 @@ describe API::Issues do end it 'avoids N+1 queries' do - control_count = ActiveRecord::QueryRecorder.new do + get api("/projects/#{project.id}/issues", user) + + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do get api("/projects/#{project.id}/issues", user) end.count - create(:issue, author: user, project: project) + create_list(:issue, 3, project: project) expect do get api("/projects/#{project.id}/issues", user) - end.not_to exceed_query_limit(control_count) + end.not_to exceed_all_query_limit(control_count) end it 'returns 404 when project does not exist' do diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb index 28536bbef5e..7ce63375d34 100644 --- a/spec/support/helpers/query_recorder.rb +++ b/spec/support/helpers/query_recorder.rb @@ -1,10 +1,11 @@ module ActiveRecord class QueryRecorder - attr_reader :log, :cached + attr_reader :log, :skip_cached, :cached - def initialize(&block) + def initialize(skip_cached: true, &block) @log = [] @cached = [] + @skip_cached = skip_cached ActiveSupport::Notifications.subscribed(method(:callback), 'sql.active_record', &block) end @@ -16,7 +17,7 @@ module ActiveRecord def callback(name, start, finish, message_id, values) show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG'] - if values[:name]&.include?("CACHE") + if values[:name]&.include?("CACHE") && skip_cached @cached << values[:sql] elsif !values[:name]&.include?("SCHEMA") @log << values[:sql] diff --git a/spec/support/matchers/exceed_query_limit.rb b/spec/support/matchers/exceed_query_limit.rb index 88d22a3ddd9..cd042401f3a 100644 --- a/spec/support/matchers/exceed_query_limit.rb +++ b/spec/support/matchers/exceed_query_limit.rb @@ -1,17 +1,4 @@ -RSpec::Matchers.define :exceed_query_limit do |expected| - supports_block_expectations - - match do |block| - @subject_block = block - actual_count > expected_count + threshold - end - - failure_message_when_negated do |actual| - threshold_message = threshold > 0 ? " (+#{@threshold})" : '' - counts = "#{expected_count}#{threshold_message}" - "Expected a maximum of #{counts} queries, got #{actual_count}:\n\n#{log_message}" - end - +module ExceedQueryLimitHelpers def with_threshold(threshold) @threshold = threshold self @@ -43,7 +30,7 @@ RSpec::Matchers.define :exceed_query_limit do |expected| end def recorder - @recorder ||= ActiveRecord::QueryRecorder.new(&@subject_block) + @recorder ||= ActiveRecord::QueryRecorder.new(skip_cached: skip_cached, &@subject_block) end def count_queries(queries) @@ -61,4 +48,52 @@ RSpec::Matchers.define :exceed_query_limit do |expected| @recorder.log_message end end + + def skip_cached + true + end + + def verify_count(&block) + @subject_block = block + actual_count > expected_count + threshold + end + + def failure_message + threshold_message = threshold > 0 ? " (+#{@threshold})" : '' + counts = "#{expected_count}#{threshold_message}" + "Expected a maximum of #{counts} queries, got #{actual_count}:\n\n#{log_message}" + end +end + +RSpec::Matchers.define :exceed_all_query_limit do |expected| + supports_block_expectations + + include ExceedQueryLimitHelpers + + match do |block| + verify_count(&block) + end + + failure_message_when_negated do |actual| + failure_message + end + + def skip_cached + false + end +end + +# Excludes cached methods from the query count +RSpec::Matchers.define :exceed_query_limit do |expected| + supports_block_expectations + + include ExceedQueryLimitHelpers + + match do |block| + verify_count(&block) + end + + failure_message_when_negated do |actual| + failure_message + end end -- cgit v1.2.1 From 2bb94ff7f915bdc09275085a075e202f92241811 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 3 Jun 2018 04:36:22 -0700 Subject: Eliminate N+1 queries for CI job artifacts in /api/projects/:id/pipelines/:pipeline_id/jobs --- spec/requests/api/jobs_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'spec') diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index 45082e644ca..50d6f4b4d99 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -177,6 +177,18 @@ describe API::Jobs do json_response.each { |job| expect(job['pipeline']['id']).to eq(pipeline.id) } end end + + it 'avoids N+1 queries' do + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do + get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query + end.count + + 3.times { create(:ci_build, :artifacts, pipeline: pipeline) } + + expect do + get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query + end.not_to exceed_all_query_limit(control_count) + end end context 'unauthorized user' do -- cgit v1.2.1 From bc7dbfdc5d88a2e78239bdf481b7b52e03e890b6 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 4 Jun 2018 20:23:40 -0500 Subject: Backport of Resolve "Epics Page Styling is broken" --- .../vue_shared/components/sidebar/labels_select/dropdown_value_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js index 4397b00acfa..370a296bd8f 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js @@ -82,7 +82,7 @@ describe('DropdownValueComponent', () => { }); it('renders label element with tooltip and styles based on label details', () => { - const labelEl = vm.$el.querySelector('a span.label.color-label'); + const labelEl = vm.$el.querySelector('a span.badge.color-label'); expect(labelEl).not.toBeNull(); expect(labelEl.dataset.placement).toBe('bottom'); expect(labelEl.dataset.container).toBe('body'); -- cgit v1.2.1 From c503429e5e8538649bec02d74963ec0eb8f0cb98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Mon, 4 Jun 2018 23:00:25 -0500 Subject: Add migration to disable the usage of DSA keys Additionally the current application setting is also updated to disable the usage of DSA keys. --- ...e_default_value_for_dsa_key_restriction_spec.rb | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 spec/migrations/change_default_value_for_dsa_key_restriction_spec.rb (limited to 'spec') diff --git a/spec/migrations/change_default_value_for_dsa_key_restriction_spec.rb b/spec/migrations/change_default_value_for_dsa_key_restriction_spec.rb new file mode 100644 index 00000000000..7e61ab9b52e --- /dev/null +++ b/spec/migrations/change_default_value_for_dsa_key_restriction_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20180531220618_change_default_value_for_dsa_key_restriction.rb') + +describe ChangeDefaultValueForDsaKeyRestriction, :migration do + let(:application_settings) { table(:application_settings) } + + before do + application_settings.create! + end + + it 'changes the default value for dsa_key_restriction' do + expect(application_settings.first.dsa_key_restriction).to eq(0) + + migrate! + + application_settings.reset_column_information + new_setting = application_settings.create! + + expect(application_settings.count).to eq(2) + expect(new_setting.dsa_key_restriction).to eq(-1) + end + + it 'changes the existing setting' do + setting = application_settings.last + + expect(setting.dsa_key_restriction).to eq(0) + + migrate! + + expect(application_settings.count).to eq(1) + expect(setting.reload.dsa_key_restriction).to eq(-1) + end +end -- cgit v1.2.1 From f46739191a71d881501bb3fc4740b953824a9fb3 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Mon, 4 Jun 2018 19:20:58 -0300 Subject: Adjust insufficient diff hunks being persisted on NoteDiffFile This currently causes 500's errors when loading the MR page (Discussion) in a few scenarios. We were not considering detailed diff headers such as "--- a/doc/update/mysql_to_postgresql.md\n+++ b/doc/update/mysql_to_postgresql.md" to crop the diff. In order to address it, we're now using Gitlab::Diff::Parser, clean the diffs and builds Gitlab::Diff::Line objects we can iterate and filter on. --- spec/lib/gitlab/diff/file_spec.rb | 113 +++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 50 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index 0588fe935c3..f0e83ccfc7a 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -470,56 +470,69 @@ describe Gitlab::Diff::File do end describe '#diff_hunk' do - let(:raw_diff) do - < path } -- options = { chdir: path } -+ -+ vars = { -+ "PWD" => path -+ } -+ -+ options = { -+ chdir: path -+ } - - unless File.directory?(path) - FileUtils.mkdir_p(path) -@@ -19,6 +25,7 @@ module Popen - - @cmd_output = "" - @cmd_status = 0 -+ - Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| - @cmd_output << stdout.read - @cmd_output << stderr.read -EOS - end - - it 'returns raw diff up to given line index' do - allow(diff_file).to receive(:raw_diff) { raw_diff } - diff_line = instance_double(Gitlab::Diff::Line, index: 5) - - diff_hunk = < Date: Mon, 4 Jun 2018 22:54:50 +0800 Subject: Also verify if extending would override a class method Since extending a class means including on the singleton class of the class, this should now complain this: ``` ruby module M extend Gitlab::Utils::Override override :f def f super.succ end end class C extend M def self.f 0 end end ``` It should complain because `C.f` wasn't calling `M#f`. This should pass verification: ``` ruby module M extend Gitlab::Utils::Override override :f def f super.succ end end class B def self.f 0 end end class C < B extend M end ``` Because `C.f` would now call `M#f`, and `M#f` does override something. --- spec/lib/gitlab/utils/override_spec.rb | 170 +++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 62 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/utils/override_spec.rb b/spec/lib/gitlab/utils/override_spec.rb index 7c97cee982a..fc08ebcfc6d 100644 --- a/spec/lib/gitlab/utils/override_spec.rb +++ b/spec/lib/gitlab/utils/override_spec.rb @@ -1,7 +1,13 @@ -require 'spec_helper' +require 'fast_spec_helper' describe Gitlab::Utils::Override do - let(:base) { Struct.new(:good) } + let(:base) do + Struct.new(:good) do + def self.good + 0 + end + end + end let(:derived) { Class.new(base).tap { |m| m.extend described_class } } let(:extension) { Module.new.tap { |m| m.extend described_class } } @@ -9,6 +15,14 @@ describe Gitlab::Utils::Override do let(:prepending_class) { base.tap { |m| m.prepend extension } } let(:including_class) { base.tap { |m| m.include extension } } + let(:prepending_class_methods) do + base.tap { |m| m.singleton_class.prepend extension } + end + + let(:extending_class_methods) do + base.tap { |m| m.extend extension } + end + let(:klass) { subject } def good(mod) @@ -36,7 +50,7 @@ describe Gitlab::Utils::Override do shared_examples 'checking as intended' do it 'checks ok for overriding method' do good(subject) - result = klass.new(0).good + result = instance.good expect(result).to eq(1) described_class.verify! @@ -45,7 +59,25 @@ describe Gitlab::Utils::Override do it 'raises NotImplementedError when it is not overriding anything' do expect do bad(subject) - klass.new(0).bad + instance.bad + described_class.verify! + end.to raise_error(NotImplementedError) + end + end + + shared_examples 'checking as intended, nothing was overridden' do + it 'raises NotImplementedError because it is not overriding it' do + expect do + good(subject) + instance.good + described_class.verify! + end.to raise_error(NotImplementedError) + end + + it 'raises NotImplementedError when it is not overriding anything' do + expect do + bad(subject) + instance.bad described_class.verify! end.to raise_error(NotImplementedError) end @@ -54,7 +86,7 @@ describe Gitlab::Utils::Override do shared_examples 'nothing happened' do it 'does not complain when it is overriding something' do good(subject) - result = klass.new(0).good + result = instance.good expect(result).to eq(1) described_class.verify! @@ -62,7 +94,7 @@ describe Gitlab::Utils::Override do it 'does not complain when it is not overriding anything' do bad(subject) - result = klass.new(0).bad + result = instance.bad expect(result).to eq(true) described_class.verify! @@ -75,83 +107,97 @@ describe Gitlab::Utils::Override do end describe '#override' do - context 'when STATIC_VERIFICATION is set' do - before do - stub_env('STATIC_VERIFICATION', 'true') - end + context 'when instance is klass.new(0)' do + let(:instance) { klass.new(0) } - context 'when subject is a class' do - subject { derived } + context 'when STATIC_VERIFICATION is set' do + before do + stub_env('STATIC_VERIFICATION', 'true') + end - it_behaves_like 'checking as intended' - end + context 'when subject is a class' do + subject { derived } + + it_behaves_like 'checking as intended' + end + + context 'when subject is a module, and class is prepending it' do + subject { extension } + let(:klass) { prepending_class } + + it_behaves_like 'checking as intended' + end - context 'when subject is a module, and class is prepending it' do - subject { extension } - let(:klass) { prepending_class } + context 'when subject is a module, and class is including it' do + subject { extension } + let(:klass) { including_class } - it_behaves_like 'checking as intended' + it_behaves_like 'checking as intended, nothing was overridden' + end end - context 'when subject is a module, and class is including it' do - subject { extension } - let(:klass) { including_class } + context 'when STATIC_VERIFICATION is not set' do + before do + stub_env('STATIC_VERIFICATION', nil) + end - it 'raises NotImplementedError because it is not overriding it' do - expect do - good(subject) - klass.new(0).good - described_class.verify! - end.to raise_error(NotImplementedError) + context 'when subject is a class' do + subject { derived } + + it_behaves_like 'nothing happened' end - it 'raises NotImplementedError when it is not overriding anything' do - expect do - bad(subject) - klass.new(0).bad - described_class.verify! - end.to raise_error(NotImplementedError) + context 'when subject is a module, and class is prepending it' do + subject { extension } + let(:klass) { prepending_class } + + it_behaves_like 'nothing happened' end - end - end - end - context 'when STATIC_VERIFICATION is not set' do - before do - stub_env('STATIC_VERIFICATION', nil) - end + context 'when subject is a module, and class is including it' do + subject { extension } + let(:klass) { including_class } - context 'when subject is a class' do - subject { derived } + it 'does not complain when it is overriding something' do + good(subject) + result = instance.good - it_behaves_like 'nothing happened' - end + expect(result).to eq(0) + described_class.verify! + end - context 'when subject is a module, and class is prepending it' do - subject { extension } - let(:klass) { prepending_class } + it 'does not complain when it is not overriding anything' do + bad(subject) + result = instance.bad - it_behaves_like 'nothing happened' + expect(result).to eq(true) + described_class.verify! + end + end + end end - context 'when subject is a module, and class is including it' do - subject { extension } - let(:klass) { including_class } + context 'when instance is klass' do + let(:instance) { klass } - it 'does not complain when it is overriding something' do - good(subject) - result = klass.new(0).good + context 'when STATIC_VERIFICATION is set' do + before do + stub_env('STATIC_VERIFICATION', 'true') + end - expect(result).to eq(0) - described_class.verify! - end + context 'when subject is a module, and class is prepending it' do + subject { extension } + let(:klass) { prepending_class_methods } - it 'does not complain when it is not overriding anything' do - bad(subject) - result = klass.new(0).bad + it_behaves_like 'checking as intended' + end - expect(result).to eq(true) - described_class.verify! + context 'when subject is a module, and class is extending it' do + subject { extension } + let(:klass) { extending_class_methods } + + it_behaves_like 'checking as intended, nothing was overridden' + end end end end -- cgit v1.2.1 From 993438ada993880c166c376eba4ae6ab02e3064b Mon Sep 17 00:00:00 2001 From: Jose Ivan Vargas Date: Tue, 5 Jun 2018 08:11:13 +0000 Subject: Support smarter system notes --- spec/javascripts/notes/mock_data.js | 578 +++++++++++++++++++++ .../notes/stores/collapse_utils_spec.js | 46 ++ spec/javascripts/notes/stores/getters_spec.js | 19 +- 3 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 spec/javascripts/notes/stores/collapse_utils_spec.js (limited to 'spec') diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js index bfe3a65feee..fa7adc32193 100644 --- a/spec/javascripts/notes/mock_data.js +++ b/spec/javascripts/notes/mock_data.js @@ -340,6 +340,79 @@ export const loggedOutnoteableData = { '/gitlab-org/gitlab-ce/preview_markdown?quick_actions_target_id=98&quick_actions_target_type=Issue', }; +export const collapseNotesMock = [ + { + expanded: true, + id: '0fb4e0e3f9276e55ff32eb4195add694aece4edd', + individual_note: true, + notes: [ + { + id: 1390, + attachment: null, + author: { + id: 1, + name: 'Root', + username: 'root', + state: 'active', + avatar_url: 'test', + path: '/root', + }, + created_at: '2018-02-26T18:07:41.071Z', + updated_at: '2018-02-26T18:07:41.071Z', + system: true, + system_note_icon_name: 'pencil', + noteable_id: 98, + noteable_type: 'Issue', + type: null, + human_access: 'Owner', + note: 'changed the description', + note_html: '

changed the description

', + current_user: { can_edit: false }, + discussion_id: 'b97fb7bda470a65b3e009377a9032edec0a4dd05', + emoji_awardable: false, + path: '/h5bp/html5-boilerplate/notes/1057', + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fh5bp%2Fhtml5-boilerplate%2Fissues%2F10%23note_1057&user_id=1', + }, + ], + }, + { + expanded: true, + id: 'ffde43f25984ad7f2b4275135e0e2846875336c0', + individual_note: true, + notes: [ + { + id: 1391, + attachment: null, + author: { + id: 1, + name: 'Root', + username: 'root', + state: 'active', + avatar_url: 'test', + path: '/root', + }, + created_at: '2018-02-26T18:13:24.071Z', + updated_at: '2018-02-26T18:13:24.071Z', + system: true, + system_note_icon_name: 'pencil', + noteable_id: 99, + noteable_type: 'Issue', + type: null, + human_access: 'Owner', + note: 'changed the description', + note_html: '

changed the description

', + current_user: { can_edit: false }, + discussion_id: '3eb958b4d81dec207ec3537a2f3bd8b9f271bb34', + emoji_awardable: false, + path: '/h5bp/html5-boilerplate/notes/1057', + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fh5bp%2Fhtml5-boilerplate%2Fissues%2F10%23note_1057&user_id=1', + }, + ], + }, +]; + export const INDIVIDUAL_NOTE_RESPONSE_MAP = { GET: { '/gitlab-org/gitlab-ce/issues/26/discussions.json': [ @@ -575,3 +648,508 @@ export function discussionNoteInterceptor(request, next) { }), ); } + +export const notesWithDescriptionChanges = [ + { + id: '39b271c2033e9ed43d8edb393702f65f7a830459', + reply_id: '39b271c2033e9ed43d8edb393702f65f7a830459', + expanded: true, + notes: [ + { + id: 901, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:05:36.117Z', + updated_at: '2018-05-29T12:05:36.117Z', + system: false, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + note_html: + '

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

', + current_user: { can_edit: true, can_award_emoji: true }, + resolved: false, + resolved_by: null, + discussion_id: '39b271c2033e9ed43d8edb393702f65f7a830459', + emoji_awardable: true, + award_emoji: [], + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_901&user_id=1', + human_access: 'Owner', + toggle_award_path: '/gitlab-org/gitlab-shell/notes/901/toggle_award_emoji', + path: '/gitlab-org/gitlab-shell/notes/901', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', + reply_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', + expanded: true, + notes: [ + { + id: 902, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:05:58.694Z', + updated_at: '2018-05-29T12:05:58.694Z', + system: false, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: + 'Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.', + note_html: + '

Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.

', + current_user: { can_edit: true, can_award_emoji: true }, + resolved: false, + resolved_by: null, + discussion_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', + emoji_awardable: true, + award_emoji: [], + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_902&user_id=1', + human_access: 'Owner', + toggle_award_path: '/gitlab-org/gitlab-shell/notes/902/toggle_award_emoji', + path: '/gitlab-org/gitlab-shell/notes/902', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: '7f1feda384083eb31763366e6392399fde6f3f31', + reply_id: '7f1feda384083eb31763366e6392399fde6f3f31', + expanded: true, + notes: [ + { + id: 903, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:06:05.772Z', + updated_at: '2018-05-29T12:06:05.772Z', + system: true, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: 'changed the description', + note_html: '

changed the description

', + current_user: { can_edit: false, can_award_emoji: true }, + resolved: false, + resolved_by: null, + system_note_icon_name: 'pencil-square', + discussion_id: '7f1feda384083eb31763366e6392399fde6f3f31', + emoji_awardable: false, + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_903&user_id=1', + human_access: 'Owner', + path: '/gitlab-org/gitlab-shell/notes/903', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: '091865fe3ae20f0045234a3d103e3b15e73405b5', + reply_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', + expanded: true, + notes: [ + { + id: 904, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:06:16.112Z', + updated_at: '2018-05-29T12:06:16.112Z', + system: false, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: 'Ullamcorper eget nulla facilisi etiam', + note_html: '

Ullamcorper eget nulla facilisi etiam

', + current_user: { can_edit: true, can_award_emoji: true }, + resolved: false, + resolved_by: null, + discussion_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', + emoji_awardable: true, + award_emoji: [], + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_904&user_id=1', + human_access: 'Owner', + toggle_award_path: '/gitlab-org/gitlab-shell/notes/904/toggle_award_emoji', + path: '/gitlab-org/gitlab-shell/notes/904', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', + reply_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', + expanded: true, + notes: [ + { + id: 905, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:06:28.851Z', + updated_at: '2018-05-29T12:06:28.851Z', + system: true, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: 'changed the description', + note_html: '

changed the description

', + current_user: { can_edit: false, can_award_emoji: true }, + resolved: false, + resolved_by: null, + system_note_icon_name: 'pencil-square', + discussion_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', + emoji_awardable: false, + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_905&user_id=1', + human_access: 'Owner', + path: '/gitlab-org/gitlab-shell/notes/905', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: '70411b08cdfc01f24187a06d77daa33464cb2620', + reply_id: '70411b08cdfc01f24187a06d77daa33464cb2620', + expanded: true, + notes: [ + { + id: 906, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:20:02.925Z', + updated_at: '2018-05-29T12:20:02.925Z', + system: true, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: 'changed the description', + note_html: '

changed the description

', + current_user: { can_edit: false, can_award_emoji: true }, + resolved: false, + resolved_by: null, + system_note_icon_name: 'pencil-square', + discussion_id: '70411b08cdfc01f24187a06d77daa33464cb2620', + emoji_awardable: false, + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_906&user_id=1', + human_access: 'Owner', + path: '/gitlab-org/gitlab-shell/notes/906', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, +]; + +export const collapsedSystemNotes = [ + { + id: '39b271c2033e9ed43d8edb393702f65f7a830459', + reply_id: '39b271c2033e9ed43d8edb393702f65f7a830459', + expanded: true, + notes: [ + { + id: 901, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:05:36.117Z', + updated_at: '2018-05-29T12:05:36.117Z', + system: false, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + note_html: + '

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

', + current_user: { can_edit: true, can_award_emoji: true }, + resolved: false, + resolved_by: null, + discussion_id: '39b271c2033e9ed43d8edb393702f65f7a830459', + emoji_awardable: true, + award_emoji: [], + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_901&user_id=1', + human_access: 'Owner', + toggle_award_path: '/gitlab-org/gitlab-shell/notes/901/toggle_award_emoji', + path: '/gitlab-org/gitlab-shell/notes/901', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', + reply_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', + expanded: true, + notes: [ + { + id: 902, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:05:58.694Z', + updated_at: '2018-05-29T12:05:58.694Z', + system: false, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: + 'Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.', + note_html: + '

Varius vel pharetra vel turpis nunc eget lorem. Ipsum dolor sit amet consectetur adipiscing.

', + current_user: { can_edit: true, can_award_emoji: true }, + resolved: false, + resolved_by: null, + discussion_id: '4852335d7dc40b9ceb8fde1a2bb9c1b67e4c7795', + emoji_awardable: true, + award_emoji: [], + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_902&user_id=1', + human_access: 'Owner', + toggle_award_path: '/gitlab-org/gitlab-shell/notes/902/toggle_award_emoji', + path: '/gitlab-org/gitlab-shell/notes/902', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: '091865fe3ae20f0045234a3d103e3b15e73405b5', + reply_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', + expanded: true, + notes: [ + { + id: 904, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:06:16.112Z', + updated_at: '2018-05-29T12:06:16.112Z', + system: false, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: 'Ullamcorper eget nulla facilisi etiam', + note_html: '

Ullamcorper eget nulla facilisi etiam

', + current_user: { can_edit: true, can_award_emoji: true }, + resolved: false, + resolved_by: null, + discussion_id: '091865fe3ae20f0045234a3d103e3b15e73405b5', + emoji_awardable: true, + award_emoji: [], + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_904&user_id=1', + human_access: 'Owner', + toggle_award_path: '/gitlab-org/gitlab-shell/notes/904/toggle_award_emoji', + path: '/gitlab-org/gitlab-shell/notes/904', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', + reply_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', + expanded: true, + notes: [ + { + id: 905, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:06:28.851Z', + updated_at: '2018-05-29T12:06:28.851Z', + system: true, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: 'changed the description', + note_html: '\n

changed the description 2 times within 1 minute

', + current_user: { can_edit: false, can_award_emoji: true }, + resolved: false, + resolved_by: null, + system_note_icon_name: 'pencil-square', + discussion_id: 'a21cf2e804acc3c60d07e37d75e395f5a9a4d044', + emoji_awardable: false, + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_905&user_id=1', + human_access: 'Owner', + path: '/gitlab-org/gitlab-shell/notes/905', + times_updated: 2, + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, + { + id: '70411b08cdfc01f24187a06d77daa33464cb2620', + reply_id: '70411b08cdfc01f24187a06d77daa33464cb2620', + expanded: true, + notes: [ + { + id: 906, + type: null, + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-05-29T12:20:02.925Z', + updated_at: '2018-05-29T12:20:02.925Z', + system: true, + noteable_id: 182, + noteable_type: 'Issue', + resolvable: false, + noteable_iid: 12, + note: 'changed the description', + note_html: '

changed the description

', + current_user: { can_edit: false, can_award_emoji: true }, + resolved: false, + resolved_by: null, + system_note_icon_name: 'pencil-square', + discussion_id: '70411b08cdfc01f24187a06d77daa33464cb2620', + emoji_awardable: false, + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-shell%2Fissues%2F12%23note_906&user_id=1', + human_access: 'Owner', + path: '/gitlab-org/gitlab-shell/notes/906', + }, + ], + individual_note: true, + resolvable: false, + resolved: false, + diff_discussion: false, + }, +]; diff --git a/spec/javascripts/notes/stores/collapse_utils_spec.js b/spec/javascripts/notes/stores/collapse_utils_spec.js new file mode 100644 index 00000000000..06a6aab932a --- /dev/null +++ b/spec/javascripts/notes/stores/collapse_utils_spec.js @@ -0,0 +1,46 @@ +import { + isDescriptionSystemNote, + changeDescriptionNote, + getTimeDifferenceMinutes, + collapseSystemNotes, +} from '~/notes/stores/collapse_utils'; +import { + notesWithDescriptionChanges, + collapsedSystemNotes, +} from '../mock_data'; + +describe('Collapse utils', () => { + const mockSystemNote = { + note: 'changed the description', + note_html: '

changed the description

', + system: true, + created_at: '2018-05-14T21:28:00.000Z', + }; + + it('checks if a system note is of a description type', () => { + expect(isDescriptionSystemNote(mockSystemNote)).toEqual(true); + }); + + it('returns false when a system note is not a description type', () => { + expect(isDescriptionSystemNote(Object.assign({}, mockSystemNote, { note: 'foo' }))).toEqual(false); + }); + + it('changes the description to contain the number of changed times', () => { + const changedNote = changeDescriptionNote(mockSystemNote, 3, 5); + + expect(changedNote.times_updated).toEqual(3); + expect(changedNote.note_html.trim()).toContain('

changed the description 3 times within 5 minutes

'); + }); + + it('gets the time difference between two notes', () => { + const anotherSystemNote = { + created_at: '2018-05-14T21:33:00.000Z', + }; + + expect(getTimeDifferenceMinutes(mockSystemNote, anotherSystemNote)).toEqual(5); + }); + + it('collapses all description system notes made within 10 minutes or less from each other', () => { + expect(collapseSystemNotes(notesWithDescriptionChanges)).toEqual(collapsedSystemNotes); + }); +}); diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js index 8b2a8d2cd7a..e5550580bf8 100644 --- a/spec/javascripts/notes/stores/getters_spec.js +++ b/spec/javascripts/notes/stores/getters_spec.js @@ -1,8 +1,9 @@ import * as getters from '~/notes/stores/getters'; -import { notesDataMock, userDataMock, noteableDataMock, individualNote } from '../mock_data'; +import { notesDataMock, userDataMock, noteableDataMock, individualNote, collapseNotesMock } from '../mock_data'; describe('Getters Notes Store', () => { let state; + beforeEach(() => { state = { notes: [individualNote], @@ -20,6 +21,22 @@ describe('Getters Notes Store', () => { }); }); + describe('Collapsed notes', () => { + const stateCollapsedNotes = { + notes: collapseNotesMock, + targetNoteHash: 'hash', + lastFetchedAt: 'timestamp', + + notesData: notesDataMock, + userData: userDataMock, + noteableData: noteableDataMock, + }; + + it('should return a single system note when a description was updated multiple times', () => { + expect(getters.notes(stateCollapsedNotes).length).toEqual(1); + }); + }); + describe('targetNoteHash', () => { it('should return `targetNoteHash`', () => { expect(getters.targetNoteHash(state)).toEqual('hash'); -- cgit v1.2.1 From 36fbe1422666cb5553ccde9e534bb2a504f0f2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Mon, 28 May 2018 18:17:18 +0200 Subject: Do not validate cached data --- spec/models/ci/runner_spec.rb | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 0f072aa1719..f6433234573 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -549,7 +549,7 @@ describe Ci::Runner do end describe '#update_cached_info' do - let(:runner) { create(:ci_runner) } + let(:runner) { create(:ci_runner, :project) } subject { runner.update_cached_info(architecture: '18-bit') } @@ -570,17 +570,22 @@ describe Ci::Runner do runner.contacted_at = 2.hours.ago end - it 'updates database' do - expect_redis_update + context 'with invalid runner' do + before do + runner.projects = [] + end + + it 'still updates redis cache and database' do + expect(runner).to be_invalid - expect { subject }.to change { runner.reload.read_attribute(:contacted_at) } - .and change { runner.reload.read_attribute(:architecture) } + expect_redis_update + does_db_update + end end - it 'updates cache' do + it 'updates redis cache and database' do expect_redis_update - - subject + does_db_update end end @@ -590,6 +595,11 @@ describe Ci::Runner do expect(redis).to receive(:set).with(redis_key, anything, any_args) end end + + def does_db_update + expect { subject }.to change { runner.reload.read_attribute(:contacted_at) } + .and change { runner.reload.read_attribute(:architecture) } + end end describe '#destroy' do -- cgit v1.2.1 From 491e1fc905ef52dcc2e7df7deabd3c1f6e42aa52 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 4 Jun 2018 17:04:04 +0200 Subject: Render a 403 when showing an access denied message When we want to show an access denied message to a user, we don't have to hide the resource's existence. So in that case we render a 403, this 403 is not handled by nginx on omnibus installs, making sure the message is visible to the user. --- spec/controllers/application_controller_spec.rb | 24 ++++++++++++++++++++++ ...troller_with_cross_project_access_check_spec.rb | 12 +++++------ spec/controllers/search_controller_spec.rb | 2 +- 3 files changed, 31 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index b048da1991c..683c57c96f8 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -477,4 +477,28 @@ describe ApplicationController do end end end + + describe '#access_denied' do + controller(described_class) do + def index + access_denied!(params[:message]) + end + end + + before do + sign_in user + end + + it 'renders a 404 without a message' do + get :index + + expect(response).to have_gitlab_http_status(404) + end + + it 'renders a 403 when a message is passed to access denied' do + get :index, message: 'None shall pass' + + expect(response).to have_gitlab_http_status(403) + end + end end diff --git a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb index 27f558e1b5d..d20471ef603 100644 --- a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb +++ b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb @@ -43,13 +43,13 @@ describe ControllerWithCrossProjectAccessCheck do end end - it 'renders a 404 with trying to access a cross project page' do + it 'renders a 403 with trying to access a cross project page' do message = "This page is unavailable because you are not allowed to read "\ "information across multiple projects." get :index - expect(response).to have_gitlab_http_status(404) + expect(response).to have_gitlab_http_status(403) expect(response.body).to match(/#{message}/) end @@ -119,7 +119,7 @@ describe ControllerWithCrossProjectAccessCheck do get :index - expect(response).to have_gitlab_http_status(404) + expect(response).to have_gitlab_http_status(403) end it 'is executed when the `unless` condition returns true' do @@ -127,19 +127,19 @@ describe ControllerWithCrossProjectAccessCheck do get :index - expect(response).to have_gitlab_http_status(404) + expect(response).to have_gitlab_http_status(403) end it 'does not skip the check on an action that is not skipped' do get :show, id: 'hello' - expect(response).to have_gitlab_http_status(404) + expect(response).to have_gitlab_http_status(403) end it 'does not skip the check on an action that was not defined to skip' do get :edit, id: 'hello' - expect(response).to have_gitlab_http_status(404) + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 30c06ddf744..416a09e1684 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -32,7 +32,7 @@ describe SearchController do it 'still blocks searches without a project_id' do get :show, search: 'hello' - expect(response).to have_gitlab_http_status(404) + expect(response).to have_gitlab_http_status(403) end it 'allows searches with a project_id' do -- cgit v1.2.1 From b009a0084c67877ba6a808c4c8a81c568598d624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Mon, 4 Jun 2018 21:49:39 +0200 Subject: Remove PagesService and instead make it explicit that we call PagesWorker --- spec/models/ci/build_spec.rb | 72 +++++++++++++++++++++++++++++++++++++ spec/services/pages_service_spec.rb | 53 --------------------------- 2 files changed, 72 insertions(+), 53 deletions(-) delete mode 100644 spec/services/pages_service_spec.rb (limited to 'spec') diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 66c9708b4cf..e5982a44e89 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2506,4 +2506,76 @@ describe Ci::Build do end end end + + describe 'pages deployments' do + set(:build) { create(:ci_build, project: project, user: user) } + + context 'when job is "pages"' do + before do + build.name = 'pages' + end + + context 'when pages are enabled' do + before do + allow(Gitlab.config.pages).to receive_messages(enabled: true) + end + + it 'is marked as pages generator' do + expect(build).to be_pages_generator + end + + context 'job succeeds' do + it "calls pages worker" do + expect(PagesWorker).to receive(:perform_async).with(:deploy, build.id) + + build.success! + end + end + + context 'job fails' do + it "does not call pages worker" do + expect(PagesWorker).not_to receive(:perform_async) + + build.drop! + end + end + end + + context 'when pages are disabled' do + before do + allow(Gitlab.config.pages).to receive_messages(enabled: false) + end + + it 'is not marked as pages generator' do + expect(build).not_to be_pages_generator + end + + context 'job succeeds' do + it "does not call pages worker" do + expect(PagesWorker).not_to receive(:perform_async) + + build.success! + end + end + end + end + + context 'when job is not "pages"' do + before do + build.name = 'other-job' + end + + it 'is not marked as pages generator' do + expect(build).not_to be_pages_generator + end + + context 'job succeeds' do + it "does not call pages worker" do + expect(PagesWorker).not_to receive(:perform_async) + + build.success + end + end + end + end end diff --git a/spec/services/pages_service_spec.rb b/spec/services/pages_service_spec.rb deleted file mode 100644 index f8db6900a0a..00000000000 --- a/spec/services/pages_service_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'spec_helper' - -describe PagesService do - let(:build) { create(:ci_build) } - let(:data) { Gitlab::DataBuilder::Build.build(build) } - let(:service) { described_class.new(data) } - - before do - allow(Gitlab.config.pages).to receive(:enabled).and_return(true) - end - - context 'execute asynchronously for pages job' do - before do - build.name = 'pages' - end - - context 'on success' do - before do - build.success - end - - it 'executes worker' do - expect(PagesWorker).to receive(:perform_async) - service.execute - end - end - - %w(pending running failed canceled).each do |status| - context "on #{status}" do - before do - build.status = status - end - - it 'does not execute worker' do - expect(PagesWorker).not_to receive(:perform_async) - service.execute - end - end - end - end - - context 'for other jobs' do - before do - build.name = 'other job' - build.success - end - - it 'does not execute worker' do - expect(PagesWorker).not_to receive(:perform_async) - service.execute - end - end -end -- cgit v1.2.1 From bc59fc3719869e60ee4ff00c7da766ea01293239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lu=C3=ADs?= Date: Tue, 5 Jun 2018 10:10:34 +0000 Subject: Resolve "Add Xcode Button in UI" --- spec/helpers/projects_helper_spec.rb | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'spec') diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index f8877b6d1aa..4e5391295b6 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -435,4 +435,46 @@ describe ProjectsHelper do expect(helper.send(:git_user_name)).to eq('John \"A\" Doe53') end end + + describe 'show_xcode_link' do + let!(:project) { create(:project) } + let(:mac_ua) { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36' } + let(:ios_ua) { 'Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3' } + + context 'when the repository is xcode compatible' do + before do + allow(project.repository).to receive(:xcode_project?).and_return(true) + end + + it 'returns false if the visitor is not using macos' do + allow(helper).to receive(:browser).and_return(Browser.new(ios_ua)) + + expect(helper.show_xcode_link?(project)).to eq(false) + end + + it 'returns true if the visitor is using macos' do + allow(helper).to receive(:browser).and_return(Browser.new(mac_ua)) + + expect(helper.show_xcode_link?(project)).to eq(true) + end + end + + context 'when the repository is not xcode compatible' do + before do + allow(project.repository).to receive(:xcode_project?).and_return(false) + end + + it 'returns false if the visitor is not using macos' do + allow(helper).to receive(:browser).and_return(Browser.new(ios_ua)) + + expect(helper.show_xcode_link?(project)).to eq(false) + end + + it 'returns false if the visitor is using macos' do + allow(helper).to receive(:browser).and_return(Browser.new(mac_ua)) + + expect(helper.show_xcode_link?(project)).to eq(false) + end + end + end end -- cgit v1.2.1 From 463d77a59e7d3c5f0089d932692aeddaff96c4c0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 5 Jun 2018 12:51:38 +0200 Subject: Fix Rubocop offense in build specs --- spec/models/ci/build_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index e5982a44e89..5e27cca6771 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2545,7 +2545,7 @@ describe Ci::Build do before do allow(Gitlab.config.pages).to receive_messages(enabled: false) end - + it 'is not marked as pages generator' do expect(build).not_to be_pages_generator end -- cgit v1.2.1 From 3a4ecfd0c9a58bcac565426df552cec0cfe77017 Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Tue, 5 Jun 2018 12:55:41 +0000 Subject: Prevent Gitaly WriteConfig log noise --- spec/lib/gitlab/git/repository_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 7a9621d9c78..20b0b2c53a0 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -2002,6 +2002,18 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(config).to include("fullpath = #{repository_path}") end end + + context 'repository does not exist' do + it 'raises NoRepository and does not call Gitaly WriteConfig' do + repository = Gitlab::Git::Repository.new('default', 'does/not/exist.git', '') + + expect(repository.gitaly_repository_client).not_to receive(:write_config) + + expect do + repository.write_config(full_path: 'foo/bar.git') + end.to raise_error(Gitlab::Git::Repository::NoRepository) + end + end end context "when gitaly_write_config is enabled" do -- cgit v1.2.1 From 6ecf819f73ae28547e1b816780de0d85c3a653cf Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 19 Apr 2018 17:22:23 +0100 Subject: Fix an N+1 in avatar URLs This is tricky: the query was being run in `ObjectStorage::Extension::RecordsUploads#retrieve_from_store!`, but we can't just add batch loading there, because the `#upload=` method there would use the result immediately, making the batch only have one item. Instead, we can pre-emptively add an item to the batch whenever an avatarable object is initialized, and then reuse that batch item in `#retrieve_from_store!`. However, this also has problems: 1. There is a lot of logic in `Avatarable#retrieve_upload_from_batch`. 2. Some of that logic constructs a 'fake' model for the batch key. This should be fine, because of ActiveRecord's override of `#==`, but it relies on that staying the same. --- spec/uploaders/object_storage_spec.rb | 22 ++++++++++++++++++++++ .../object_storage/background_move_worker_spec.rb | 6 ++++-- 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 01166865e88..cda911032b2 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -739,4 +739,26 @@ describe ObjectStorage do end end end + + describe '#retrieve_from_store!' do + [:group, :project, :user].each do |model| + context "for #{model}s" do + let(:models) { create_list(model, 3, :with_avatar).map(&:reload) } + let(:avatars) { models.map(&:avatar) } + + it 'batches fetching uploads from the database' do + # Ensure that these are all created and fully loaded before we start + # running queries for avatars + models + + expect { avatars }.not_to exceed_query_limit(1) + end + + it 'fetches a unique upload for each model' do + expect(avatars.map(&:url).uniq).to eq(avatars.map(&:url)) + expect(avatars.map(&:upload).uniq).to eq(avatars.map(&:upload)) + end + end + end + end end diff --git a/spec/uploaders/workers/object_storage/background_move_worker_spec.rb b/spec/uploaders/workers/object_storage/background_move_worker_spec.rb index b34f427fd8a..95813d15e52 100644 --- a/spec/uploaders/workers/object_storage/background_move_worker_spec.rb +++ b/spec/uploaders/workers/object_storage/background_move_worker_spec.rb @@ -125,8 +125,10 @@ describe ObjectStorage::BackgroundMoveWorker do it "migrates file to remote storage" do perform + project.reload + BatchLoader::Executor.clear_current - expect(project.reload.avatar.file_storage?).to be_falsey + expect(project.avatar).not_to be_file_storage end end @@ -137,7 +139,7 @@ describe ObjectStorage::BackgroundMoveWorker do it "migrates file to remote storage" do perform - expect(project.reload.avatar.file_storage?).to be_falsey + expect(project.reload.avatar).not_to be_file_storage end end end -- cgit v1.2.1 From c8a4d202c99c772822a2b9b09fa6da2c90b2ae81 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 09:51:24 +0200 Subject: favicon uploader generating ci status favicons --- spec/uploaders/favicon_uploader_spec.rb | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 spec/uploaders/favicon_uploader_spec.rb (limited to 'spec') diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb new file mode 100644 index 00000000000..5989d294112 --- /dev/null +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +RSpec.describe FaviconUploader do + include CarrierWave::Test::Matchers + + let(:uploader) { described_class.new(build_stubbed(:user)) } + + after do + uploader.remove! + end + + def upload_fixture(filename) + fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) + end + + context 'versions' do + before do + uploader.store!(upload_fixture('dk.png')) + end + + it 'has the correct format' do + expect(uploader.default).to be_format('ico') + end + + it 'has the correct dimensions' do + expect(uploader.default).to have_dimensions(32, 32) + end + + it 'generates all the status icons' do + # make sure that the following each statement actually loops + expect(FaviconUploader::STATUS_ICON_NAMES.count).to eq 10 + + FaviconUploader::STATUS_ICON_NAMES.each do |status_name| + expect(File.exist?(uploader.status_not_found.file.file)).to be true + end + end + end +end -- cgit v1.2.1 From 40d8d7df4bd437efc81f0bdff5f93b4b65844cb5 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 16:07:53 +0200 Subject: feature spec for managing appearance > favicon --- spec/features/admin/admin_appearance_spec.rb | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'spec') diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index d91dcf76191..556aa10d226 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -76,6 +76,40 @@ feature 'Admin Appearance' do expect(page).not_to have_css(header_logo_selector) end + scenario 'Favicon' do + sign_in(create(:admin)) + visit admin_appearances_path + + attach_file(:appearance_favicon, logo_fixture) + click_button 'Save' + + expect(page).to have_css('//img[data-src$="/default_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_canceled_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_created_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_failed_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_manual_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_not_found_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_pending_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_running_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_skipped_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_success_dk.ico"]') + expect(page).to have_css('//img[data-src$="/status_warning_dk.ico"]') + + click_link 'Remove favicon' + + expect(page).not_to have_css('//img[data-src$="/default_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_canceled_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_created_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_failed_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_manual_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_not_found_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_pending_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_running_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_skipped_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_success_dk.ico"]') + expect(page).not_to have_css('//img[data-src$="/status_warning_dk.ico"]') + end + def expect_custom_sign_in_appearance(appearance) expect(page).to have_content appearance.title expect(page).to have_content appearance.description -- cgit v1.2.1 From 8967fc0477d176cb5b93ad3a9f2cf19eaca14876 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 16:22:04 +0200 Subject: use custom main favicon --- spec/helpers/page_layout_helper_spec.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'spec') diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index b77114a8152..53ecf25612f 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -55,6 +55,11 @@ describe PageLayoutHelper do stub_env('CANARY', 'true') expect(helper.favicon).to eq 'favicon-yellow.ico' end + + it 'uses the custom favicon if an favicon appearance is present' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + expect(helper.favicon).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} + end end describe 'page_image' do -- cgit v1.2.1 From a6f3f6b8cd2e79acbc824c401435284635071e1a Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 26 Sep 2017 17:04:37 +0200 Subject: extract favicon logic to lib class --- spec/helpers/page_layout_helper_spec.rb | 22 ---------------------- spec/lib/gitlab/favicon_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 spec/lib/gitlab/favicon_spec.rb (limited to 'spec') diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index 53ecf25612f..cf98eed27f1 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -40,28 +40,6 @@ describe PageLayoutHelper do end end - describe 'favicon' do - it 'defaults to favicon.ico' do - allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) - expect(helper.favicon).to eq 'favicon.ico' - end - - it 'has blue favicon for development' do - allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(helper.favicon).to eq 'favicon-blue.ico' - end - - it 'has yellow favicon for canary' do - stub_env('CANARY', 'true') - expect(helper.favicon).to eq 'favicon-yellow.ico' - end - - it 'uses the custom favicon if an favicon appearance is present' do - create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(helper.favicon).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} - end - end - describe 'page_image' do it 'defaults to the GitLab logo' do expect(helper.page_image).to match_asset_path 'assets/gitlab_logo.png' diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb new file mode 100644 index 00000000000..51a8ed47abf --- /dev/null +++ b/spec/lib/gitlab/favicon_spec.rb @@ -0,0 +1,25 @@ +require 'rails_helper' + +RSpec.describe Gitlab::Favicon do + describe '.default' do + it 'defaults to favicon.ico' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) + expect(described_class.default).to eq 'favicon.ico' + end + + it 'has blue favicon for development' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) + expect(described_class.default).to eq 'favicon-blue.ico' + end + + it 'has yellow favicon for canary' do + stub_env('CANARY', 'true') + expect(described_class.favicon).to eq 'favicon-yellow.ico' + end + + it 'uses the custom favicon if a favicon appearance is present' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} + end + end +end -- cgit v1.2.1 From 44d7b1583348513f8faa680a864efdbb39be70ab Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 13:18:49 +0200 Subject: use custom favicon for ci build status favicons --- spec/lib/gitlab/favicon_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 51a8ed47abf..d74dabf1458 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -22,4 +22,17 @@ RSpec.describe Gitlab::Favicon do expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} end end + + describe '.status' do + subject { described_class.status('created') } + + it 'defaults to the stock icon' do + expect(subject).to eq 'ci_favicons/favicon_status_created.ico' + end + + it 'uses the custom favicon if a favicon appearance is present' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico}) + end + end end -- cgit v1.2.1 From bf27c6841c1cb6b68f67d33d6eb2de63ad8b390f Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 13:21:58 +0200 Subject: send ico files with inline disposition --- spec/models/group_spec.rb | 2 +- spec/models/project_spec.rb | 2 +- spec/models/user_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index f83b52e8975..8f1f4938065 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -240,7 +240,7 @@ describe Group do it "is false if avatar is html page" do group.update_attribute(:avatar, 'uploads/avatar.html') - expect(group.avatar_type).to eq(["file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff"]) + expect(group.avatar_type).to eq(["file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico"]) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9a76452a808..ed1253c6d97 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -960,7 +960,7 @@ describe Project do it 'is false if avatar is html page' do project.update_attribute(:avatar, 'uploads/avatar.html') - expect(project.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff']) + expect(project.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico']) end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 09dfeae6377..097144d04ce 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1260,7 +1260,7 @@ describe User do it 'is false if avatar is html page' do user.update_attribute(:avatar, 'uploads/avatar.html') - expect(user.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff']) + expect(user.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico']) end end -- cgit v1.2.1 From 822023c64ccab23cfdacb42e191dcec4f812adfd Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 13:23:05 +0200 Subject: Add a '?' to the custom favicon's urls Without the '?' at the end of the favicon url the custom favicon (i.e. the favicons that are served through `UploadController`) are not shown in the browser. It may have something to do with how `#send_file` / `#send_data` set http headers. When serving the same icon file from the public directory everything is fine. --- spec/lib/gitlab/favicon_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index d74dabf1458..fe0bff51308 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -19,7 +19,7 @@ RSpec.describe Gitlab::Favicon do it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico} + expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico\?} end end @@ -32,7 +32,7 @@ RSpec.describe Gitlab::Favicon do it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico}) + expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico\?}) end end end -- cgit v1.2.1 From 85a8e6f26a8fa0ea9f430f0094fb14706bfd2991 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 27 Sep 2017 15:03:49 +0200 Subject: whitelist allowed file types for custom favicons --- spec/features/admin/admin_appearance_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'spec') diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 556aa10d226..ffffd14752e 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -108,6 +108,12 @@ feature 'Admin Appearance' do expect(page).not_to have_css('//img[data-src$="/status_skipped_dk.ico"]') expect(page).not_to have_css('//img[data-src$="/status_success_dk.ico"]') expect(page).not_to have_css('//img[data-src$="/status_warning_dk.ico"]') + + # allowed file types + attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) + click_button 'Save' + + expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, jpg, jpeg, gif, bmp, tiff, ico' end def expect_custom_sign_in_appearance(appearance) -- cgit v1.2.1 From 40ffa8401b96dda5f67ea699dbcca0ff64263810 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 28 Sep 2017 10:45:50 +0200 Subject: add request store caching to favicon --- spec/lib/gitlab/favicon_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index fe0bff51308..ddfa81c0b5d 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Gitlab::Favicon do +RSpec.describe Gitlab::Favicon, :request_store do describe '.default' do it 'defaults to favicon.ico' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) -- cgit v1.2.1 From 67fe0a17d87a7a5380b41e04ef23212d5da637ba Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 28 Sep 2017 13:57:08 +0200 Subject: call Gitlab::Favicon.status in serializer this ways we can keep the `lib/gitlab/ci/status/*` classes to return the bare favicon name as it was before. also the favicon uploader versions are now have the same names as the stock favicons (+ `favicon_` prefix), which makes working with the status names easier. --- spec/lib/gitlab/favicon_spec.rb | 16 ++++++++-------- spec/uploaders/favicon_uploader_spec.rb | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index ddfa81c0b5d..51b8fda81d1 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -1,38 +1,38 @@ require 'rails_helper' RSpec.describe Gitlab::Favicon, :request_store do - describe '.default' do + describe '.main' do it 'defaults to favicon.ico' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) - expect(described_class.default).to eq 'favicon.ico' + expect(described_class.main).to eq 'favicon.ico' end it 'has blue favicon for development' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(described_class.default).to eq 'favicon-blue.ico' + expect(described_class.main).to eq 'favicon-blue.ico' end it 'has yellow favicon for canary' do stub_env('CANARY', 'true') - expect(described_class.favicon).to eq 'favicon-yellow.ico' + expect(described_class.main).to eq 'favicon-yellow.ico' end it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(described_class.default).to match %r{/uploads/-/system/appearance/favicon/\d+/default_dk.ico\?} + expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.ico} end end describe '.status' do - subject { described_class.status('created') } + subject { described_class.status('favicon_status_created') } it 'defaults to the stock icon' do - expect(subject).to eq 'ci_favicons/favicon_status_created.ico' + expect(subject).to eq '/assets/ci_favicons/favicon_status_created.ico' end it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/status_created_dk.ico\?}) + expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/favicon_status_created_dk.ico}) end end end diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb index 5989d294112..b521670addb 100644 --- a/spec/uploaders/favicon_uploader_spec.rb +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -19,11 +19,11 @@ RSpec.describe FaviconUploader do end it 'has the correct format' do - expect(uploader.default).to be_format('ico') + expect(uploader.favicon_main).to be_format('ico') end it 'has the correct dimensions' do - expect(uploader.default).to have_dimensions(32, 32) + expect(uploader.favicon_main).to have_dimensions(32, 32) end it 'generates all the status icons' do @@ -31,7 +31,7 @@ RSpec.describe FaviconUploader do expect(FaviconUploader::STATUS_ICON_NAMES.count).to eq 10 FaviconUploader::STATUS_ICON_NAMES.each do |status_name| - expect(File.exist?(uploader.status_not_found.file.file)).to be true + expect(File.exist?(uploader.favicon_status_not_found.file.file)).to be true end end end -- cgit v1.2.1 From 5202c3f0c8da618e2d3821917f6f5d48ae8ae3c2 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 5 Dec 2017 18:47:31 +0100 Subject: fix resetFavicon so that it actually resets --- spec/javascripts/lib/utils/common_utils_spec.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 27f06573432..64d13275a59 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -395,6 +395,7 @@ describe('common_utils', () => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); favicon.setAttribute('href', 'default/favicon'); + favicon.setAttribute('data-default-href', 'default/favicon'); document.body.appendChild(favicon); }); @@ -413,7 +414,7 @@ describe('common_utils', () => { beforeEach(() => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); - favicon.setAttribute('href', 'default/favicon'); + favicon.setAttribute('data-original-href', 'default/favicon'); document.body.appendChild(favicon); }); @@ -421,7 +422,9 @@ describe('common_utils', () => { document.body.removeChild(document.getElementById('favicon')); }); - it('should reset page favicon to tanuki', () => { + it('should reset page favicon to the default icon', () => { + const favicon = document.getElementById('favicon'); + favicon.setAttribute('href', 'new/favicon'); commonUtils.resetFavicon(); expect(document.getElementById('favicon').getAttribute('href')).toEqual('default/favicon'); }); @@ -434,6 +437,8 @@ describe('common_utils', () => { beforeEach(() => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); + favicon.setAttribute('href', 'null'); + favicon.setAttribute('data-original-href', faviconDataUrl); document.body.appendChild(favicon); mock = new MockAdapter(axios); }); @@ -449,7 +454,7 @@ describe('common_utils', () => { commonUtils.setCiStatusFavicon(BUILD_URL) .then(() => { const favicon = document.getElementById('favicon'); - expect(favicon.getAttribute('href')).toEqual('null'); + expect(favicon.getAttribute('href')).toEqual(faviconDataUrl); done(); }) // Error is already caught in catch() block of setCiStatusFavicon, -- cgit v1.2.1 From 9e14f437b6ed205744d916f5566ee2c11e52b734 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 6 Dec 2017 21:04:53 +0100 Subject: create favicon overlay on the client the initial reason for this change was that graphicsmagick does not support writing to ico files. this fact lead to a chain of changes: 1. use png instead of ico (browser support is good enough) 2. render the overlays on the client using the canvas API. this way we only need to store the original favion and generate the overlay versions dynamically. this change also enables (next step) to simplify the handling of the stock favicons as well, as we don't need to generate all the versions upfront. --- spec/features/admin/admin_appearance_spec.rb | 27 +++------------- spec/javascripts/lib/utils/common_utils_spec.js | 36 +++++++++++++++++++--- spec/javascripts/lib/utils/mock_data.js | 5 +++ .../vue_mr_widget/mr_widget_options_spec.js | 13 +++++--- spec/lib/gitlab/favicon_spec.rb | 30 +++++++++++++----- spec/uploaders/favicon_uploader_spec.rb | 11 +------ 6 files changed, 73 insertions(+), 49 deletions(-) create mode 100644 spec/javascripts/lib/utils/mock_data.js (limited to 'spec') diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index ffffd14752e..0ac4f111c52 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -76,38 +76,19 @@ feature 'Admin Appearance' do expect(page).not_to have_css(header_logo_selector) end - scenario 'Favicon' do + scenario 'Favicon', :js do sign_in(create(:admin)) visit admin_appearances_path attach_file(:appearance_favicon, logo_fixture) click_button 'Save' - expect(page).to have_css('//img[data-src$="/default_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_canceled_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_created_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_failed_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_manual_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_not_found_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_pending_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_running_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_skipped_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_success_dk.ico"]') - expect(page).to have_css('//img[data-src$="/status_warning_dk.ico"]') + # 11 = 1 original + 10 overlay variations + expect(page).to have_css('.appearance-light-logo-preview', count: 11) click_link 'Remove favicon' - expect(page).not_to have_css('//img[data-src$="/default_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_canceled_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_created_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_failed_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_manual_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_not_found_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_pending_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_running_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_skipped_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_success_dk.ico"]') - expect(page).not_to have_css('//img[data-src$="/status_warning_dk.ico"]') + expect(page).not_to have_css('.appearance-light-logo-preview') # allowed file types attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 64d13275a59..2d7cc3443cf 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -2,6 +2,7 @@ import axios from '~/lib/utils/axios_utils'; import * as commonUtils from '~/lib/utils/common_utils'; import MockAdapter from 'axios-mock-adapter'; +import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data'; describe('common_utils', () => { describe('parseUrl', () => { @@ -430,6 +431,35 @@ describe('common_utils', () => { }); }); + describe('createOverlayIcon', () => { + it('should return the favicon with the overlay', (done) => { + commonUtils.createOverlayIcon(faviconDataUrl, overlayDataUrl).then((url) => { + expect(url).toEqual(faviconWithOverlayDataUrl); + done(); + }); + }); + }); + + describe('setFaviconOverlay', () => { + beforeEach(() => { + const favicon = document.createElement('link'); + favicon.setAttribute('id', 'favicon'); + favicon.setAttribute('data-original-href', faviconDataUrl); + document.body.appendChild(favicon); + }); + + afterEach(() => { + document.body.removeChild(document.getElementById('favicon')); + }); + + it('should set page favicon to provided favicon overlay', (done) => { + commonUtils.setFaviconOverlay(overlayDataUrl).then(() => { + expect(document.getElementById('favicon').getAttribute('href')).toEqual(faviconWithOverlayDataUrl); + done(); + }); + }); + }); + describe('setCiStatusFavicon', () => { const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`; let mock; @@ -463,16 +493,14 @@ describe('common_utils', () => { }); it('should set page favicon to CI status favicon based on provided status', (done) => { - const FAVICON_PATH = '//icon_status_success'; - mock.onGet(BUILD_URL).reply(200, { - favicon: FAVICON_PATH, + favicon: overlayDataUrl, }); commonUtils.setCiStatusFavicon(BUILD_URL) .then(() => { const favicon = document.getElementById('favicon'); - expect(favicon.getAttribute('href')).toEqual(FAVICON_PATH); + expect(favicon.getAttribute('href')).toEqual(faviconWithOverlayDataUrl); done(); }) .catch(done.fail); diff --git a/spec/javascripts/lib/utils/mock_data.js b/spec/javascripts/lib/utils/mock_data.js new file mode 100644 index 00000000000..fd0d62b751f --- /dev/null +++ b/spec/javascripts/lib/utils/mock_data.js @@ -0,0 +1,5 @@ +export const faviconDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC'; + +export const overlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC'; + +export const faviconWithOverlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGt0lEQVRYR8WWf3DT5R3H35/vN0lDiCztSuiPAEnTFhSOUamIzFGokwMH55g0E845yjbP6+4qIoiHY6JjnHLeRI6h9jgQpQcD3ImH2u00eHBwjGthKLI1TSm26S8oKYVS0vT7/X52z7ckTUhqC/yx55/kks/zeb+ez6/nIWYm/B8XJQB4SGq6lL+CJA47vvRtvWs2D0nNl/Kf0qBZxx6u23arv0QAAIHivK8BynB4ffa7BgDQXJx/ngGnw+uThgTwP5ZnMocoJAxVxZDVZ+0L5n5WF75TkMafjLdJxpSg2E+gqW1X7zk3rbpaifhLiEBgTv4mEFbpBoTyu01DoDj/dQAv9rvjtdnp/k3Yx9rgAMV5QYCsAAwAgg6vL/1OTy/2BYrzzwLIBWACuNHhrXPG+otGoKaw0JA58kqGJtOFfgPS8yWT4sz88nzj7UIIfz+wd0mRdEZPLMnp2V/8R0+JrhLbBYFHJvwWzBUxYgqYNzhG+zfEhm24MIE5ectBtP0W+y0Or29FcoDifHFSRxwAcMrh9c0YrmisXaA4r0V0U8xvopgDDq9PpCQ+Ag0/zbEbNUNbMiG9fTwkDTsKHpJa2t1Zmiw1AqLg+tMZ+R6WVVtnZ2qP6Ib+FIjh05G3lsDrB4xjUIbRDeM+WZLJYZ4B1rKMzKPm/fdyzs9qg6WT225IMnPcuYjxbPZhn57qaA00zc4/QYT7b1b/wAZmDYSLjsN1WcmiM+6jXz7JTCs1aNPASBjrtrCGOXVBLK9ph72772bc0REZcsQlkEVoOxblhaFBH0Bxi6GBWFNC8gpV0XqYSe/hI85R9o1zxr/QaZbdbmuW9oRzljRrzBRkW9JhMaTgYugKzl35DlXNJ/Fp43FImoZnz7T0ln7bLihM0g85N627vkWPgLrbvYyCvAP1+rRIWETA5QsyQlcJYOCbMRasWpALtljwSsFyeJxFYsoNWqdN1y/ildM78Y/WGjxx8TL+ol3oluy8VupKe7cfoNLdCJkdqEUPOmBJ5ksJoae91mBps5lQ6pkIm20MPiz6A3KsmcNukDe/3Ye3zh3A77Q2XqcGjslLz88i/nB8pkpSoL8nAFSTBpUN4qSxS5KB5jOGUOniCebmzFQcevSN2xKP+Fp7ajt21f8TOxU/5i45JZFS6XwcTB9HxZgUnGTRNgk31x5jet+aGU7jWw+UweOcPeyTxxoqrGL25+UwdjehSvnmOVIqcz4C8y8GAABcQwjnYI5NheikhQWT+EZmDh2ev/l7cz4U2cGmYyg78TYqVH87Kbtd1wFY4hsVQAt14zu2RiDaTUZMf/BHWD35STx37wDv94k1dLeh7MRmvDZ1GR5Inxg17dX6MPnjZfh5X6tGSqXrV2B8ACIx98UNGOlV4CxCuA6zqIeq9FQ8c68bhx7ZiIK06CQdVF+Il3y1Hq03gnDfk4Uj8zbH2T51dCPOtlW39Q+iPTl2VSMfwKPiKw8aTuhgpl1Zdqxzj8PphRWwm21xZjv9VcgYkYb52dP132PFbSYr/la0DpNtrrg9a2oqsKfB2zlwG+4nSe1z7QDjaQBi2Eh6J4QRwimYt43LwOsuB2oX7YLVMCLqTAya3xx/EwZJxtYHy3WhyMkHExebXz3zAbbXfdo7AFBRaMAz1Ypa6XoaoPejKRGteZm6D3SlWVdOcOHo/Lfj2u9aXw+WHNmA00G/DiFEO0Jd+meyk0fIf/+vLfik6Xhj4qN0v7i5HCY1bBQPk+ij9GSzNbzYNdH03kMrscARfzvHQgiBocSFTVHVCrW+u+WrpK9iCIgS1rRK93oG/1GkRJVIup8KMNs1Sw/1rUtALD36ZzRca8XeJDmPtRc18vDn5SCJViYHENY3IZTK3JkE7RAYtpdkp3bAaJeOzN+CsSMTX+wqa7ih9sbVSLI2WV3znihAJYXZPThA7M6KQoM2MniyhUxTioxTpKLMadjx8Jqh5k3S//8d9GOh92XWmP/aXLKvfHgA0ZTklL0jj9m6UR6L5+9bjFWTPLcFIWbCY1+8pHb0drWybJ4aWLQrODyAWJndzoyylNyGg0hL+bV7Ll4rKIWB5CFBxMlLj21SL4W6QjDQjwOL9n4tNt0+AADPfo+UqgXPHJLSJrkso7F6ylLMy56OFMmYACIKblvtQext8Iqp0swyLYiI3zEAbs6Ml3cXv/p3Y+ryq5KcnSKb1Jmj75P7X0Rm/UV0tvO86r/WIhORwszvkmHEehH2WMo7ikDUQUWhoaIG+NNc96Os8eMEmklE2Qy2ANTO0OrA+CwFOFBfsq8pWZ7+B25aDBxvPp+QAAAAAElFTkSuQmCC'; diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index 30918428da2..6342ea00436 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -5,6 +5,7 @@ import notify from '~/lib/utils/notify'; import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mockData from './mock_data'; +import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from '../lib/utils/mock_data'; const returnPromise = data => new Promise((resolve) => { resolve({ @@ -273,6 +274,7 @@ describe('mrWidgetOptions', () => { beforeEach(() => { const favicon = document.createElement('link'); favicon.setAttribute('id', 'favicon'); + favicon.setAttribute('data-original-href', faviconDataUrl); document.body.appendChild(favicon); faviconElement = document.getElementById('favicon'); @@ -282,10 +284,13 @@ describe('mrWidgetOptions', () => { document.body.removeChild(document.getElementById('favicon')); }); - it('should call setFavicon method', () => { - vm.setFaviconHelper(); - - expect(faviconElement.getAttribute('href')).toEqual(vm.mr.ciStatusFaviconPath); + it('should call setFavicon method', (done) => { + vm.mr.ciStatusFaviconPath = overlayDataUrl; + vm.setFaviconHelper().then(() => { + expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl); + done(); + }) + .catch(done.fail); }); it('should not call setFavicon when there is no ciStatusFaviconPath', () => { diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 51b8fda81d1..22b9c631ed8 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -19,20 +19,34 @@ RSpec.describe Gitlab::Favicon, :request_store do it 'uses the custom favicon if a favicon appearance is present' do create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.ico} + expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png} end end - describe '.status' do - subject { described_class.status('favicon_status_created') } + describe '.status_overlay' do + subject { described_class.status_overlay('favicon_status_created') } - it 'defaults to the stock icon' do - expect(subject).to eq '/assets/ci_favicons/favicon_status_created.ico' + it 'returns the overlay for the status' do + expect(subject).to eq '/assets/ci_favicons/overlays/favicon_status_created.png' end + end - it 'uses the custom favicon if a favicon appearance is present' do - create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) - expect(subject).to match(%r{/uploads/-/system/appearance/favicon/\d+/favicon_status_created_dk.ico}) + describe '.available_status_names' do + subject { described_class.available_status_names } + + it 'returns the available status names' do + expect(subject).to eq %w( + favicon_status_canceled + favicon_status_created + favicon_status_failed + favicon_status_manual + favicon_status_not_found + favicon_status_pending + favicon_status_running + favicon_status_skipped + favicon_status_success + favicon_status_warning + ) end end end diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb index b521670addb..db8a3207f4d 100644 --- a/spec/uploaders/favicon_uploader_spec.rb +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -19,20 +19,11 @@ RSpec.describe FaviconUploader do end it 'has the correct format' do - expect(uploader.favicon_main).to be_format('ico') + expect(uploader.favicon_main).to be_format('png') end it 'has the correct dimensions' do expect(uploader.favicon_main).to have_dimensions(32, 32) end - - it 'generates all the status icons' do - # make sure that the following each statement actually loops - expect(FaviconUploader::STATUS_ICON_NAMES.count).to eq 10 - - FaviconUploader::STATUS_ICON_NAMES.each do |status_name| - expect(File.exist?(uploader.favicon_status_not_found.file.file)).to be true - end - end end end -- cgit v1.2.1 From 949c30d42b91a0dd3959a3ca303b8f76158a2556 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 7 Dec 2017 13:15:49 +0100 Subject: remove all .ico favicon variations, use png always the ci status icons are generated client side, wo we don't need the static files anymore. --- spec/controllers/projects/jobs_controller_spec.rb | 2 +- .../projects/merge_requests_controller_spec.rb | 2 +- spec/controllers/projects/pipelines_controller_spec.rb | 2 +- .../user_creates_image_diff_notes_spec.rb | 2 +- spec/javascripts/jobs/mock_data.js | 4 ++-- spec/javascripts/pipelines/graph/mock_data.js | 18 +++++++++--------- spec/lib/gitlab/favicon_spec.rb | 10 +++++----- spec/models/project_services/jira_service_spec.rb | 2 +- spec/serializers/build_serializer_spec.rb | 4 ++-- spec/serializers/pipeline_serializer_spec.rb | 2 +- spec/serializers/status_entity_spec.rb | 6 +++--- spec/services/system_note_service_spec.rb | 6 +++--- 12 files changed, 30 insertions(+), 30 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index a08fcea27a5..06c8a432561 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -265,7 +265,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico" + expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png" end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 6e8de6db9c3..eb83884d54e 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -681,7 +681,7 @@ describe Projects::MergeRequestsController do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico" + expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png" end end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 9e7bc20a6d1..c524f5d7f8a 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -190,7 +190,7 @@ describe Projects::PipelinesController do expect(json_response['text']).to eq status.text expect(json_response['label']).to eq status.label expect(json_response['icon']).to eq status.icon - expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb index 7c4fd25bb39..25c408516d1 100644 --- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb +++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb @@ -12,7 +12,7 @@ feature 'Merge request > User creates image diff notes', :js do # Stub helper to return any blob file as image from public app folder. # This is necessary to run this specs since we don't display repo images in capybara. allow_any_instance_of(DiffHelper).to receive(:diff_file_blob_raw_url).and_return('/apple-touch-icon.png') - allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.ico') + allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.png') end context 'create commit diff notes' do diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js index 25ca8eb6c0b..dd025255bd1 100644 --- a/spec/javascripts/jobs/mock_data.js +++ b/spec/javascripts/jobs/mock_data.js @@ -20,7 +20,7 @@ export default { group: 'success', has_details: true, details_path: '/root/ci-mock/-/jobs/4757', - favicon: '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -78,7 +78,7 @@ export default { group: 'success', has_details: true, details_path: '/root/ci-mock/pipelines/140', - favicon: '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, duration: 6, finished_at: '2017-06-01T17:32:00.042Z', diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js index 70eba98e939..9e25a4b3fed 100644 --- a/spec/javascripts/pipelines/graph/mock_data.js +++ b/spec/javascripts/pipelines/graph/mock_data.js @@ -20,7 +20,7 @@ export default { has_details: true, details_path: '/root/ci-mock/pipelines/123', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, duration: 9, finished_at: '2017-04-19T14:30:27.542Z', @@ -40,7 +40,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4153', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -65,7 +65,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4153', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -85,7 +85,7 @@ export default { has_details: true, details_path: '/root/ci-mock/pipelines/123#test', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, path: '/root/ci-mock/pipelines/123#test', dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test', @@ -105,7 +105,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4166', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -130,7 +130,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4166', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -152,7 +152,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4159', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -177,7 +177,7 @@ export default { has_details: true, details_path: '/root/ci-mock/builds/4159', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -197,7 +197,7 @@ export default { has_details: true, details_path: '/root/ci-mock/pipelines/123#deploy', favicon: - '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico', + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, path: '/root/ci-mock/pipelines/123#deploy', dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=deploy', diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 22b9c631ed8..fdc5c0180e4 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -2,19 +2,19 @@ require 'rails_helper' RSpec.describe Gitlab::Favicon, :request_store do describe '.main' do - it 'defaults to favicon.ico' do + it 'defaults to favicon.png' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) - expect(described_class.main).to eq 'favicon.ico' + expect(described_class.main).to match_asset_path '/assets/favicon.png' end it 'has blue favicon for development' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(described_class.main).to eq 'favicon-blue.ico' + expect(described_class.main).to match_asset_path '/assets/favicon-blue.png' end it 'has yellow favicon for canary' do stub_env('CANARY', 'true') - expect(described_class.main).to eq 'favicon-yellow.ico' + expect(described_class.main).to match_asset_path 'favicon-yellow.png' end it 'uses the custom favicon if a favicon appearance is present' do @@ -27,7 +27,7 @@ RSpec.describe Gitlab::Favicon, :request_store do subject { described_class.status_overlay('favicon_status_created') } it 'returns the overlay for the status' do - expect(subject).to eq '/assets/ci_favicons/overlays/favicon_status_created.png' + expect(subject).to match_asset_path '/assets/ci_favicons/favicon_status_created.png' end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 54ef0be67ff..3a6a6c116c2 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -173,7 +173,7 @@ describe JiraService do object: { url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}", title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: true } } ) diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb index 98cd15e248b..52459cd369d 100644 --- a/spec/serializers/build_serializer_spec.rb +++ b/spec/serializers/build_serializer_spec.rb @@ -39,7 +39,7 @@ describe BuildSerializer do expect(subject[:label]).to eq('failed') expect(subject[:tooltip]).to eq('failed
(unknown failure)') expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end @@ -54,7 +54,7 @@ describe BuildSerializer do expect(subject[:label]).to eq('passed') expect(subject[:tooltip]).to eq('passed') expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end end diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index b741308e2c5..33c8213f9a7 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -174,7 +174,7 @@ describe PipelineSerializer do expect(subject[:text]).to eq(status.text) expect(subject[:label]).to eq(status.label) expect(subject[:icon]).to eq(status.icon) - expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png") end end end diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index 559475e571c..73f81405d54 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -18,17 +18,17 @@ describe StatusEntity do it 'contains status details' do expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip expect(subject).to include :has_details, :details_path - expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico') + expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') end it 'contains a dev namespaced favicon if dev env' do allow(Rails.env).to receive(:development?) { true } - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/dev/favicon_status_success.ico') + expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') end it 'contains a canary namespaced favicon if canary env' do stub_env('CANARY', 'true') - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/canary/favicon_status_success.ico') + expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/canary/favicon_status_success.png') end end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index e28b0ea5cf2..b7f38874c26 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -789,7 +789,7 @@ describe SystemNoteService do object: { url: project_commit_url(project, commit), title: "GitLab: Mentioned on commit - #{commit.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: false } } ) @@ -815,7 +815,7 @@ describe SystemNoteService do object: { url: project_issue_url(project, issue), title: "GitLab: Mentioned on issue - #{issue.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: false } } ) @@ -841,7 +841,7 @@ describe SystemNoteService do object: { url: project_snippet_url(project, snippet), title: "GitLab: Mentioned on snippet - #{snippet.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" }, + icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, status: { resolved: false } } ) -- cgit v1.2.1 From b4d84c07bcf143aeab7abccb8d0cdb849f605af5 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Mon, 5 Feb 2018 15:41:37 +0100 Subject: remove favicon preview on appearance page --- spec/features/admin/admin_appearance_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 0ac4f111c52..bd879635d2f 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -76,15 +76,14 @@ feature 'Admin Appearance' do expect(page).not_to have_css(header_logo_selector) end - scenario 'Favicon', :js do + scenario 'Favicon' do sign_in(create(:admin)) visit admin_appearances_path attach_file(:appearance_favicon, logo_fixture) click_button 'Save' - # 11 = 1 original + 10 overlay variations - expect(page).to have_css('.appearance-light-logo-preview', count: 11) + expect(page).to have_css('.appearance-light-logo-preview') click_link 'Remove favicon' -- cgit v1.2.1 From 256d959729f14094a490c102508e2878c1dd87fc Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 12 Apr 2018 14:11:21 +0200 Subject: ability to get an image's alternative version --- spec/controllers/uploads_controller_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'spec') diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 376b229ffc9..ae62039fb32 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -560,5 +560,27 @@ describe UploadsController do end end end + + context 'the version filename must match' do + let!(:appearance) { create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + + context 'has a valid filename on the version file' do + it 'successfully returns the file' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png' + + expect(response).to have_gitlab_http_status(200) + expect(response.header['Content-Disposition']).to eq 'inline; filename="favicon_main_dk.png"' + end + end + + context 'has an invalid filename on the version file' do + it 'returns the original file' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png' + + expect(response).to have_gitlab_http_status(200) + expect(response.header['Content-Disposition']).to eq 'inline; filename="dk.png"' + end + end + end end end -- cgit v1.2.1 From 96d0b1c67bc1f2a2881298ff898954ba00cd563f Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Thu, 12 Apr 2018 14:13:06 +0200 Subject: require uploaded file's name to match in any case --- spec/controllers/uploads_controller_spec.rb | 92 +++++++++++++++++------------ 1 file changed, 54 insertions(+), 38 deletions(-) (limited to 'spec') diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index ae62039fb32..912aa82526a 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -136,7 +136,7 @@ describe UploadsController do context 'for PNG files' do it 'returns Content-Disposition: inline' do note = create(:note, :with_attachment, project: project) - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' expect(response['Content-Disposition']).to start_with('inline;') end @@ -145,7 +145,7 @@ describe UploadsController do context 'for SVG files' do it 'returns Content-Disposition: attachment' do note = create(:note, :with_svg_attachment, project: project) - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.svg' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'unsanitized.svg' expect(response['Content-Disposition']).to start_with('attachment;') end @@ -164,7 +164,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -172,14 +172,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'image.png' + get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png' response end @@ -189,14 +189,14 @@ describe UploadsController do context "when not signed in" do it "responds with status 200" do - get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'image.png' + get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png' response end @@ -214,14 +214,14 @@ describe UploadsController do context "when not signed in" do it "responds with status 200" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png' + get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' response end @@ -234,14 +234,14 @@ describe UploadsController do end it "responds with status 200" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png' + get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' response end @@ -256,7 +256,7 @@ describe UploadsController do context "when not signed in" do it "redirects to the sign in page" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -279,7 +279,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -287,14 +287,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png' + get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png' response end @@ -304,7 +304,7 @@ describe UploadsController do context "when the user doesn't have access to the project" do it "responds with status 404" do - get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png" expect(response).to have_gitlab_http_status(404) end @@ -319,14 +319,14 @@ describe UploadsController do context "when the group is public" do context "when not signed in" do it "responds with status 200" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png' + get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' response end @@ -339,14 +339,14 @@ describe UploadsController do end it "responds with status 200" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png' + get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' response end @@ -375,7 +375,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -383,14 +383,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png' + get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png' response end @@ -400,7 +400,7 @@ describe UploadsController do context "when the user doesn't have access to the project" do it "responds with status 404" do - get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png" expect(response).to have_gitlab_http_status(404) end @@ -420,14 +420,14 @@ describe UploadsController do context "when not signed in" do it "responds with status 200" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' response end @@ -440,14 +440,14 @@ describe UploadsController do end it "responds with status 200" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' response end @@ -462,7 +462,7 @@ describe UploadsController do context "when not signed in" do it "redirects to the sign in page" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -485,7 +485,7 @@ describe UploadsController do end it "redirects to the sign in page" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to redirect_to(new_user_session_path) end @@ -493,14 +493,14 @@ describe UploadsController do context "when the user isn't blocked" do it "responds with status 200" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(200) end it_behaves_like 'content not cached without revalidation' do subject do - get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' response end @@ -510,7 +510,7 @@ describe UploadsController do context "when the user doesn't have access to the project" do it "responds with status 404" do - get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png" expect(response).to have_gitlab_http_status(404) end @@ -561,24 +561,40 @@ describe UploadsController do end end - context 'the version filename must match' do + context 'original filename or a version filename must match' do let!(:appearance) { create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + context 'has a valid filename on the original file' do + it 'successfully returns the file' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'dk.png' + + expect(response).to have_gitlab_http_status(200) + expect(response.header['Content-Disposition']).to end_with 'filename="dk.png"' + end + end + + context 'has an invalid filename on the original file' do + it 'returns a 404' do + get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'bogus.png' + + expect(response).to have_gitlab_http_status(404) + end + end + context 'has a valid filename on the version file' do it 'successfully returns the file' do get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png' expect(response).to have_gitlab_http_status(200) - expect(response.header['Content-Disposition']).to eq 'inline; filename="favicon_main_dk.png"' + expect(response.header['Content-Disposition']).to end_with 'filename="favicon_main_dk.png"' end end context 'has an invalid filename on the version file' do - it 'returns the original file' do + it 'returns a 404' do get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png' - expect(response).to have_gitlab_http_status(200) - expect(response.header['Content-Disposition']).to eq 'inline; filename="dk.png"' + expect(response).to have_gitlab_http_status(404) end end end -- cgit v1.2.1 From 7706b3a471de37240c0da6fcc14c7d38c6d62eb9 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Fri, 13 Apr 2018 22:30:08 +0200 Subject: use Gitlab::Favicon for jira service --- spec/models/project_services/jira_service_spec.rb | 18 +++++++++++++++++- spec/services/system_note_service_spec.rb | 8 +++++--- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 3a6a6c116c2..50bdb80ff92 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -164,6 +164,8 @@ describe JiraService do it "creates Remote Link reference in JIRA for comment" do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) + favicon_path = "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" + # Creates comment expect(WebMock).to have_requested(:post, @comment_url) # Creates Remote Link in JIRA issue fields @@ -173,7 +175,7 @@ describe JiraService do object: { url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}", title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: true } } ) @@ -464,4 +466,18 @@ describe JiraService do end end end + + describe 'favicon urls', :request_store do + it 'includes the standard favicon' do + props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title') + expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/assets/favicon(?:-\h+).png$} + end + + it 'includes returns the custom favicon' do + create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + + props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title') + expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$} + end + end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index b7f38874c26..e2ee421921c 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -769,6 +769,8 @@ describe SystemNoteService do end describe "new reference" do + let(:favicon_path) { "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" } + before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) end @@ -789,7 +791,7 @@ describe SystemNoteService do object: { url: project_commit_url(project, commit), title: "GitLab: Mentioned on commit - #{commit.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: false } } ) @@ -815,7 +817,7 @@ describe SystemNoteService do object: { url: project_issue_url(project, issue), title: "GitLab: Mentioned on issue - #{issue.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: false } } ) @@ -841,7 +843,7 @@ describe SystemNoteService do object: { url: project_snippet_url(project, snippet), title: "GitLab: Mentioned on snippet - #{snippet.title}", - icon: { title: "GitLab", url16x16: "http://localhost/favicon.png" }, + icon: { title: "GitLab", url16x16: favicon_path }, status: { resolved: false } } ) -- cgit v1.2.1 From 1e9c33acd1129557124e330df2f178fb097d67e5 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Sat, 14 Apr 2018 15:37:32 +0200 Subject: remove obsolete favicon related spec the favicon variations don't need to be tested on the entity anymore, as they are A) tested in the dedicated `Gitlab::Favicon` and B) they are created on the client (i.e. the overlays are always the same for all base favicon variants). --- spec/serializers/status_entity_spec.rb | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'spec') diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index 73f81405d54..0b010ebd507 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -20,15 +20,5 @@ describe StatusEntity do expect(subject).to include :has_details, :details_path expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') end - - it 'contains a dev namespaced favicon if dev env' do - allow(Rails.env).to receive(:development?) { true } - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') - end - - it 'contains a canary namespaced favicon if canary env' do - stub_env('CANARY', 'true') - expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/canary/favicon_status_success.png') - end end end -- cgit v1.2.1 From 197932a2225904898778e7edadc0e447b91cc2ef Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Mon, 30 Apr 2018 12:45:07 +0200 Subject: allow only png, ico for favicon uploads the related omnibus graphicsmagick package only supports those formats. see https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1975 --- spec/features/admin/admin_appearance_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index bd879635d2f..a5e0ac592b9 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -93,7 +93,7 @@ feature 'Admin Appearance' do attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) click_button 'Save' - expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, jpg, jpeg, gif, bmp, tiff, ico' + expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, ico' end def expect_custom_sign_in_appearance(appearance) -- cgit v1.2.1 From 6f4a0a92e6331b7033a8165c5b5e9cc7b0f880c2 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 3 Jun 2018 04:36:22 -0700 Subject: Eliminate N+1 queries with authors and push_data_payload in Events API --- spec/requests/api/events_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index 962c845f36d..e6a61fdcf39 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -176,7 +176,7 @@ describe API::Events do end it 'avoids N+1 queries' do - control_count = ActiveRecord::QueryRecorder.new do + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do get api("/projects/#{private_project.id}/events", user), target_type: :merge_request end.count @@ -184,7 +184,7 @@ describe API::Events do expect do get api("/projects/#{private_project.id}/events", user), target_type: :merge_request - end.not_to exceed_query_limit(control_count) + end.not_to exceed_all_query_limit(control_count) expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers -- cgit v1.2.1 From 53d1c87c8673fa6c9d83519490698b37d67572df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Tue, 5 Jun 2018 16:17:04 +0200 Subject: Fix Fog mocking --- spec/initializers/fog_google_https_private_urls_spec.rb | 12 ++++++------ spec/lib/backup/manager_spec.rb | 7 ++----- spec/lib/object_storage/direct_upload_spec.rb | 4 ++++ spec/spec_helper.rb | 4 ++++ spec/uploaders/object_storage_spec.rb | 7 ------- 5 files changed, 16 insertions(+), 18 deletions(-) (limited to 'spec') diff --git a/spec/initializers/fog_google_https_private_urls_spec.rb b/spec/initializers/fog_google_https_private_urls_spec.rb index de3c157ab7b..08346b71fee 100644 --- a/spec/initializers/fog_google_https_private_urls_spec.rb +++ b/spec/initializers/fog_google_https_private_urls_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe 'Fog::Storage::GoogleXML::File' do +describe 'Fog::Storage::GoogleXML::File', :fog_requests do let(:storage) do Fog.mock! - Fog::Storage.new({ - google_storage_access_key_id: "asdf", - google_storage_secret_access_key: "asdf", - provider: "Google" - }) + Fog::Storage.new( + google_storage_access_key_id: "asdf", + google_storage_secret_access_key: "asdf", + provider: "Google" + ) end let(:file) do diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb index 23c04a1a101..ca319679e80 100644 --- a/spec/lib/backup/manager_spec.rb +++ b/spec/lib/backup/manager_spec.rb @@ -274,16 +274,13 @@ describe Backup::Manager do } ) - # the Fog mock only knows about directories we create explicitly Fog.mock! + + # the Fog mock only knows about directories we create explicitly connection = ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys) connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) end - after do - Fog.unmock! - end - context 'target path' do it 'uses the tar filename by default' do expect_any_instance_of(Fog::Collection).to receive(:create) diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb index 5187821e8f4..e0569218d78 100644 --- a/spec/lib/object_storage/direct_upload_spec.rb +++ b/spec/lib/object_storage/direct_upload_spec.rb @@ -17,6 +17,10 @@ describe ObjectStorage::DirectUpload do let(:direct_upload) { described_class.new(credentials, bucket_name, object_name, has_length: has_length, maximum_size: maximum_size) } + before do + Fog.unmock! + end + describe '#has_length' do context 'is known' do let(:has_length) { true } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d3de2331244..e093444121a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -133,6 +133,10 @@ RSpec.configure do |config| RequestStore.clear! end + config.after(:example) do + Fog.unmock! if Fog.mock? + end + config.before(:example, :mailer) do reset_delivered_emails! end diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 01166865e88..fb51ae4c02c 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -360,13 +360,6 @@ describe ObjectStorage do subject { uploader_class.workhorse_authorize(has_length: has_length, maximum_size: maximum_size) } - before do - # ensure that we use regular Fog libraries - # other tests might call `Fog.mock!` and - # it will make tests to fail - Fog.unmock! - end - shared_examples 'uses local storage' do it "returns temporary path" do is_expected.to have_key(:TempPath) -- cgit v1.2.1 From a0808df0b627180d7773d5d13a0f64d6e7c45f5d Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Tue, 5 Jun 2018 15:51:14 +0000 Subject: Find and mark more Git disk access locations --- .../projects/import_export/import_file_spec.rb | 6 ++- spec/lib/backup/files_spec.rb | 4 +- spec/lib/backup/repository_spec.rb | 4 +- .../gitlab/bare_repository_import/importer_spec.rb | 7 ++++ .../bare_repository_import/repository_spec.rb | 6 ++- spec/lib/gitlab/git/repository_spec.rb | 4 ++ spec/lib/gitlab/git/rev_list_spec.rb | 4 +- spec/lib/gitlab/git_access_spec.rb | 32 ++++++++-------- spec/lib/gitlab/import_export/fork_spec.rb | 6 ++- .../lib/gitlab/import_export/repo_restorer_spec.rb | 10 +++-- spec/lib/gitlab/shell_spec.rb | 6 ++- spec/models/merge_request_spec.rb | 12 +++++- spec/models/namespace_spec.rb | 10 ++++- spec/models/repository_spec.rb | 43 +++++++++++++++++++--- spec/requests/api/tags_spec.rb | 5 ++- spec/services/projects/transfer_service_spec.rb | 14 +++++-- spec/services/projects/update_service_spec.rb | 4 +- spec/support/helpers/cycle_analytics_helpers.rb | 6 ++- spec/tasks/gitlab/backup_rake_spec.rb | 19 +++++++--- .../single_repository_worker_spec.rb | 8 +++- 20 files changed, 158 insertions(+), 52 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 60fe30bd898..d0912e645bc 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -87,11 +87,13 @@ feature 'Import/Export - project import integration test', :js do def wiki_exists?(project) wiki = ProjectWiki.new(project) - File.exist?(wiki.repository.path_to_repo) && !wiki.repository.empty? + wiki.repository.exists? && !wiki.repository.empty? end def project_hook_exists?(project) - Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository).exists? + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository).exists? + end end def click_import_project_tab diff --git a/spec/lib/backup/files_spec.rb b/spec/lib/backup/files_spec.rb index 99872211a4e..63f2298357f 100644 --- a/spec/lib/backup/files_spec.rb +++ b/spec/lib/backup/files_spec.rb @@ -46,7 +46,9 @@ describe Backup::Files do end it 'calls tar command with unlink' do - expect(subject).to receive(:run_pipeline!).with([%w(gzip -cd), %w(tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -)], any_args) + expect(subject).to receive(:tar).and_return('blabla-tar') + + expect(subject).to receive(:run_pipeline!).with([%w(gzip -cd), %w(blabla-tar --unlink-first --recursive-unlink -C /var/gitlab-registry -xf -)], any_args) subject.restore end end diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb index f583b2021a2..92a27e308d2 100644 --- a/spec/lib/backup/repository_spec.rb +++ b/spec/lib/backup/repository_spec.rb @@ -34,7 +34,9 @@ describe Backup::Repository do let(:timestamp) { Time.utc(2017, 3, 22) } let(:temp_dirs) do Gitlab.config.repositories.storages.map do |name, storage| - File.join(storage.legacy_disk_path, '..', 'repositories.old.' + timestamp.to_i.to_s) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + File.join(storage.legacy_disk_path, '..', 'repositories.old.' + timestamp.to_i.to_s) + end end end diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb index 5c8a19a53bc..468f6ff6d24 100644 --- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb @@ -20,6 +20,13 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do Rainbow.enabled = @rainbow end + around do |example| + # TODO migrate BareRepositoryImport https://gitlab.com/gitlab-org/gitaly/issues/953 + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + shared_examples 'importing a repository' do describe '.execute' do it 'creates a project for a repository in storage' do diff --git a/spec/lib/gitlab/bare_repository_import/repository_spec.rb b/spec/lib/gitlab/bare_repository_import/repository_spec.rb index 1504826c7a5..afd8f5da39f 100644 --- a/spec/lib/gitlab/bare_repository_import/repository_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/repository_spec.rb @@ -62,8 +62,10 @@ describe ::Gitlab::BareRepositoryImport::Repository do before do gitlab_shell.create_repository(repository_storage, hashed_path) - repository = Rugged::Repository.new(repo_path) - repository.config['gitlab.fullpath'] = 'to/repo' + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository = Rugged::Repository.new(repo_path) + repository.config['gitlab.fullpath'] = 'to/repo' + end end after do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 20b0b2c53a0..8dd7911f49c 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -159,6 +159,7 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:feature2) { 'feature2' } around do |example| + # discover_default_branch will be moved to gitaly-ruby Gitlab::GitalyClient::StorageSettings.allow_disk_access do example.run end @@ -373,6 +374,7 @@ describe Gitlab::Git::Repository, seed_helper: true do context '#submodules' do around do |example| + # TODO #submodules will be removed, has been migrated to gitaly Gitlab::GitalyClient::StorageSettings.allow_disk_access do example.run end @@ -1055,6 +1057,7 @@ describe Gitlab::Git::Repository, seed_helper: true do describe "#rugged_commits_between" do around do |example| + # TODO #rugged_commits_between will be removed, has been migrated to gitaly Gitlab::GitalyClient::StorageSettings.allow_disk_access do example.run end @@ -1703,6 +1706,7 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:refs) { ['deadbeef', SeedRepo::RubyBlob::ID, '909e6157199'] } around do |example| + # TODO #batch_existence isn't used anywhere, can we remove it? Gitlab::GitalyClient::StorageSettings.allow_disk_access do example.run end diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 32ec1e029c8..95dc47e2a00 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -9,9 +9,11 @@ describe Gitlab::Git::RevList do end def stub_popen_rev_list(*additional_args, with_lazy_block: true, output:) + repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.path } + params = [ args_for_popen(additional_args), - repository.path, + repo_path, {}, hash_including(lazy_block: with_lazy_block ? anything : nil) ] diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index dfffea7797f..0d5f6a0b576 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -552,7 +552,7 @@ describe Gitlab::GitAccess do it 'returns not found' do project.add_guest(user) repo = project.repository - FileUtils.rm_rf(repo.path) + Gitlab::GitalyClient::StorageSettings.allow_disk_access { FileUtils.rm_rf(repo.path) } # Sanity check for rm_rf expect(repo.exists?).to eq(false) @@ -750,20 +750,22 @@ describe Gitlab::GitAccess do def merge_into_protected_branch @protected_branch_merge_commit ||= begin - stub_git_hooks - project.repository.add_branch(user, unprotected_branch, 'feature') - target_branch = project.repository.lookup('feature') - source_branch = project.repository.create_file( - user, - 'filename', - 'This is the file content', - message: 'This is a good commit message', - branch_name: unprotected_branch) - rugged = project.repository.rugged - author = { email: "email@example.com", time: Time.now, name: "Example Git User" } - - merge_index = rugged.merge_commits(target_branch, source_branch) - Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged)) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + stub_git_hooks + project.repository.add_branch(user, unprotected_branch, 'feature') + target_branch = project.repository.lookup('feature') + source_branch = project.repository.create_file( + user, + 'filename', + 'This is the file content', + message: 'This is a good commit message', + branch_name: unprotected_branch) + rugged = project.repository.rugged + author = { email: "email@example.com", time: Time.now, name: "Example Git User" } + + merge_index = rugged.merge_commits(target_branch, source_branch) + Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged)) + end end end diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb index 17e06a6a83f..71fd5a51c3b 100644 --- a/spec/lib/gitlab/import_export/fork_spec.rb +++ b/spec/lib/gitlab/import_export/fork_spec.rb @@ -41,8 +41,10 @@ describe 'forked project import' do after do FileUtils.rm_rf(export_path) - FileUtils.rm_rf(project_with_repo.repository.path_to_repo) - FileUtils.rm_rf(project.repository.path_to_repo) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + FileUtils.rm_rf(project_with_repo.repository.path_to_repo) + FileUtils.rm_rf(project.repository.path_to_repo) + end end it 'can access the MR' do diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb index dc806d036ff..013b8895f67 100644 --- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb @@ -23,8 +23,10 @@ describe Gitlab::ImportExport::RepoRestorer do after do FileUtils.rm_rf(export_path) - FileUtils.rm_rf(project_with_repo.repository.path_to_repo) - FileUtils.rm_rf(project.repository.path_to_repo) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + FileUtils.rm_rf(project_with_repo.repository.path_to_repo) + FileUtils.rm_rf(project.repository.path_to_repo) + end end it 'restores the repo successfully' do @@ -34,7 +36,9 @@ describe Gitlab::ImportExport::RepoRestorer do it 'has the webhooks' do restorer.restore - expect(Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository)).to exist + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + expect(Gitlab::Git::Hook.new('post-receive', project.repository.raw_repository)).to exist + end end end end diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index bf6ee4b0b59..14eae22a2ec 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -405,7 +405,11 @@ describe Gitlab::Shell do describe '#create_repository' do shared_examples '#create_repository' do let(:repository_storage) { 'default' } - let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage].legacy_disk_path } + let(:repository_storage_path) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab.config.repositories.storages[repository_storage].legacy_disk_path + end + end let(:repo_name) { 'project/path' } let(:created_path) { File.join(repository_storage_path, repo_name + '.git') } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 7e85a99494a..3f028b3bd5c 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -16,7 +16,11 @@ describe MergeRequest do describe '#squash_in_progress?' do shared_examples 'checking whether a squash is in progress' do - let(:repo_path) { subject.source_project.repository.path } + let(:repo_path) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + subject.source_project.repository.path + end + end let(:squash_path) { File.join(repo_path, "gitlab-worktree", "squash-#{subject.id}") } before do @@ -2197,7 +2201,11 @@ describe MergeRequest do describe '#rebase_in_progress?' do shared_examples 'checking whether a rebase is in progress' do - let(:repo_path) { subject.source_project.repository.path } + let(:repo_path) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + subject.source_project.repository.path + end + end let(:rebase_path) { File.join(repo_path, "gitlab-worktree", "rebase-#{subject.id}") } before do diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 6f702d8d95e..18b01c3e6b7 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -301,12 +301,18 @@ describe Namespace do end def project_rugged(project) - project.repository.rugged + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.rugged + end end end describe '#rm_dir', 'callback' do - let(:repository_storage_path) { Gitlab.config.repositories.storages.default.legacy_disk_path } + let(:repository_storage_path) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab.config.repositories.storages.default.legacy_disk_path + end + end let(:path_in_dir) { File.join(repository_storage_path, namespace.full_path) } let(:deleted_path) { namespace.full_path.gsub(namespace.path, "#{namespace.full_path}+#{namespace.id}+deleted") } let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 0ccf55bd895..c1aa7d80c94 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -136,7 +136,10 @@ describe Repository do before do options = { message: 'test tag message\n', tagger: { name: 'John Smith', email: 'john@gmail.com' } } - repository.rugged.tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options) + + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged.tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options) + end double_first = double(committed_date: Time.now - 1.second) double_last = double(committed_date: Time.now) @@ -1048,6 +1051,13 @@ describe Repository do let(:target_project) { project } let(:target_repository) { target_project.repository } + around do |example| + # TODO Gitlab::Git::OperationService will be moved to gitaly-ruby and disappear from this repo + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + context 'when pre hooks were successful' do before do service = Gitlab::Git::HooksService.new @@ -1309,6 +1319,13 @@ describe Repository do end describe '#update_autocrlf_option' do + around do |example| + # TODO Gitlab::Git::OperationService will be moved to gitaly-ruby and disappear from this repo + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + describe 'when autocrlf is not already set to :input' do before do repository.raw_repository.autocrlf = true @@ -1802,7 +1819,9 @@ describe Repository do expect(repository.branch_count).to be_an(Integer) # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync - rugged_count = repository.raw_repository.rugged.branches.count + rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.raw_repository.rugged.branches.count + end expect(repository.branch_count).to eq(rugged_count) end @@ -1813,7 +1832,9 @@ describe Repository do expect(repository.tag_count).to be_an(Integer) # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync - rugged_count = repository.raw_repository.rugged.tags.count + rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.raw_repository.rugged.tags.count + end expect(repository.tag_count).to eq(rugged_count) end @@ -2073,7 +2094,10 @@ describe Repository do it "attempting to call keep_around on truncated ref does not fail" do repository.keep_around(sample_commit.id) ref = repository.send(:keep_around_ref_name, sample_commit.id) - path = File.join(repository.path, ref) + + path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + File.join(repository.path, ref) + end # Corrupt the reference File.truncate(path, 0) @@ -2088,6 +2112,13 @@ describe Repository do end describe '#update_ref' do + around do |example| + # TODO Gitlab::Git::OperationService will be moved to gitaly-ruby and disappear from this repo + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + it 'can create a ref' do Gitlab::Git::OperationService.new(nil, repository.raw_repository).send(:update_ref, 'refs/heads/foobar', 'refs/heads/master', Gitlab::Git::BLANK_SHA) @@ -2372,7 +2403,9 @@ describe Repository do end def create_remote_branch(remote_name, branch_name, target) - rugged = repository.rugged + rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged + end rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index e2b19ad59f9..969710d6613 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -287,7 +287,10 @@ describe API::Tags do context 'annotated tag' do it 'creates a new annotated tag' do # Identity must be set in .gitconfig to create annotated tag. - repo_path = project.repository.path_to_repo + repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.path_to_repo + end + system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name})) system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email})) diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 3e6483d7e28..5100987c2fe 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -64,7 +64,7 @@ describe Projects::TransferService do it 'updates project full path in .git/config' do transfer_project(project, user, group) - expect(project.repository.rugged.config['gitlab.fullpath']).to eq "#{group.full_path}/#{project.path}" + expect(rugged_config['gitlab.fullpath']).to eq "#{group.full_path}/#{project.path}" end end @@ -84,7 +84,9 @@ describe Projects::TransferService do end def project_path(project) - project.repository.path_to_repo + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.path_to_repo + end end def current_path @@ -101,7 +103,7 @@ describe Projects::TransferService do it 'rolls back project full path in .git/config' do attempt_project_transfer - expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path + expect(rugged_config['gitlab.fullpath']).to eq project.full_path end it "doesn't send move notifications" do @@ -264,4 +266,10 @@ describe Projects::TransferService do transfer_project(project, owner, group) end end + + def rugged_config + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.rugged.config + end + end end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 1f761bcbbad..ecf1ba05618 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -125,7 +125,7 @@ describe Projects::UpdateService do context 'when we update project but not enabling a wiki' do it 'does not try to create an empty wiki' do - FileUtils.rm_rf(project.wiki.repository.path) + Gitlab::Shell.new.rm_directory(project.repository_storage, project.wiki.path) result = update_project(project, user, { name: 'test1' }) @@ -146,7 +146,7 @@ describe Projects::UpdateService do context 'when enabling a wiki' do it 'creates a wiki' do project.project_feature.update(wiki_access_level: ProjectFeature::DISABLED) - FileUtils.rm_rf(project.wiki.repository.path) + Gitlab::Shell.new.rm_directory(project.repository_storage, project.wiki.path) result = update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED }) diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index 06a76d53354..32d9807f06a 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -123,7 +123,11 @@ module CycleAnalyticsHelpers if branch_update.newrev _, opts = args - commit = raw_repository.commit(branch_update.newrev).rugged_commit + + commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + raw_repository.commit(branch_update.newrev).rugged_commit + end + branch_update.newrev = commit.amend( update_ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}#{opts[:branch_name]}", author: commit.author.merge(time: new_date), diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index c9252bebb2e..93a436cb2b5 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -101,7 +101,9 @@ describe 'gitlab:app namespace rake task' do before do stub_env('SKIP', 'db') - path = File.join(project.repository.path_to_repo, 'custom_hooks') + path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + File.join(project.repository.path_to_repo, 'custom_hooks') + end FileUtils.mkdir_p(path) FileUtils.touch(File.join(path, "dummy.txt")) end @@ -122,7 +124,10 @@ describe 'gitlab:app namespace rake task' do expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout expect { run_rake_task('gitlab:backup:restore') }.to output.to_stdout - expect(Dir.entries(File.join(project.repository.path, 'custom_hooks'))).to include("dummy.txt") + repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.path + end + expect(Dir.entries(File.join(repo_path, 'custom_hooks'))).to include("dummy.txt") end end @@ -243,10 +248,12 @@ describe 'gitlab:app namespace rake task' do FileUtils.mkdir_p(b_storage_dir) # Even when overriding the storage, we have to move it there, so it exists - FileUtils.mv( - File.join(Settings.absolute(storages['default'].legacy_disk_path), project_b.repository.disk_path + '.git'), - Rails.root.join(storages['test_second_storage'].legacy_disk_path, project_b.repository.disk_path + '.git') - ) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + FileUtils.mv( + File.join(Settings.absolute(storages['default'].legacy_disk_path), project_b.repository.disk_path + '.git'), + Rails.root.join(storages['test_second_storage'].legacy_disk_path, project_b.repository.disk_path + '.git') + ) + end expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb index a021235aed6..22fc64c1536 100644 --- a/spec/workers/repository_check/single_repository_worker_spec.rb +++ b/spec/workers/repository_check/single_repository_worker_spec.rb @@ -88,7 +88,9 @@ describe RepositoryCheck::SingleRepositoryWorker do end def break_wiki(project) - break_repo(wiki_path(project)) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + break_repo(wiki_path(project)) + end end def wiki_path(project) @@ -96,7 +98,9 @@ describe RepositoryCheck::SingleRepositoryWorker do end def break_project(project) - break_repo(project.repository.path_to_repo) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + break_repo(project.repository.path_to_repo) + end end def break_repo(repo) -- cgit v1.2.1 From 7519a25a6161b2ca540ca9fc4e42e4b03aed4eeb Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 5 Jun 2018 17:21:33 +0100 Subject: Use `started` key to show the triggered date --- spec/javascripts/jobs/header_spec.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/jobs/header_spec.js b/spec/javascripts/jobs/header_spec.js index 4f861c39d3f..cef30a007db 100644 --- a/spec/javascripts/jobs/header_spec.js +++ b/spec/javascripts/jobs/header_spec.js @@ -13,6 +13,9 @@ describe('Job details header', () => { const threeWeeksAgo = new Date(); threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); + const twoDaysAgo = new Date(); + twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); + props = { job: { status: { @@ -31,7 +34,7 @@ describe('Job details header', () => { email: 'foo@bar.com', avatar_url: 'link', }, - started: '2018-01-08T09:48:27.319Z', + started: twoDaysAgo.toISOString(), new_issue_path: 'path', }, isLoading: false, @@ -69,7 +72,7 @@ describe('Job details header', () => { .querySelector('.header-main-content') .textContent.replace(/\s+/g, ' ') .trim(), - ).toEqual('failed Job #123 triggered 3 weeks ago by Foo'); + ).toEqual('failed Job #123 triggered 2 days ago by Foo'); }); it('should render new issue link', () => { -- cgit v1.2.1 From df5b4c0038c31385c3e4452115becd9f8fc335e4 Mon Sep 17 00:00:00 2001 From: Sam Beckham Date: Tue, 5 Jun 2018 16:38:22 +0000 Subject: Resolve "BS4: Pagination is pipeline's page has no styling" --- spec/javascripts/vue_shared/components/table_pagination_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js index c63f15e5880..c36b607a34e 100644 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js +++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js @@ -51,7 +51,7 @@ describe('Pagination component', () => { expect( component.$el.querySelector('.js-previous-button').classList.contains('disabled'), - ).toEqual(true); + ).toEqual(true); component.$el.querySelector('.js-previous-button a').click(); -- cgit v1.2.1 From fc916b068199f2624c5b9cfeb0389760448910ff Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Sun, 27 May 2018 22:02:47 +0200 Subject: Migrate jobs in object_storage_upload queue --- ...ate_object_storage_upload_sidekiq_queue_spec.rb | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 spec/migrations/migrate_object_storage_upload_sidekiq_queue_spec.rb (limited to 'spec') diff --git a/spec/migrations/migrate_object_storage_upload_sidekiq_queue_spec.rb b/spec/migrations/migrate_object_storage_upload_sidekiq_queue_spec.rb new file mode 100644 index 00000000000..1ee6c440cf4 --- /dev/null +++ b/spec/migrations/migrate_object_storage_upload_sidekiq_queue_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20180603190921_migrate_object_storage_upload_sidekiq_queue.rb') + +describe MigrateObjectStorageUploadSidekiqQueue, :sidekiq, :redis do + include Gitlab::Database::MigrationHelpers + + context 'when there are jobs in the queue' do + it 'correctly migrates queue when migrating up' do + Sidekiq::Testing.disable! do + stubbed_worker(queue: 'object_storage_upload').perform_async('Something', [1]) + stubbed_worker(queue: 'object_storage:object_storage_background_move').perform_async('Something', [1]) + + described_class.new.up + + expect(sidekiq_queue_length('object_storage_upload')).to eq 0 + expect(sidekiq_queue_length('object_storage:object_storage_background_move')).to eq 2 + end + end + end + + context 'when there are no jobs in the queues' do + it 'does not raise error when migrating up' do + expect { described_class.new.up }.not_to raise_error + end + end + + def stubbed_worker(queue:) + Class.new do + include Sidekiq::Worker + sidekiq_options queue: queue + end + end +end -- cgit v1.2.1 From 9c6c17cbcdb8bf8185fc1b873dcfd08f723e4df5 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 16 Aug 2017 14:04:41 +0100 Subject: Add a minimal GraphQL API --- spec/controllers/graphql_controller_spec.rb | 58 ++++++++++++++++++++++++ spec/graphql/gitlab_schema_spec.rb | 27 +++++++++++ spec/graphql/loaders/full_path_loader_spec.rb | 38 ++++++++++++++++ spec/graphql/loaders/iid_loader_spec.rb | 64 +++++++++++++++++++++++++++ spec/graphql/types/query_type_spec.rb | 37 ++++++++++++++++ spec/graphql/types/time_type_spec.rb | 16 +++++++ spec/lib/gitlab/path_regex_spec.rb | 12 ++--- spec/support/helpers/graphql_helpers.rb | 29 ++++++++++++ spec/support/matchers/graphql_matchers.rb | 31 +++++++++++++ 9 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 spec/controllers/graphql_controller_spec.rb create mode 100644 spec/graphql/gitlab_schema_spec.rb create mode 100644 spec/graphql/loaders/full_path_loader_spec.rb create mode 100644 spec/graphql/loaders/iid_loader_spec.rb create mode 100644 spec/graphql/types/query_type_spec.rb create mode 100644 spec/graphql/types/time_type_spec.rb create mode 100644 spec/support/helpers/graphql_helpers.rb create mode 100644 spec/support/matchers/graphql_matchers.rb (limited to 'spec') diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb new file mode 100644 index 00000000000..d6689dbc3c6 --- /dev/null +++ b/spec/controllers/graphql_controller_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe GraphqlController do + describe 'execute' do + before do + sign_in(user) if user + + run_test_query! + end + + subject { query_response } + + context 'graphql is disabled by feature flag' do + let(:user) { nil } + + before do + stub_feature_flags(graphql: false) + end + + it 'returns 404' do + run_test_query! + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'signed out' do + let(:user) { nil } + + it 'runs the query with current_user: nil' do + is_expected.to eq('echo' => 'nil says: test success') + end + end + + context 'signed in' do + let(:user) { create(:user, username: 'Simon') } + + it 'runs the query with current_user set' do + is_expected.to eq('echo' => '"Simon" says: test success') + end + end + end + + # Chosen to exercise all the moving parts in GraphqlController#execute + def run_test_query! + query = <<~QUERY + query Echo($text: String) { + echo(text: $text) + } + QUERY + + post :execute, query: query, operationName: 'Echo', variables: { 'text' => 'test success' } + end + + def query_response + json_response['data'] + end +end diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb new file mode 100644 index 00000000000..3582f297866 --- /dev/null +++ b/spec/graphql/gitlab_schema_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe GitlabSchema do + it 'uses batch loading' do + expect(described_class.instrumenters[:multiplex]).to include(GraphQL::Batch::SetupMultiplex) + end + + it 'enables the preload instrumenter' do + expect(field_instrumenters).to include(instance_of(::GraphQL::Preload::Instrument)) + end + + it 'enables the authorization instrumenter' do + expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Authorize)) + end + + it 'has the base mutation' do + expect(described_class.mutation).to eq(::Types::MutationType) + end + + it 'has the base query' do + expect(described_class.query).to eq(::Types::QueryType) + end + + def field_instrumenters + described_class.instrumenters[:field] + end +end diff --git a/spec/graphql/loaders/full_path_loader_spec.rb b/spec/graphql/loaders/full_path_loader_spec.rb new file mode 100644 index 00000000000..2a473239550 --- /dev/null +++ b/spec/graphql/loaders/full_path_loader_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe Loaders::FullPathLoader do + include GraphqlHelpers + + set(:project1) { create(:project) } + set(:project2) { create(:project) } + + set(:other_project) { create(:project) } + + describe '.project' do + it 'batch-resolves projects by full path' do + paths = [project1.full_path, project2.full_path] + + result = batch(max_queries: 1) do + paths.map { |path| resolve_project(path) } + end + + expect(result).to contain_exactly(project1, project2) + end + + it 'resolves an unknown full_path to nil' do + result = batch { resolve_project('unknown/project') } + + expect(result).to be_nil + end + + it 'returns a promise' do + batch do + expect(resolve_project(project1.full_path)).to be_a(Promise) + end + end + end + + def resolve_project(full_path) + resolve(described_class, :project, args: { full_path: full_path }) + end +end diff --git a/spec/graphql/loaders/iid_loader_spec.rb b/spec/graphql/loaders/iid_loader_spec.rb new file mode 100644 index 00000000000..8a0c1f0791a --- /dev/null +++ b/spec/graphql/loaders/iid_loader_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe Loaders::IidLoader do + include GraphqlHelpers + + set(:project) { create(:project, :repository) } + set(:merge_request_1) { create(:merge_request, :simple, source_project: project, target_project: project) } + set(:merge_request_2) { create(:merge_request, :rebased, source_project: project, target_project: project) } + + set(:other_project) { create(:project, :repository) } + set(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) } + + let(:full_path) { project.full_path } + let(:iid_1) { merge_request_1.iid } + let(:iid_2) { merge_request_2.iid } + + let(:other_full_path) { other_project.full_path } + let(:other_iid) { other_merge_request.iid } + + describe '.merge_request' do + it 'batch-resolves merge requests by target project full path and IID' do + path = full_path # avoid database query + + result = batch(max_queries: 2) do + [resolve_mr(path, iid_1), resolve_mr(path, iid_2)] + end + + expect(result).to contain_exactly(merge_request_1, merge_request_2) + end + + it 'can batch-resolve merge requests from different projects' do + path = project.full_path # avoid database queries + other_path = other_full_path + + result = batch(max_queries: 3) do + [resolve_mr(path, iid_1), resolve_mr(path, iid_2), resolve_mr(other_path, other_iid)] + end + + expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request) + end + + it 'resolves an unknown iid to nil' do + result = batch { resolve_mr(full_path, -1) } + + expect(result).to be_nil + end + + it 'resolves a known iid for an unknown full_path to nil' do + result = batch { resolve_mr('unknown/project', iid_1) } + + expect(result).to be_nil + end + + it 'returns a promise' do + batch do + expect(resolve_mr(full_path, iid_1)).to be_a(Promise) + end + end + end + + def resolve_mr(full_path, iid) + resolve(described_class, :merge_request, args: { project: full_path, iid: iid }) + end +end diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb new file mode 100644 index 00000000000..17d9395504c --- /dev/null +++ b/spec/graphql/types/query_type_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe GitlabSchema.types['Query'] do + it 'is called Query' do + expect(described_class.name).to eq('Query') + end + + it { is_expected.to have_graphql_fields(:project, :merge_request, :echo) } + + describe 'project field' do + subject { described_class.fields['project'] } + + it 'finds projects by full path' do + is_expected.to have_graphql_arguments(:full_path) + is_expected.to have_graphql_type(Types::ProjectType) + is_expected.to have_graphql_resolver(Loaders::FullPathLoader[:project]) + end + + it 'authorizes with read_project' do + is_expected.to require_graphql_authorizations(:read_project) + end + end + + describe 'merge_request field' do + subject { described_class.fields['merge_request'] } + + it 'finds MRs by project and IID' do + is_expected.to have_graphql_arguments(:project, :iid) + is_expected.to have_graphql_type(Types::MergeRequestType) + is_expected.to have_graphql_resolver(Loaders::IidLoader[:merge_request]) + end + + it 'authorizes with read_merge_request' do + is_expected.to require_graphql_authorizations(:read_merge_request) + end + end +end diff --git a/spec/graphql/types/time_type_spec.rb b/spec/graphql/types/time_type_spec.rb new file mode 100644 index 00000000000..087655cc67d --- /dev/null +++ b/spec/graphql/types/time_type_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe GitlabSchema.types['Time'] do + let(:float) { 1504630455.96215 } + let(:time) { Time.at(float) } + + it { expect(described_class.name).to eq('Time') } + + it 'coerces Time into fractional seconds since epoch' do + expect(described_class.coerce_isolated_result(time)).to eq(float) + end + + it 'coerces fractional seconds since epoch into Time' do + expect(described_class.coerce_isolated_input(float)).to eq(time) + end +end diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb index a40330d853f..e90e0aba0a4 100644 --- a/spec/lib/gitlab/path_regex_spec.rb +++ b/spec/lib/gitlab/path_regex_spec.rb @@ -90,11 +90,13 @@ describe Gitlab::PathRegex do let(:routes_not_starting_in_wildcard) { routes_without_format.select { |p| p !~ %r{^/[:*]} } } let(:top_level_words) do - words = routes_not_starting_in_wildcard.map do |route| - route.split('/')[1] - end.compact - - (words + ee_top_level_words + files_in_public + Array(API::API.prefix.to_s)).uniq + routes_not_starting_in_wildcard + .map { |route| route.split('/')[1] } + .concat(ee_top_level_words) + .concat(files_in_public) + .concat(Array(API::API.prefix.to_s)) + .compact + .uniq end let(:ee_top_level_words) do diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb new file mode 100644 index 00000000000..5bb2cf9dd9e --- /dev/null +++ b/spec/support/helpers/graphql_helpers.rb @@ -0,0 +1,29 @@ +module GraphqlHelpers + # Run a loader's named resolver + def resolve(kls, name, obj: nil, args: {}, ctx: {}) + kls[name].call(obj, args, ctx) + end + + # Runs a block inside a GraphQL::Batch wrapper + def batch(max_queries: nil, &blk) + wrapper = proc do + GraphQL::Batch.batch do + result = yield + + if result.is_a?(Array) + Promise.all(result) + else + result + end + end + end + + if max_queries + result = nil + expect { result = wrapper.call }.not_to exceed_query_limit(max_queries) + result + else + wrapper.call + end + end +end diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb new file mode 100644 index 00000000000..c0ed16ecaba --- /dev/null +++ b/spec/support/matchers/graphql_matchers.rb @@ -0,0 +1,31 @@ +RSpec::Matchers.define :require_graphql_authorizations do |*expected| + match do |field| + authorizations = field.metadata[:authorize] + + expect(authorizations).to contain_exactly(*expected) + end +end + +RSpec::Matchers.define :have_graphql_fields do |*expected| + match do |kls| + expect(kls.fields.keys).to contain_exactly(*expected.map(&:to_s)) + end +end + +RSpec::Matchers.define :have_graphql_arguments do |*expected| + match do |field| + expect(field.arguments.keys).to contain_exactly(*expected.map(&:to_s)) + end +end + +RSpec::Matchers.define :have_graphql_type do |expected| + match do |field| + expect(field.type).to eq(expected) + end +end + +RSpec::Matchers.define :have_graphql_resolver do |expected| + match do |field| + expect(field.resolve_proc).to eq(expected) + end +end -- cgit v1.2.1 From 287c34ca1f9af4e395493c99623af8437f82d919 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 23 Feb 2018 15:36:40 +0000 Subject: Convert from GraphQL::Batch to BatchLoader --- spec/graphql/gitlab_schema_spec.rb | 2 +- spec/graphql/loaders/full_path_loader_spec.rb | 6 ------ spec/graphql/loaders/iid_loader_spec.rb | 6 ------ spec/support/helpers/graphql_helpers.rb | 17 +++++++---------- 4 files changed, 8 insertions(+), 23 deletions(-) (limited to 'spec') diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 3582f297866..070b851b109 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe GitlabSchema do it 'uses batch loading' do - expect(described_class.instrumenters[:multiplex]).to include(GraphQL::Batch::SetupMultiplex) + expect(field_instrumenters).to include(BatchLoader::GraphQL) end it 'enables the preload instrumenter' do diff --git a/spec/graphql/loaders/full_path_loader_spec.rb b/spec/graphql/loaders/full_path_loader_spec.rb index 2a473239550..2732dd8c9da 100644 --- a/spec/graphql/loaders/full_path_loader_spec.rb +++ b/spec/graphql/loaders/full_path_loader_spec.rb @@ -24,12 +24,6 @@ describe Loaders::FullPathLoader do expect(result).to be_nil end - - it 'returns a promise' do - batch do - expect(resolve_project(project1.full_path)).to be_a(Promise) - end - end end def resolve_project(full_path) diff --git a/spec/graphql/loaders/iid_loader_spec.rb b/spec/graphql/loaders/iid_loader_spec.rb index 8a0c1f0791a..0a57d7c4ed4 100644 --- a/spec/graphql/loaders/iid_loader_spec.rb +++ b/spec/graphql/loaders/iid_loader_spec.rb @@ -50,12 +50,6 @@ describe Loaders::IidLoader do expect(result).to be_nil end - - it 'returns a promise' do - batch do - expect(resolve_mr(full_path, iid_1)).to be_a(Promise) - end - end end def resolve_mr(full_path, iid) diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 5bb2cf9dd9e..1eaa7603af0 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -4,20 +4,17 @@ module GraphqlHelpers kls[name].call(obj, args, ctx) end - # Runs a block inside a GraphQL::Batch wrapper + # Runs a block inside a BatchLoader::Executor wrapper def batch(max_queries: nil, &blk) wrapper = proc do - GraphQL::Batch.batch do - result = yield - - if result.is_a?(Array) - Promise.all(result) - else - result - end + begin + BatchLoader::Executor.ensure_current + blk.call + ensure + BatchLoader::Executor.clear_current end end - + if max_queries result = nil expect { result = wrapper.call }.not_to exceed_query_limit(max_queries) -- cgit v1.2.1 From aa4b1ae71260720b47695b8a256134f20280f61a Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 21 May 2018 09:52:24 +0200 Subject: Add `present_using` to types By specifying a presenter for the object type, we can keep the logic out of `GitlabSchema`. The presenter gets initialized using the object being presented, and the context (including the `current_user`). --- spec/graphql/gitlab_schema_spec.rb | 7 +++++ spec/requests/graphql/merge_request_query_spec.rb | 24 ++++++++++++++++ spec/requests/graphql/project_query_spec.rb | 23 +++++++++++++++ spec/support/helpers/graphql_helpers.rb | 33 ++++++++++++++++++++-- .../requests/graphql_shared_examples.rb | 18 ++++++++++++ 5 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 spec/requests/graphql/merge_request_query_spec.rb create mode 100644 spec/requests/graphql/project_query_spec.rb create mode 100644 spec/support/shared_examples/requests/graphql_shared_examples.rb (limited to 'spec') diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 070b851b109..f25cc2fd6c9 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -13,7 +13,14 @@ describe GitlabSchema do expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Authorize)) end + it 'enables using presenters' do + expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Present)) + end + it 'has the base mutation' do + pending <<~REASON + Having empty mutations breaks the automatic documentation in Graphiql, so removed for now." + REASON expect(described_class.mutation).to eq(::Types::MutationType) end diff --git a/spec/requests/graphql/merge_request_query_spec.rb b/spec/requests/graphql/merge_request_query_spec.rb new file mode 100644 index 00000000000..cbc19782e2f --- /dev/null +++ b/spec/requests/graphql/merge_request_query_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe 'getting merge request information' do + include GraphqlHelpers + + let(:project) { create(:project, :repository, :public) } + let(:merge_request) { create(:merge_request, source_project: project) } + + let(:query) do + <<~QUERY + { + merge_request(project: "#{merge_request.project.full_path}", iid: "#{merge_request.iid}") { + #{all_graphql_fields_for(MergeRequest)} + } + } + QUERY + end + + it_behaves_like 'a working graphql query' do + it 'renders a merge request with all fields' do + expect(response_data['merge_request']).not_to be_nil + end + end +end diff --git a/spec/requests/graphql/project_query_spec.rb b/spec/requests/graphql/project_query_spec.rb new file mode 100644 index 00000000000..110ed433e03 --- /dev/null +++ b/spec/requests/graphql/project_query_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe 'getting project information' do + include GraphqlHelpers + + let(:project) { create(:project, :repository, :public) } + + let(:query) do + <<~QUERY + { + project(full_path: "#{project.full_path}") { + #{all_graphql_fields_for(Project)} + } + } + QUERY + end + + it_behaves_like 'a working graphql query' do + it 'renders a project with all fields' do + expect(response_data['project']).not_to be_nil + end + end +end diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 1eaa7603af0..ae29b16e32c 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -9,12 +9,12 @@ module GraphqlHelpers wrapper = proc do begin BatchLoader::Executor.ensure_current - blk.call + yield ensure BatchLoader::Executor.clear_current end end - + if max_queries result = nil expect { result = wrapper.call }.not_to exceed_query_limit(max_queries) @@ -23,4 +23,33 @@ module GraphqlHelpers wrapper.call end end + + def all_graphql_fields_for(klass) + type = GitlabSchema.types[klass.name] + return "" unless type + + type.fields.map do |name, field| + if scalar?(field) + name + else + "#{name} { #{all_graphql_fields_for(field_type(field))} }" + end + end.join("\n") + end + + def post_graphql(query) + post '/api/graphql', query: query + end + + def scalar?(field) + field_type(field).kind.scalar? + end + + def field_type(field) + if field.type.respond_to?(:of_type) + field.type.of_type + else + field.type + end + end end diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb new file mode 100644 index 00000000000..c143b83696e --- /dev/null +++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +shared_examples 'a working graphql query' do + include GraphqlHelpers + + let(:parsed_response) { JSON.parse(response.body) } + let(:response_data) { parsed_response['data'] } + + before do + post_graphql(query) + end + + it 'is returns a successfull response', :aggregate_failures do + expect(response).to be_success + expect(parsed_response['errors']).to be_nil + expect(response_data).not_to be_empty + end +end -- cgit v1.2.1 From c443133e779c4c508b9c6429dd4ba623d64f03f1 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 21 May 2018 13:42:07 +0200 Subject: Handle exceptions outside the GraphQL schema This allows us to report JSON parse exceptions to clients and ignore them in sentry. --- spec/controllers/graphql_controller_spec.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index d6689dbc3c6..1449036e148 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' describe GraphqlController do describe 'execute' do + let(:user) { nil } + before do sign_in(user) if user @@ -39,17 +41,26 @@ describe GraphqlController do is_expected.to eq('echo' => '"Simon" says: test success') end end + + context 'invalid variables' do + it 'returns an error' do + run_test_query!(variables: "This is not JSON") + + expect(response).to have_gitlab_http_status(422) + expect(json_response['errors'].first['message']).not_to be_nil + end + end end # Chosen to exercise all the moving parts in GraphqlController#execute - def run_test_query! + def run_test_query!(variables: { 'text' => 'test success' }) query = <<~QUERY query Echo($text: String) { echo(text: $text) } QUERY - post :execute, query: query, operationName: 'Echo', variables: { 'text' => 'test success' } + post :execute, query: query, operationName: 'Echo', variables: variables end def query_response -- cgit v1.2.1 From 65dbead7967937eb87ffdc987c319d7006662889 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Tue, 5 Jun 2018 19:22:55 +0100 Subject: Fix repository archive generation when hashed storage is enabled --- spec/lib/gitlab/git/repository_spec.rb | 2 +- spec/models/repository_spec.rb | 26 ++++++++++++++++++++++++++ spec/requests/api/repositories_spec.rb | 9 +++------ 3 files changed, 30 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 8dd7911f49c..1744db1b17e 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -256,7 +256,7 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:expected_path) { File.join(storage_path, cache_key, expected_filename) } let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" } - subject(:metadata) { repository.archive_metadata(ref, storage_path, format, append_sha: append_sha) } + subject(:metadata) { repository.archive_metadata(ref, storage_path, 'gitlab-git-test', format, append_sha: append_sha) } it 'sets CommitId to the commit SHA' do expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c1aa7d80c94..7c0a1cd967c 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2443,6 +2443,32 @@ describe Repository do end end + describe '#archive_metadata' do + let(:ref) { 'master' } + let(:storage_path) { '/tmp' } + + let(:prefix) { [project.path, ref].join('-') } + let(:filename) { prefix + '.tar.gz' } + + subject(:result) { repository.archive_metadata(ref, storage_path, append_sha: false) } + + context 'with hashed storage disabled' do + let(:project) { create(:project, :repository, :legacy_storage) } + + it 'uses the project path to generate the filename' do + expect(result['ArchivePrefix']).to eq(prefix) + expect(File.basename(result['ArchivePath'])).to eq(filename) + end + end + + context 'with hashed storage enabled' do + it 'uses the project path to generate the filename' do + expect(result['ArchivePrefix']).to eq(prefix) + expect(File.basename(result['ArchivePath'])).to eq(filename) + end + end + end + describe 'commit cache' do set(:project) { create(:project, :repository) } diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 9e6d69e3874..cd135dfc32a 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -220,11 +220,10 @@ describe API::Repositories do expect(response).to have_gitlab_http_status(200) - repo_name = project.repository.name.gsub("\.git", "") type, params = workhorse_send_data expect(type).to eq('git-archive') - expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/) + expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\.tar.gz/) end it 'returns the repository archive archive.zip' do @@ -232,11 +231,10 @@ describe API::Repositories do expect(response).to have_gitlab_http_status(200) - repo_name = project.repository.name.gsub("\.git", "") type, params = workhorse_send_data expect(type).to eq('git-archive') - expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/) + expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\.zip/) end it 'returns the repository archive archive.tar.bz2' do @@ -244,11 +242,10 @@ describe API::Repositories do expect(response).to have_gitlab_http_status(200) - repo_name = project.repository.name.gsub("\.git", "") type, params = workhorse_send_data expect(type).to eq('git-archive') - expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/) + expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\.tar.bz2/) end context 'when sha does not exist' do -- cgit v1.2.1 From 46317b7dabd585bed42cf9c0008a7c69c080d33f Mon Sep 17 00:00:00 2001 From: Jacopo Date: Tue, 5 Jun 2018 21:57:29 +0200 Subject: Removes variables from pipelines api --- spec/requests/api/pipelines_spec.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index 0a64c46bb92..78ea77cb3bb 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -285,6 +285,15 @@ describe API::Pipelines do end describe 'POST /projects/:id/pipeline ' do + def expect_variables(variables, expected_variables) + variables.each_with_index do |variable, index| + expected_variable = expected_variables[index] + + expect(variable.key).to eq(expected_variable['key']) + expect(variable.value).to eq(expected_variable['value']) + end + end + context 'authorized user' do context 'with gitlab-ci.yml' do before do @@ -308,11 +317,12 @@ describe API::Pipelines do expect do post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch, variables: variables end.to change { project.pipelines.count }.by(1) + expect_variables(project.pipelines.last.variables, variables) expect(response).to have_gitlab_http_status(201) expect(json_response).to be_a Hash expect(json_response['sha']).to eq project.commit.id - expect(json_response['variables']).to eq variables + expect(json_response).not_to have_key('variables') end end @@ -328,11 +338,12 @@ describe API::Pipelines do expect do post api("/projects/#{project.id}/pipeline", user), ref: project.default_branch, variables: variables end.to change { project.pipelines.count }.by(1) + expect_variables(project.pipelines.last.variables, variables) expect(response).to have_gitlab_http_status(201) expect(json_response).to be_a Hash expect(json_response['sha']).to eq project.commit.id - expect(json_response['variables']).to eq variables + expect(json_response).not_to have_key('variables') end context 'condition unmatch' do -- cgit v1.2.1 From 9c3c198daf30981d7512f0dfbb58b4f1b7df4416 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jun 2018 15:00:55 -0500 Subject: Combine multiple `it` blocks to improve Markdown feature spec times The setup of each spec is too expensive to perform as many times as we were doing it. Reduced duration from 255 seconds to 43. --- spec/features/markdown/markdown_spec.rb | 204 +++++++++++++++----------------- 1 file changed, 97 insertions(+), 107 deletions(-) (limited to 'spec') diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index f13d78d24e3..c86ba8c50a5 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -24,7 +24,7 @@ require 'erb' # # See the MarkdownFeature class for setup details. -describe 'GitLab Markdown' do +describe 'GitLab Markdown', :aggregate_failures do include Capybara::Node::Matchers include MarkupHelper include MarkdownMatchers @@ -44,112 +44,102 @@ describe 'GitLab Markdown' do # Shared behavior that all pipelines should exhibit shared_examples 'all pipelines' do - describe 'Redcarpet extensions' do - it 'does not parse emphasis inside of words' do + it 'includes Redcarpet extensions' do + aggregate_failures 'does not parse emphasis inside of words' do expect(doc.to_html).not_to match('foobarbaz') end - it 'parses table Markdown' do - aggregate_failures do - expect(doc).to have_selector('th:contains("Header")') - expect(doc).to have_selector('th:contains("Row")') - expect(doc).to have_selector('th:contains("Example")') - end + aggregate_failures 'parses table Markdown' do + expect(doc).to have_selector('th:contains("Header")') + expect(doc).to have_selector('th:contains("Row")') + expect(doc).to have_selector('th:contains("Example")') end - it 'allows Markdown in tables' do + aggregate_failures 'allows Markdown in tables' do expect(doc.at_css('td:contains("Baz")').children.to_html) .to eq 'Baz' end - it 'parses fenced code blocks' do - aggregate_failures do - expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c') - expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python') - end + aggregate_failures 'parses fenced code blocks' do + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c') + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python') end - it 'parses mermaid code block' do - aggregate_failures do - expect(doc).to have_selector('pre[lang=mermaid] > code.js-render-mermaid') - end + aggregate_failures 'parses mermaid code block' do + expect(doc).to have_selector('pre[lang=mermaid] > code.js-render-mermaid') end - it 'parses strikethroughs' do + aggregate_failures 'parses strikethroughs' do expect(doc).to have_selector(%{del:contains("and this text doesn't")}) end - it 'parses superscript' do + aggregate_failures 'parses superscript' do expect(doc).to have_selector('sup', count: 2) end end - describe 'SanitizationFilter' do - it 'permits b elements' do + it 'includes SanitizationFilter' do + aggregate_failures 'permits b elements' do expect(doc).to have_selector('b:contains("b tag")') end - it 'permits em elements' do + aggregate_failures 'permits em elements' do expect(doc).to have_selector('em:contains("em tag")') end - it 'permits code elements' do + aggregate_failures 'permits code elements' do expect(doc).to have_selector('code:contains("code tag")') end - it 'permits kbd elements' do + aggregate_failures 'permits kbd elements' do expect(doc).to have_selector('kbd:contains("s")') end - it 'permits strike elements' do + aggregate_failures 'permits strike elements' do expect(doc).to have_selector('strike:contains(Emoji)') end - it 'permits img elements' do + aggregate_failures 'permits img elements' do expect(doc).to have_selector('img[data-src*="smile.png"]') end - it 'permits br elements' do + aggregate_failures 'permits br elements' do expect(doc).to have_selector('br') end - it 'permits hr elements' do + aggregate_failures 'permits hr elements' do expect(doc).to have_selector('hr') end - it 'permits span elements' do + aggregate_failures 'permits span elements' do expect(doc).to have_selector('span:contains("span tag")') end - it 'permits details elements' do + aggregate_failures 'permits details elements' do expect(doc).to have_selector('details:contains("Hiding the details")') end - it 'permits summary elements' do + aggregate_failures 'permits summary elements' do expect(doc).to have_selector('details summary:contains("collapsible")') end - it 'permits style attribute in th elements' do - aggregate_failures do - expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' - expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' - expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' - end + aggregate_failures 'permits style attribute in th elements' do + expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' + expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' + expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' end - it 'permits style attribute in td elements' do - aggregate_failures do - expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' - expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' - expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' - end + aggregate_failures 'permits style attribute in td elements' do + expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' + expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' + expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' end - it 'removes `rel` attribute from links' do + aggregate_failures 'removes `rel` attribute from links' do expect(doc).not_to have_selector('a[rel="bookmark"]') end - it "removes `href` from `a` elements if it's fishy" do + aggregate_failures "removes `href` from `a` elements if it's fishy" do expect(doc).not_to have_selector('a[href*="javascript"]') end end @@ -176,26 +166,26 @@ describe 'GitLab Markdown' do end end - describe 'ExternalLinkFilter' do - it 'adds nofollow to external link' do + it 'includes ExternalLinkFilter' do + aggregate_failures 'adds nofollow to external link' do link = doc.at_css('a:contains("Google")') expect(link.attr('rel')).to include('nofollow') end - it 'adds noreferrer to external link' do + aggregate_failures 'adds noreferrer to external link' do link = doc.at_css('a:contains("Google")') expect(link.attr('rel')).to include('noreferrer') end - it 'adds _blank to target attribute for external links' do + aggregate_failures 'adds _blank to target attribute for external links' do link = doc.at_css('a:contains("Google")') expect(link.attr('target')).to match('_blank') end - it 'ignores internal link' do + aggregate_failures 'ignores internal link' do link = doc.at_css('a:contains("GitLab Root")') expect(link.attr('rel')).not_to match 'nofollow' @@ -219,24 +209,24 @@ describe 'GitLab Markdown' do it_behaves_like 'all pipelines' - it 'includes RelativeLinkFilter' do - expect(doc).to parse_relative_links - end + it 'includes custom filters' do + aggregate_failures 'RelativeLinkFilter' do + expect(doc).to parse_relative_links + end - it 'includes EmojiFilter' do - expect(doc).to parse_emoji - end + aggregate_failures 'EmojiFilter' do + expect(doc).to parse_emoji + end - it 'includes TableOfContentsFilter' do - expect(doc).to create_header_links - end + aggregate_failures 'TableOfContentsFilter' do + expect(doc).to create_header_links + end - it 'includes AutolinkFilter' do - expect(doc).to create_autolinks - end + aggregate_failures 'AutolinkFilter' do + expect(doc).to create_autolinks + end - it 'includes all reference filters' do - aggregate_failures do + aggregate_failures 'all reference filters' do expect(doc).to reference_users expect(doc).to reference_issues expect(doc).to reference_merge_requests @@ -246,22 +236,22 @@ describe 'GitLab Markdown' do expect(doc).to reference_labels expect(doc).to reference_milestones end - end - it 'includes TaskListFilter' do - expect(doc).to parse_task_lists - end + aggregate_failures 'TaskListFilter' do + expect(doc).to parse_task_lists + end - it 'includes InlineDiffFilter' do - expect(doc).to parse_inline_diffs - end + aggregate_failures 'InlineDiffFilter' do + expect(doc).to parse_inline_diffs + end - it 'includes VideoLinkFilter' do - expect(doc).to parse_video_links - end + aggregate_failures 'VideoLinkFilter' do + expect(doc).to parse_video_links + end - it 'includes ColorFilter' do - expect(doc).to parse_colors + aggregate_failures 'ColorFilter' do + expect(doc).to parse_colors + end end end @@ -280,24 +270,24 @@ describe 'GitLab Markdown' do it_behaves_like 'all pipelines' - it 'includes RelativeLinkFilter' do - expect(doc).not_to parse_relative_links - end + it 'includes custom filters' do + aggregate_failures 'RelativeLinkFilter' do + expect(doc).not_to parse_relative_links + end - it 'includes EmojiFilter' do - expect(doc).to parse_emoji - end + aggregate_failures 'EmojiFilter' do + expect(doc).to parse_emoji + end - it 'includes TableOfContentsFilter' do - expect(doc).to create_header_links - end + aggregate_failures 'TableOfContentsFilter' do + expect(doc).to create_header_links + end - it 'includes AutolinkFilter' do - expect(doc).to create_autolinks - end + aggregate_failures 'AutolinkFilter' do + expect(doc).to create_autolinks + end - it 'includes all reference filters' do - aggregate_failures do + aggregate_failures 'all reference filters' do expect(doc).to reference_users expect(doc).to reference_issues expect(doc).to reference_merge_requests @@ -307,26 +297,26 @@ describe 'GitLab Markdown' do expect(doc).to reference_labels expect(doc).to reference_milestones end - end - it 'includes TaskListFilter' do - expect(doc).to parse_task_lists - end + aggregate_failures 'TaskListFilter' do + expect(doc).to parse_task_lists + end - it 'includes GollumTagsFilter' do - expect(doc).to parse_gollum_tags - end + aggregate_failures 'GollumTagsFilter' do + expect(doc).to parse_gollum_tags + end - it 'includes InlineDiffFilter' do - expect(doc).to parse_inline_diffs - end + aggregate_failures 'InlineDiffFilter' do + expect(doc).to parse_inline_diffs + end - it 'includes VideoLinkFilter' do - expect(doc).to parse_video_links - end + aggregate_failures 'VideoLinkFilter' do + expect(doc).to parse_video_links + end - it 'includes ColorFilter' do - expect(doc).to parse_colors + aggregate_failures 'ColorFilter' do + expect(doc).to parse_colors + end end end -- cgit v1.2.1 From 36a8f1a677df85d61c4948e9f28293a1c75096a9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 5 Jun 2018 14:39:44 -0700 Subject: Reject GPG keys that have e-mail or names with non-valid UTF-8 encodings These were causing 500 Errors when accessing GPG keys for some users. Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/47280 --- spec/lib/gitlab/gpg_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb index ab9a166db00..47f37cae98f 100644 --- a/spec/lib/gitlab/gpg_spec.rb +++ b/spec/lib/gitlab/gpg_spec.rb @@ -74,6 +74,19 @@ describe Gitlab::Gpg do email: 'nannie.bernhard@example.com' }]) end + + it 'rejects non UTF-8 names and addresses' do + public_key = double(:key) + fingerprints = double(:fingerprints) + email = "\xEEch@test.com".force_encoding('ASCII-8BIT') + uid = double(:uid, name: 'Test User', email: email) + raw_key = double(:raw_key, uids: [uid]) + allow(Gitlab::Gpg::CurrentKeyChain).to receive(:fingerprints_from_key).with(public_key).and_return(fingerprints) + allow(GPGME::Key).to receive(:find).with(:public, anything).and_return([raw_key]) + + user_infos = described_class.user_infos_from_key(public_key) + expect(user_infos).to eq([]) + end end describe '.current_home_dir' do -- cgit v1.2.1 From bbafb85395cf2786f21a5a92745b9b3a181a4ce8 Mon Sep 17 00:00:00 2001 From: Ash McKenzie Date: Tue, 5 Jun 2018 21:42:18 +1000 Subject: Add missing tests around rendering invalid merge requests (HTML+JSON) --- .../projects/merge_requests_controller_spec.rb | 20 ++++++++++++++++++++ spec/factories/merge_requests.rb | 6 ++++++ 2 files changed, 26 insertions(+) (limited to 'spec') diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 6e8de6db9c3..6e710c9b20b 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -80,6 +80,16 @@ describe Projects::MergeRequestsController do )) end end + + context "that is invalid" do + let(:merge_request) { create(:invalid_merge_request, target_project: project, source_project: project) } + + it "renders merge request page" do + go(format: :html) + + expect(response).to be_success + end + end end describe 'as json' do @@ -106,6 +116,16 @@ describe Projects::MergeRequestsController do expect(response).to match_response_schema('entities/merge_request_widget') end end + + context "that is invalid" do + let(:merge_request) { create(:invalid_merge_request, target_project: project, source_project: project) } + + it "renders merge request page" do + go(format: :json) + + expect(response).to be_success + end + end end describe "as diff" do diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index fab0ec22450..3441ce1b8cb 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -54,6 +54,11 @@ FactoryBot.define do state :opened end + trait :invalid do + source_branch "feature_one" + target_branch "feature_two" + end + trait :locked do state :locked end @@ -98,6 +103,7 @@ FactoryBot.define do factory :merged_merge_request, traits: [:merged] factory :closed_merge_request, traits: [:closed] factory :reopened_merge_request, traits: [:opened] + factory :invalid_merge_request, traits: [:invalid] factory :merge_request_with_diffs, traits: [:with_diffs] factory :merge_request_with_diff_notes do after(:create) do |mr| -- cgit v1.2.1 From b61e1e7a5cd496a9b2de3d6a6192b3aed2d6acdd Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 6 Jun 2018 01:09:58 +0000 Subject: Fix invisible rows on importer status --- spec/helpers/projects_helper_spec.rb | 6 +++--- spec/javascripts/importer_status_spec.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 4e5391295b6..d372e58f63d 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -5,9 +5,9 @@ describe ProjectsHelper do describe "#project_status_css_class" do it "returns appropriate class" do - expect(project_status_css_class("started")).to eq("active") - expect(project_status_css_class("failed")).to eq("danger") - expect(project_status_css_class("finished")).to eq("success") + expect(project_status_css_class("started")).to eq("table-active") + expect(project_status_css_class("failed")).to eq("table-danger") + expect(project_status_css_class("finished")).to eq("table-success") end end diff --git a/spec/javascripts/importer_status_spec.js b/spec/javascripts/importer_status_spec.js index 0575d02886d..87b46ccf7c3 100644 --- a/spec/javascripts/importer_status_spec.js +++ b/spec/javascripts/importer_status_spec.js @@ -45,7 +45,7 @@ describe('Importer Status', () => { currentTarget: document.querySelector('.js-add-to-import'), }) .then(() => { - expect(document.querySelector('tr').classList.contains('active')).toEqual(true); + expect(document.querySelector('tr').classList.contains('table-active')).toEqual(true); done(); }) .catch(done.fail); -- cgit v1.2.1 From 078a5dcd1b285fdb1f2ec029054da389b9e1203f Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 6 Jun 2018 07:22:54 +0200 Subject: Rails 5 fix unknown keywords: changes, key_id, project, gl_repository, action, secret_token, protocol --- spec/requests/api/internal_spec.rb | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 5dc3ddd4b36..bc32372d3a9 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -835,8 +835,7 @@ describe API::Internal do end def push(key, project, protocol = 'ssh', env: nil) - post( - api("/internal/allowed"), + params = { changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master', key_id: key.id, project: project.full_path, @@ -845,7 +844,19 @@ describe API::Internal do secret_token: secret_token, protocol: protocol, env: env - ) + } + + if Gitlab.rails5? + post( + api("/internal/allowed"), + params: params + ) + else + post( + api("/internal/allowed"), + params + ) + end end def archive(key, project) -- cgit v1.2.1 From 2d1faddbcb3b814a381f91cf48cc597d47b29ee0 Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 6 Jun 2018 08:46:42 +0200 Subject: Rails 5 fix glob spec --- spec/lib/gitlab/sql/glob_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/sql/glob_spec.rb b/spec/lib/gitlab/sql/glob_spec.rb index f0bb4294d62..1cf8935bfe3 100644 --- a/spec/lib/gitlab/sql/glob_spec.rb +++ b/spec/lib/gitlab/sql/glob_spec.rb @@ -35,8 +35,9 @@ describe Gitlab::SQL::Glob do value = query("SELECT #{quote(string)} LIKE #{pattern}") .rows.flatten.first + check = Gitlab.rails5? ? true : 't' case value - when 't', 1 + when check, 1 true else false -- cgit v1.2.1 From 2d6022e0865642deb10d458d58d6b0b8c072e98e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 1 Jun 2018 14:44:22 +0100 Subject: store specs --- spec/javascripts/ide/mock_data.js | 4 + .../ide/stores/modules/pipelines/actions_spec.js | 135 +++++++++++++++++++++ .../ide/stores/modules/pipelines/mutations_spec.js | 49 ++++++++ 3 files changed, 188 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index dcf857f7e04..dd87a43f370 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -75,6 +75,7 @@ export const jobs = [ }, stage: 'test', duration: 1, + started: new Date(), }, { id: 2, @@ -86,6 +87,7 @@ export const jobs = [ }, stage: 'test', duration: 1, + started: new Date(), }, { id: 3, @@ -97,6 +99,7 @@ export const jobs = [ }, stage: 'test', duration: 1, + started: new Date(), }, { id: 4, @@ -108,6 +111,7 @@ export const jobs = [ }, stage: 'build', duration: 1, + started: new Date(), }, ]; diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index f26eaf9c81f..f2f8e780cd1 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -13,9 +13,15 @@ import actions, { receiveJobsSuccess, fetchJobs, toggleStageCollapsed, + setDetailJob, + requestJobTrace, + receiveJobTraceError, + receiveJobTraceSuccess, + fetchJobTrace, } from '~/ide/stores/modules/pipelines/actions'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; +import { rightSidebarViews } from '~/ide/constants'; import testAction from '../../../../helpers/vuex_action_helper'; import { pipelines, jobs } from '../../../mock_data'; @@ -281,4 +287,133 @@ describe('IDE pipelines actions', () => { ); }); }); + + describe('setDetailJob', () => { + it('commits job', done => { + testAction( + setDetailJob, + 'job', + mockedState, + [{ type: types.SET_DETAIL_JOB, payload: 'job' }], + [{ type: 'setRightPane' }], + done, + ); + }); + + it('dispatches setRightPane as pipeline when job is null', done => { + testAction( + setDetailJob, + null, + mockedState, + [{ type: types.SET_DETAIL_JOB }], + [{ type: 'setRightPane', payload: rightSidebarViews.pipelines }], + done, + ); + }); + + it('dispatches setRightPane as job', done => { + testAction( + setDetailJob, + 'job', + mockedState, + [{ type: types.SET_DETAIL_JOB }], + [{ type: 'setRightPane', payload: rightSidebarViews.jobsDetail }], + done, + ); + }); + }); + + describe('requestJobTrace', () => { + it('commits request', done => { + testAction(requestJobTrace, null, mockedState, [{ type: types.REQUEST_JOB_TRACE }], [], done); + }); + }); + + describe('receiveJobTraceError', () => { + it('commits error', done => { + testAction( + receiveJobTraceError, + null, + mockedState, + [{ type: types.RECEIVE_JOB_TRACE_ERROR }], + [], + done, + ); + }); + + it('creates flash message', () => { + const flashSpy = spyOnDependency(actions, 'flash'); + + receiveJobTraceError({ commit() {} }); + + expect(flashSpy).toHaveBeenCalled(); + }); + }); + + describe('receiveJobTraceSuccess', () => { + it('commits data', done => { + testAction( + receiveJobTraceSuccess, + 'data', + mockedState, + [{ type: types.RECEIVE_JOB_TRACE_SUCCESS, payload: 'data' }], + [], + done, + ); + }); + }); + + describe('fetchJobTrace', () => { + beforeEach(() => { + mockedState.detailJob = { + path: `${gl.TEST_HOST}/project/builds`, + }; + }); + + describe('success', () => { + beforeEach(() => { + spyOn(axios, 'get').and.callThrough(); + mock.onGet(`${gl.TEST_HOST}/project/builds/trace`).replyOnce(200, { html: 'html' }); + }); + + it('dispatches request', done => { + testAction( + fetchJobTrace, + null, + mockedState, + [], + [ + { type: 'requestJobTrace' }, + { type: 'receiveJobTraceSuccess', payload: { html: 'html' } }, + ], + done, + ); + }); + + it('sends get request to correct URL', () => { + fetchJobTrace({ state: mockedState, dispatch() {} }); + + expect(axios.get).toHaveBeenCalledWith(`${gl.TEST_HOST}/project/builds/trace`, { + params: { format: 'json' }, + }); + }); + }); + + describe('error', () => { + beforeEach(() => { + mock.onGet(`${gl.TEST_HOST}/project/builds/trace`).replyOnce(500); + }); + + it('dispatches error', done => { + testAction( + fetchJobTrace, + null, + mockedState, + [], + [{ type: 'requestJobTrace' }, { type: 'receiveJobTraceError' }], + done, + ); + }); + }); + }); }); diff --git a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js index 6285c01d483..eb7346bd5fc 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/mutations_spec.js @@ -147,6 +147,10 @@ describe('IDE pipelines mutations', () => { name: job.name, status: job.status, path: job.build_path, + rawPath: `${job.build_path}/raw`, + started: job.started, + isLoading: false, + output: '', })), ); }); @@ -171,4 +175,49 @@ describe('IDE pipelines mutations', () => { expect(mockedState.stages[0].isCollapsed).toBe(false); }); }); + + describe(types.SET_DETAIL_JOB, () => { + it('sets detail job', () => { + mutations[types.SET_DETAIL_JOB](mockedState, jobs[0]); + + expect(mockedState.detailJob).toEqual(jobs[0]); + }); + }); + + describe(types.REQUEST_JOB_TRACE, () => { + beforeEach(() => { + mockedState.detailJob = { ...jobs[0] }; + }); + + it('sets loading on detail job', () => { + mutations[types.REQUEST_JOB_TRACE](mockedState); + + expect(mockedState.detailJob.isLoading).toBe(true); + }); + }); + + describe(types.RECEIVE_JOB_TRACE_ERROR, () => { + beforeEach(() => { + mockedState.detailJob = { ...jobs[0], isLoading: true }; + }); + + it('sets loading to false on detail job', () => { + mutations[types.RECEIVE_JOB_TRACE_ERROR](mockedState); + + expect(mockedState.detailJob.isLoading).toBe(false); + }); + }); + + describe(types.RECEIVE_JOB_TRACE_SUCCESS, () => { + beforeEach(() => { + mockedState.detailJob = { ...jobs[0], isLoading: true }; + }); + + it('sets output on detail job', () => { + mutations[types.RECEIVE_JOB_TRACE_SUCCESS](mockedState, { html: 'html' }); + + expect(mockedState.detailJob.output).toBe('html'); + expect(mockedState.detailJob.isLoading).toBe(false); + }); + }); }); -- cgit v1.2.1 From f73b2c8e5ee0e02407b6674b9b0021ce4843349b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Sat, 2 Jun 2018 14:45:58 +0100 Subject: component specs --- .../ide/components/jobs/detail/description_spec.js | 28 ++++ .../components/jobs/detail/scroll_button_spec.js | 59 +++++++ .../javascripts/ide/components/jobs/detail_spec.js | 170 +++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 spec/javascripts/ide/components/jobs/detail/description_spec.js create mode 100644 spec/javascripts/ide/components/jobs/detail/scroll_button_spec.js create mode 100644 spec/javascripts/ide/components/jobs/detail_spec.js (limited to 'spec') diff --git a/spec/javascripts/ide/components/jobs/detail/description_spec.js b/spec/javascripts/ide/components/jobs/detail/description_spec.js new file mode 100644 index 00000000000..9b715a41499 --- /dev/null +++ b/spec/javascripts/ide/components/jobs/detail/description_spec.js @@ -0,0 +1,28 @@ +import Vue from 'vue'; +import Description from '~/ide/components/jobs/detail/description.vue'; +import mountComponent from '../../../../helpers/vue_mount_component_helper'; +import { jobs } from '../../../mock_data'; + +describe('IDE job description', () => { + const Component = Vue.extend(Description); + let vm; + + beforeEach(() => { + vm = mountComponent(Component, { + job: jobs[0], + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders job details', () => { + expect(vm.$el.textContent).toContain('#1'); + expect(vm.$el.textContent).toContain('test'); + }); + + it('renders CI icon', () => { + expect(vm.$el.querySelector('.ci-status-icon .ic-status_passed_borderless')).not.toBe(null); + }); +}); diff --git a/spec/javascripts/ide/components/jobs/detail/scroll_button_spec.js b/spec/javascripts/ide/components/jobs/detail/scroll_button_spec.js new file mode 100644 index 00000000000..fff382a107f --- /dev/null +++ b/spec/javascripts/ide/components/jobs/detail/scroll_button_spec.js @@ -0,0 +1,59 @@ +import Vue from 'vue'; +import ScrollButton from '~/ide/components/jobs/detail/scroll_button.vue'; +import mountComponent from '../../../../helpers/vue_mount_component_helper'; + +describe('IDE job log scroll button', () => { + const Component = Vue.extend(ScrollButton); + let vm; + + beforeEach(() => { + vm = mountComponent(Component, { + direction: 'up', + disabled: false, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('iconName', () => { + ['up', 'down'].forEach(direction => { + it(`returns icon name for ${direction}`, () => { + vm.direction = direction; + + expect(vm.iconName).toBe(`scroll_${direction}`); + }); + }); + }); + + describe('tooltipTitle', () => { + it('returns title for up', () => { + expect(vm.tooltipTitle).toBe('Scroll to top'); + }); + + it('returns title for down', () => { + vm.direction = 'down'; + + expect(vm.tooltipTitle).toBe('Scroll to bottom'); + }); + }); + + it('emits click event on click', () => { + spyOn(vm, '$emit'); + + vm.$el.querySelector('.btn-scroll').click(); + + expect(vm.$emit).toHaveBeenCalledWith('click'); + }); + + it('disables button when disabled is true', done => { + vm.disabled = true; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.btn-scroll').hasAttribute('disabled')).toBe(true); + + done(); + }); + }); +}); diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js new file mode 100644 index 00000000000..00a2dc6c74c --- /dev/null +++ b/spec/javascripts/ide/components/jobs/detail_spec.js @@ -0,0 +1,170 @@ +import Vue from 'vue'; +import JobDetail from '~/ide/components/jobs/detail.vue'; +import { createStore } from '~/ide/stores'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { jobs } from '../../mock_data'; + +describe('IDE jobs detail view', () => { + const Component = Vue.extend(JobDetail); + let vm; + + beforeEach(() => { + const store = createStore(); + + store.state.pipelines.detailJob = { + ...jobs[0], + isLoading: true, + output: 'testing', + rawPath: `${gl.TEST_HOST}/raw`, + }; + + vm = createComponentWithStore(Component, store); + + spyOn(vm, 'fetchJobTrace').and.returnValue(Promise.resolve()); + + vm = vm.$mount(); + + spyOn(vm.$refs.buildTrace, 'scrollTo'); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('calls fetchJobTrace on mount', () => { + expect(vm.fetchJobTrace).toHaveBeenCalled(); + }); + + it('scrolls to bottom on mount', done => { + setTimeout(() => { + expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalled(); + + done(); + }); + }); + + it('renders job output', () => { + expect(vm.$el.querySelector('.bash').textContent).toContain('testing'); + }); + + it('renders loading icon', () => { + expect(vm.$el.querySelector('.build-loader-animation')).not.toBe(null); + expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe(''); + }); + + it('hide loading icon when isLoading is false', done => { + vm.$store.state.pipelines.detailJob.isLoading = false; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe('none'); + + done(); + }); + }); + + it('resets detailJob when clicking header button', () => { + spyOn(vm, 'setDetailJob'); + + vm.$el.querySelector('.btn').click(); + + expect(vm.setDetailJob).toHaveBeenCalledWith(null); + }); + + it('renders raw path link', () => { + expect(vm.$el.querySelector('.controllers-buttons').getAttribute('href')).toBe( + `${gl.TEST_HOST}/raw`, + ); + }); + + describe('scroll buttons', () => { + it('triggers scrollDown when clicking down button', done => { + spyOn(vm, 'scrollDown'); + + vm.$el.querySelectorAll('.btn-scroll')[1].click(); + + vm.$nextTick(() => { + expect(vm.scrollDown).toHaveBeenCalled(); + + done(); + }); + }); + + it('triggers scrollUp when clicking up button', done => { + spyOn(vm, 'scrollUp'); + + vm.scrollPos = 1; + + vm + .$nextTick() + .then(() => vm.$el.querySelector('.btn-scroll').click()) + .then(() => vm.$nextTick()) + .then(() => { + expect(vm.scrollUp).toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('scrollDown', () => { + it('scrolls build trace to bottom', () => { + spyOnProperty(vm.$refs.buildTrace, 'scrollHeight').and.returnValue(1000); + + vm.scrollDown(); + + expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 1000); + }); + }); + + describe('scrollUp', () => { + it('scrolls build trace to top', () => { + vm.scrollUp(); + + expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 0); + }); + }); + + describe('scrollBuildLog', () => { + beforeEach(() => { + spyOnProperty(vm.$refs.buildTrace, 'offsetHeight').and.returnValue(100); + spyOnProperty(vm.$refs.buildTrace, 'scrollHeight').and.returnValue(200); + }); + + it('sets scrollPos to bottom when at the bottom', done => { + spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(100); + + vm.scrollBuildLog(); + + setTimeout(() => { + expect(vm.scrollPos).toBe(1); + + done(); + }); + }); + + it('sets scrollPos to top when at the top', done => { + spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(0); + vm.scrollPos = 1; + + vm.scrollBuildLog(); + + setTimeout(() => { + expect(vm.scrollPos).toBe(0); + + done(); + }); + }); + + it('resets scrollPos when not at top or bottom', done => { + spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(10); + + vm.scrollBuildLog(); + + setTimeout(() => { + expect(vm.scrollPos).toBe(''); + + done(); + }); + }); + }); +}); -- cgit v1.2.1 From ec37b1b20c09d3a5b5a0e2cd0b03311ec95d7c68 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 6 Jun 2018 08:43:15 +0100 Subject: added empty state --- spec/javascripts/ide/components/jobs/detail_spec.js | 10 ++++++++++ spec/javascripts/ide/components/jobs/item_spec.js | 10 ++++++++++ 2 files changed, 20 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js index 00a2dc6c74c..641ba06f653 100644 --- a/spec/javascripts/ide/components/jobs/detail_spec.js +++ b/spec/javascripts/ide/components/jobs/detail_spec.js @@ -47,6 +47,16 @@ describe('IDE jobs detail view', () => { expect(vm.$el.querySelector('.bash').textContent).toContain('testing'); }); + it('renders empty message output', done => { + vm.$store.state.pipelines.detailJob.output = ''; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.bash').textContent).toContain('No messages were logged'); + + done(); + }); + }); + it('renders loading icon', () => { expect(vm.$el.querySelector('.build-loader-animation')).not.toBe(null); expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe(''); diff --git a/spec/javascripts/ide/components/jobs/item_spec.js b/spec/javascripts/ide/components/jobs/item_spec.js index 7c1dd4e475c..79e07f00e7b 100644 --- a/spec/javascripts/ide/components/jobs/item_spec.js +++ b/spec/javascripts/ide/components/jobs/item_spec.js @@ -26,4 +26,14 @@ describe('IDE jobs item', () => { it('renders CI icon', () => { expect(vm.$el.querySelector('.ic-status_passed_borderless')).not.toBe(null); }); + + it('does not render view logs button if not started', done => { + vm.job.started = false; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.btn')).toBe(null); + + done(); + }); + }); }); -- cgit v1.2.1 From c8f0e4b5da0c9d578035e74b524f5adcb80efcf6 Mon Sep 17 00:00:00 2001 From: Imre Date: Fri, 18 May 2018 10:49:02 +0200 Subject: Add Avatar API --- spec/requests/api/avatar_spec.rb | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 spec/requests/api/avatar_spec.rb (limited to 'spec') diff --git a/spec/requests/api/avatar_spec.rb b/spec/requests/api/avatar_spec.rb new file mode 100644 index 00000000000..26e0435a6d5 --- /dev/null +++ b/spec/requests/api/avatar_spec.rb @@ -0,0 +1,106 @@ +require 'spec_helper' + +describe API::Avatar do + let(:gravatar_service) { double('GravatarService') } + + describe 'GET /avatar' do + context 'avatar uploaded to GitLab' do + context 'user with matching public email address' do + let(:user) { create(:user, :with_avatar, email: 'public@example.com', public_email: 'public@example.com') } + + before do + user + end + + it 'returns the avatar url' do + get api('/avatar'), { email: 'public@example.com' } + + expect(response.status).to eq 200 + expect(json_response['avatar_url']).to eql("#{::Settings.gitlab.base_url}#{user.avatar.local_url}") + end + end + + context 'no user with matching public email address' do + before do + expect(GravatarService).to receive(:new).and_return(gravatar_service) + expect(gravatar_service).to( + receive(:execute) + .with('private@example.com', nil, 2, { username: nil }) + .and_return('https://gravatar')) + end + + it 'returns the avatar url from Gravatar' do + get api('/avatar'), { email: 'private@example.com' } + + expect(response.status).to eq 200 + expect(json_response['avatar_url']).to eq('https://gravatar') + end + end + end + + context 'avatar uploaded to Gravatar' do + context 'user with matching public email address' do + let(:user) { create(:user, email: 'public@example.com', public_email: 'public@example.com') } + + before do + user + + expect(GravatarService).to receive(:new).and_return(gravatar_service) + expect(gravatar_service).to( + receive(:execute) + .with('public@example.com', nil, 2, { username: user.username }) + .and_return('https://gravatar')) + end + + it 'returns the avatar url from Gravatar' do + get api('/avatar'), { email: 'public@example.com' } + + expect(response.status).to eq 200 + expect(json_response['avatar_url']).to eq('https://gravatar') + end + end + + context 'no user with matching public email address' do + before do + expect(GravatarService).to receive(:new).and_return(gravatar_service) + expect(gravatar_service).to( + receive(:execute) + .with('private@example.com', nil, 2, { username: nil }) + .and_return('https://gravatar')) + end + + it 'returns the avatar url from Gravatar' do + get api('/avatar'), { email: 'private@example.com' } + + expect(response.status).to eq 200 + expect(json_response['avatar_url']).to eq('https://gravatar') + end + end + + context 'public visibility level restricted' do + let(:user) { create(:user, :with_avatar, email: 'public@example.com', public_email: 'public@example.com') } + + before do + user + + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + context 'when authenticated' do + it 'returns the avatar url' do + get api('/avatar', user), { email: 'public@example.com' } + + expect(response.status).to eq 200 + expect(json_response['avatar_url']).to eql("#{::Settings.gitlab.base_url}#{user.avatar.local_url}") + end + end + + context 'when unauthenticated' do + it_behaves_like '403 response' do + let(:request) { get api('/avatar'), { email: 'public@example.com' } } + end + end + end + end + end +end -- cgit v1.2.1 From 3ed66d4abde28c9f586342fe8e6481360825b823 Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Tue, 5 Jun 2018 19:12:26 +0100 Subject: Adds #human_import_status_name to make it comply with ProjectImportState#human_status_name --- spec/models/project_spec.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'spec') diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9a76452a808..59d1671b036 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1693,6 +1693,31 @@ describe Project do end end + describe '#human_import_status_name' do + context 'when import_state exists' do + it 'returns the humanized status name' do + project = create(:project) + create(:import_state, :started, project: project) + + expect(project.human_import_status_name).to eq("started") + end + end + + context 'when import_state was not created yet' do + let(:project) { create(:project, :import_started) } + + it 'ensures import_state is created and returns humanized status name' do + expect do + project.human_import_status_name + end.to change { ProjectImportState.count }.from(0).to(1) + end + + it 'returns humanized status name' do + expect(project.human_import_status_name).to eq("started") + end + end + end + describe 'Project import job' do let(:project) { create(:project, import_url: generate(:url)) } -- cgit v1.2.1 From 3a722ff53fe86ce6194f81ade810196f4f8e870c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 5 Jun 2018 22:34:06 -0700 Subject: Show a more helpful error for import status Importing a project from GitHub for a project namespace that already exists would show an unhelpful error, "An error occurred while importing project." We now add the base message from Projects::CreateService when this fails. Closes #47365 --- spec/javascripts/importer_status_spec.js | 18 ++++++++++++++++ .../githubish_import_controller_shared_examples.rb | 24 ++++++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/importer_status_spec.js b/spec/javascripts/importer_status_spec.js index 87b46ccf7c3..63cdb3d5114 100644 --- a/spec/javascripts/importer_status_spec.js +++ b/spec/javascripts/importer_status_spec.js @@ -50,6 +50,24 @@ describe('Importer Status', () => { }) .catch(done.fail); }); + + it('shows error message after failed POST request', (done) => { + appendSetFixtures('
'); + + mock.onPost(importUrl).reply(422, { + errors: 'You forgot your lunch', + }); + + instance.addToImport({ + currentTarget: document.querySelector('.js-add-to-import'), + }) + .then(() => { + const flashMessage = document.querySelector('.flash-text'); + expect(flashMessage.textContent.trim()).toEqual('An error occurred while importing project: You forgot your lunch'); + done(); + }) + .catch(done.fail); + }); }); describe('autoUpdate', () => { diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb index 368439aa5b0..1a53c5fa487 100644 --- a/spec/support/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -118,14 +118,34 @@ shared_examples 'a GitHub-ish import controller: POST create' do expect(response).to have_gitlab_http_status(200) end - it 'returns 422 response when the project could not be imported' do + it 'returns 422 response with the base error when the project could not be imported' do + project = build(:project) + error_message = 'This is an error' + project.errors.add(:base, error_message) + + allow(Gitlab::LegacyGithubImport::ProjectCreator) + .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider) + .and_return(double(execute: project)) + + post :create, format: :json + + expect(response).to have_gitlab_http_status(422) + expect(json_response['errors']).to eq(error_message) + end + + it 'returns 422 response with a combined error when the project could not be imported' do + project = build(:project) + project.errors.add(:name, 'is invalid') + project.errors.add(:path, 'is old') + allow(Gitlab::LegacyGithubImport::ProjectCreator) .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider) - .and_return(double(execute: build(:project))) + .and_return(double(execute: project)) post :create, format: :json expect(response).to have_gitlab_http_status(422) + expect(json_response['errors']).to eq('Name is invalid, Path is old') end context "when the repository owner is the provider user" do -- cgit v1.2.1 From 4b458ca79ad516cbc556ad9cd7e384c10bce037f Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Wed, 23 May 2018 15:51:50 +0900 Subject: Add tests --- .../workers/rescue_stale_live_trace_worker_spec.rb | 71 ++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 spec/workers/rescue_stale_live_trace_worker_spec.rb (limited to 'spec') diff --git a/spec/workers/rescue_stale_live_trace_worker_spec.rb b/spec/workers/rescue_stale_live_trace_worker_spec.rb new file mode 100644 index 00000000000..f2b6361c54b --- /dev/null +++ b/spec/workers/rescue_stale_live_trace_worker_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe RescueStaleLiveTraceWorker do + subject { described_class.new.perform } + + before do + stub_feature_flags(ci_enable_live_trace: true) + end + + shared_examples_for 'schedules to archive traces' do + it do + expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[build.id]]) + + subject + end + end + + shared_examples_for 'does not schedule to archive traces' do + it do + expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async) + + subject + end + end + + context 'when a job was succeeded 2 hours ago' do + let!(:build) { create(:ci_build, :success, :trace_live) } + + before do + build.update(finished_at: 2.hours.ago) + end + + it_behaves_like 'schedules to archive traces' + end + + context 'when a job was failed 2 hours ago' do + let!(:build) { create(:ci_build, :failed, :trace_live) } + + before do + build.update(finished_at: 2.hours.ago) + end + + it_behaves_like 'schedules to archive traces' + end + + context 'when a job was cancelled 2 hours ago' do + let!(:build) { create(:ci_build, :canceled, :trace_live) } + + before do + build.update(finished_at: 2.hours.ago) + end + + it_behaves_like 'schedules to archive traces' + end + + context 'when a job has been finished 10 minutes ago' do + let!(:build) { create(:ci_build, :success, :trace_live) } + + before do + build.update(finished_at: 10.minutes.ago) + end + + it_behaves_like 'does not schedule to archive traces' + end + + context 'when a job is running' do + let!(:build) { create(:ci_build, :running, :trace_live) } + + it_behaves_like 'does not schedule to archive traces' + end +end -- cgit v1.2.1 From fb1e35e556f50636645d48c739dad37a4c7f722c Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Thu, 31 May 2018 16:29:17 +0900 Subject: Fix spec fiel location --- .../ci/rescue_stale_live_trace_worker_spec.rb | 71 ++++++++++++++++++++++ .../workers/rescue_stale_live_trace_worker_spec.rb | 71 ---------------------- 2 files changed, 71 insertions(+), 71 deletions(-) create mode 100644 spec/workers/ci/rescue_stale_live_trace_worker_spec.rb delete mode 100644 spec/workers/rescue_stale_live_trace_worker_spec.rb (limited to 'spec') diff --git a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb new file mode 100644 index 00000000000..87796fd66db --- /dev/null +++ b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe Ci::RescueStaleLiveTraceWorker do + subject { described_class.new.perform } + + before do + stub_feature_flags(ci_enable_live_trace: true) + end + + shared_examples_for 'schedules to archive traces' do + it do + expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[build.id]]) + + subject + end + end + + shared_examples_for 'does not schedule to archive traces' do + it do + expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async) + + subject + end + end + + context 'when a job was succeeded 2 hours ago' do + let!(:build) { create(:ci_build, :success, :trace_live) } + + before do + build.update(finished_at: 2.hours.ago) + end + + it_behaves_like 'schedules to archive traces' + end + + context 'when a job was failed 2 hours ago' do + let!(:build) { create(:ci_build, :failed, :trace_live) } + + before do + build.update(finished_at: 2.hours.ago) + end + + it_behaves_like 'schedules to archive traces' + end + + context 'when a job was cancelled 2 hours ago' do + let!(:build) { create(:ci_build, :canceled, :trace_live) } + + before do + build.update(finished_at: 2.hours.ago) + end + + it_behaves_like 'schedules to archive traces' + end + + context 'when a job has been finished 10 minutes ago' do + let!(:build) { create(:ci_build, :success, :trace_live) } + + before do + build.update(finished_at: 10.minutes.ago) + end + + it_behaves_like 'does not schedule to archive traces' + end + + context 'when a job is running' do + let!(:build) { create(:ci_build, :running, :trace_live) } + + it_behaves_like 'does not schedule to archive traces' + end +end diff --git a/spec/workers/rescue_stale_live_trace_worker_spec.rb b/spec/workers/rescue_stale_live_trace_worker_spec.rb deleted file mode 100644 index f2b6361c54b..00000000000 --- a/spec/workers/rescue_stale_live_trace_worker_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -require 'spec_helper' - -describe RescueStaleLiveTraceWorker do - subject { described_class.new.perform } - - before do - stub_feature_flags(ci_enable_live_trace: true) - end - - shared_examples_for 'schedules to archive traces' do - it do - expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[build.id]]) - - subject - end - end - - shared_examples_for 'does not schedule to archive traces' do - it do - expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async) - - subject - end - end - - context 'when a job was succeeded 2 hours ago' do - let!(:build) { create(:ci_build, :success, :trace_live) } - - before do - build.update(finished_at: 2.hours.ago) - end - - it_behaves_like 'schedules to archive traces' - end - - context 'when a job was failed 2 hours ago' do - let!(:build) { create(:ci_build, :failed, :trace_live) } - - before do - build.update(finished_at: 2.hours.ago) - end - - it_behaves_like 'schedules to archive traces' - end - - context 'when a job was cancelled 2 hours ago' do - let!(:build) { create(:ci_build, :canceled, :trace_live) } - - before do - build.update(finished_at: 2.hours.ago) - end - - it_behaves_like 'schedules to archive traces' - end - - context 'when a job has been finished 10 minutes ago' do - let!(:build) { create(:ci_build, :success, :trace_live) } - - before do - build.update(finished_at: 10.minutes.ago) - end - - it_behaves_like 'does not schedule to archive traces' - end - - context 'when a job is running' do - let!(:build) { create(:ci_build, :running, :trace_live) } - - it_behaves_like 'does not schedule to archive traces' - end -end -- cgit v1.2.1 From 32f825c648cb5fc829cd32b3392530613c62e983 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 14:22:31 +0900 Subject: Add tests for each new code --- spec/lib/gitlab/ci/trace_spec.rb | 2 +- spec/models/ci/build_trace_chunk_spec.rb | 40 ++++++++++++++++++++++ .../shared_examples/ci_trace_shared_examples.rb | 25 ++++++++++++++ .../ci/rescue_stale_live_trace_worker_spec.rb | 34 ++++++++++++------ 4 files changed, 89 insertions(+), 12 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb index e9d755c2021..d6510649dba 100644 --- a/spec/lib/gitlab/ci/trace_spec.rb +++ b/spec/lib/gitlab/ci/trace_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Trace, :clean_gitlab_redis_cache do +describe Gitlab::Ci::Trace, :clean_gitlab_redis_shared_state do let(:build) { create(:ci_build) } let(:trace) { described_class.new(build) } diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index cbcf1e55979..246152d1c8f 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -35,6 +35,46 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end end + describe '.find_stale_in_batches' do + subject { described_class.find_stale_in_batches } + + context 'when build status is finished' do + context 'when build finished 2 days ago' do + context 'when build has an archived trace' do + let!(:build) { create(:ci_build, :success, :trace_artifact, finished_at: 2.days.ago) } + + it 'does not yield build id' do + expect { |b| described_class.find_stale_in_batches(&b) }.not_to yield_control + end + end + + context 'when build has a live trace' do + let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } + + it 'yields build id' do + expect { |b| described_class.find_stale_in_batches(&b) }.to yield_with_args([build.id]) + end + end + end + + context 'when build finished 10 minutes ago' do + let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 10.minutes.ago) } + + it 'does not yield build id' do + expect { |b| described_class.find_stale_in_batches(&b) }.not_to yield_control + end + end + end + + context 'when build status is running' do + let!(:build) { create(:ci_build, :running, :trace_live) } + + it 'does not yield build id' do + expect { |b| described_class.find_stale_in_batches(&b) }.not_to yield_control + end + end + end + describe '#data' do subject { build_trace_chunk.data } diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index 21c6f3c829f..a3a0b2d0eb5 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -227,6 +227,31 @@ shared_examples_for 'common trace features' do end end end + + describe '#archive!' do + subject { trace.archive! } + + context 'when build status is success' do + let!(:build) { create(:ci_build, :success, :trace_live) } + + it 'archives a trace' do + subject + + expect(build.job_artifacts_trace).to be_exist + end + + context 'when anothe process had already been archiving', :clean_gitlab_redis_shared_state do + before do + Gitlab::ExclusiveLease.new("trace:archive:#{trace.job.id}", timeout: 1.hour).try_obtain + end + + it 'prevents multiple archiving' do + build.reload + expect(build.job_artifacts_trace).to be_nil + end + end + end + end end shared_examples_for 'trace with disabled live trace feature' do diff --git a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb index 87796fd66db..43a6362a131 100644 --- a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb +++ b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb @@ -7,19 +7,20 @@ describe Ci::RescueStaleLiveTraceWorker do stub_feature_flags(ci_enable_live_trace: true) end - shared_examples_for 'schedules to archive traces' do + shared_examples_for 'archives trace' do it do - expect(ArchiveTraceWorker).to receive(:bulk_perform_async).with([[build.id]]) - subject + + expect(build.job_artifacts_trace).to be_exist end end - shared_examples_for 'does not schedule to archive traces' do + shared_examples_for 'does not archive trace' do it do - expect(ArchiveTraceWorker).not_to receive(:bulk_perform_async) - subject + + build.reload + expect(build.job_artifacts_trace).to be_nil end end @@ -30,7 +31,18 @@ describe Ci::RescueStaleLiveTraceWorker do build.update(finished_at: 2.hours.ago) end - it_behaves_like 'schedules to archive traces' + it_behaves_like 'archives trace' + + context 'when build has both archived trace and live trace' do + let!(:build2) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } + + it 'archives only available targets' do + subject + + build.reload + expect(build.job_artifacts_trace).to be_exist + end + end end context 'when a job was failed 2 hours ago' do @@ -40,7 +52,7 @@ describe Ci::RescueStaleLiveTraceWorker do build.update(finished_at: 2.hours.ago) end - it_behaves_like 'schedules to archive traces' + it_behaves_like 'archives trace' end context 'when a job was cancelled 2 hours ago' do @@ -50,7 +62,7 @@ describe Ci::RescueStaleLiveTraceWorker do build.update(finished_at: 2.hours.ago) end - it_behaves_like 'schedules to archive traces' + it_behaves_like 'archives trace' end context 'when a job has been finished 10 minutes ago' do @@ -60,12 +72,12 @@ describe Ci::RescueStaleLiveTraceWorker do build.update(finished_at: 10.minutes.ago) end - it_behaves_like 'does not schedule to archive traces' + it_behaves_like 'does not archive trace' end context 'when a job is running' do let!(:build) { create(:ci_build, :running, :trace_live) } - it_behaves_like 'does not schedule to archive traces' + it_behaves_like 'does not archive trace' end end -- cgit v1.2.1 From 2522691eda46ef3ed572b747074e9b3b2e776198 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 16:15:41 +0900 Subject: Fix ambiguous stuck ci job worker's spec. Rename lease key of archive --- spec/workers/ci/rescue_stale_live_trace_worker_spec.rb | 2 +- spec/workers/stuck_ci_jobs_worker_spec.rb | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb index 43a6362a131..1a694290562 100644 --- a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb +++ b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb @@ -35,7 +35,7 @@ describe Ci::RescueStaleLiveTraceWorker do context 'when build has both archived trace and live trace' do let!(:build2) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } - + it 'archives only available targets' do subject diff --git a/spec/workers/stuck_ci_jobs_worker_spec.rb b/spec/workers/stuck_ci_jobs_worker_spec.rb index bdc64c6785b..c3294fce5ea 100644 --- a/spec/workers/stuck_ci_jobs_worker_spec.rb +++ b/spec/workers/stuck_ci_jobs_worker_spec.rb @@ -132,8 +132,10 @@ describe StuckCiJobsWorker do end it 'cancels exclusive lease after worker perform' do - expect(Gitlab::ExclusiveLease).to receive(:cancel).with(described_class::EXCLUSIVE_LEASE_KEY, exclusive_lease_uuid) worker.perform + + expect(Gitlab::ExclusiveLease.new(described_class::EXCLUSIVE_LEASE_KEY, timeout: 1.hour).exists?) + .to be_falsy end end end -- cgit v1.2.1 From 4064481501a24d31872914c845f5d8c2cfc08040 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 16:25:17 +0900 Subject: Rename find_stale_in_batches to find_builds_from_stale_live_trace. Fix comments --- spec/models/ci/build_trace_chunk_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 246152d1c8f..5d524d7cf49 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -35,8 +35,8 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end end - describe '.find_stale_in_batches' do - subject { described_class.find_stale_in_batches } + describe '.find_builds_from_stale_live_trace' do + subject { described_class.find_builds_from_stale_live_trace } context 'when build status is finished' do context 'when build finished 2 days ago' do @@ -44,7 +44,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :success, :trace_artifact, finished_at: 2.days.ago) } it 'does not yield build id' do - expect { |b| described_class.find_stale_in_batches(&b) }.not_to yield_control + expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.not_to yield_control end end @@ -52,7 +52,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } it 'yields build id' do - expect { |b| described_class.find_stale_in_batches(&b) }.to yield_with_args([build.id]) + expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.to yield_with_args([build.id]) end end end @@ -61,7 +61,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 10.minutes.ago) } it 'does not yield build id' do - expect { |b| described_class.find_stale_in_batches(&b) }.not_to yield_control + expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.not_to yield_control end end end @@ -70,7 +70,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :running, :trace_live) } it 'does not yield build id' do - expect { |b| described_class.find_stale_in_batches(&b) }.not_to yield_control + expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.not_to yield_control end end end -- cgit v1.2.1 From 2084e7ab9aad92d4a8196af01b9b8e02ffacb0a4 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 1 Jun 2018 17:09:46 +0900 Subject: Move find_builds_from_stale_live_traces method to Ci::Build --- spec/models/ci/build_trace_chunk_spec.rb | 12 ++++++------ spec/workers/ci/rescue_stale_live_trace_worker_spec.rb | 17 +++++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 5d524d7cf49..2dd8e935d62 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -35,8 +35,8 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end end - describe '.find_builds_from_stale_live_trace' do - subject { described_class.find_builds_from_stale_live_trace } + describe '.find_builds_from_stale_live_traces' do + subject { described_class.find_builds_from_stale_live_traces } context 'when build status is finished' do context 'when build finished 2 days ago' do @@ -44,7 +44,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :success, :trace_artifact, finished_at: 2.days.ago) } it 'does not yield build id' do - expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.not_to yield_control + expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.not_to yield_control end end @@ -52,7 +52,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } it 'yields build id' do - expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.to yield_with_args([build.id]) + expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.to yield_with_args([build.id]) end end end @@ -61,7 +61,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 10.minutes.ago) } it 'does not yield build id' do - expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.not_to yield_control + expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.not_to yield_control end end end @@ -70,7 +70,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do let!(:build) { create(:ci_build, :running, :trace_live) } it 'does not yield build id' do - expect { |b| described_class.find_builds_from_stale_live_trace(&b) }.not_to yield_control + expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.not_to yield_control end end end diff --git a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb index 1a694290562..6eee08fdbb5 100644 --- a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb +++ b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb @@ -11,6 +11,7 @@ describe Ci::RescueStaleLiveTraceWorker do it do subject + build.reload expect(build.job_artifacts_trace).to be_exist end end @@ -33,16 +34,16 @@ describe Ci::RescueStaleLiveTraceWorker do it_behaves_like 'archives trace' - context 'when build has both archived trace and live trace' do - let!(:build2) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } + # context 'when build has both archived trace and live trace' do + # let!(:build2) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } - it 'archives only available targets' do - subject + # it 'archives only available targets' do + # subject - build.reload - expect(build.job_artifacts_trace).to be_exist - end - end + # build.reload + # expect(build.job_artifacts_trace).to be_exist + # end + # end end context 'when a job was failed 2 hours ago' do -- cgit v1.2.1 From 5a1ee0c391a06a323d960eab6ffb33dfcf2edca7 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 2 Jun 2018 13:08:34 +0900 Subject: Fix the query to select stale live traces --- spec/models/ci/build_spec.rb | 20 +++++++++ spec/models/ci/build_trace_chunk_spec.rb | 40 ------------------ .../ci/rescue_stale_live_trace_worker_spec.rb | 47 +++++----------------- spec/workers/stuck_ci_jobs_worker_spec.rb | 4 +- 4 files changed, 33 insertions(+), 78 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 5e27cca6771..1b26c8d3b49 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -116,6 +116,26 @@ describe Ci::Build do end end + describe '.with_live_trace' do + subject { described_class.with_live_trace } + + context 'when build has live trace' do + let!(:build) { create(:ci_build, :success, :trace_live) } + + it 'selects the build' do + is_expected.to eq([build]) + end + end + + context 'when build does not have live trace' do + let!(:build) { create(:ci_build, :success, :trace_artifact) } + + it 'selects the build' do + is_expected.to be_empty + end + end + end + describe '#actionize' do context 'when build is a created' do before do diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 2dd8e935d62..cbcf1e55979 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -35,46 +35,6 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end end - describe '.find_builds_from_stale_live_traces' do - subject { described_class.find_builds_from_stale_live_traces } - - context 'when build status is finished' do - context 'when build finished 2 days ago' do - context 'when build has an archived trace' do - let!(:build) { create(:ci_build, :success, :trace_artifact, finished_at: 2.days.ago) } - - it 'does not yield build id' do - expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.not_to yield_control - end - end - - context 'when build has a live trace' do - let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } - - it 'yields build id' do - expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.to yield_with_args([build.id]) - end - end - end - - context 'when build finished 10 minutes ago' do - let!(:build) { create(:ci_build, :success, :trace_live, finished_at: 10.minutes.ago) } - - it 'does not yield build id' do - expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.not_to yield_control - end - end - end - - context 'when build status is running' do - let!(:build) { create(:ci_build, :running, :trace_live) } - - it 'does not yield build id' do - expect { |b| described_class.find_builds_from_stale_live_traces(&b) }.not_to yield_control - end - end - end - describe '#data' do subject { build_trace_chunk.data } diff --git a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb index 6eee08fdbb5..0e3fbe2d3a9 100644 --- a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb +++ b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb @@ -25,57 +25,32 @@ describe Ci::RescueStaleLiveTraceWorker do end end - context 'when a job was succeeded 2 hours ago' do + context 'when a job was succeeded' do let!(:build) { create(:ci_build, :success, :trace_live) } - before do - build.update(finished_at: 2.hours.ago) - end - it_behaves_like 'archives trace' - # context 'when build has both archived trace and live trace' do - # let!(:build2) { create(:ci_build, :success, :trace_live, finished_at: 2.days.ago) } + context 'when archive raised an exception' do + let!(:build) { create(:ci_build, :success, :trace_artifact, :trace_live) } + let!(:build2) { create(:ci_build, :success, :trace_live) } - # it 'archives only available targets' do - # subject - - # build.reload - # expect(build.job_artifacts_trace).to be_exist - # end - # end - end + it 'archives valid targets' do + expect(Rails.logger).to receive(:error).with("Failed to archive stale live trace. id: #{build.id} message: Already archived") - context 'when a job was failed 2 hours ago' do - let!(:build) { create(:ci_build, :failed, :trace_live) } + subject - before do - build.update(finished_at: 2.hours.ago) + build2.reload + expect(build2.job_artifacts_trace).to be_exist + end end - - it_behaves_like 'archives trace' end - context 'when a job was cancelled 2 hours ago' do + context 'when a job was cancelled' do let!(:build) { create(:ci_build, :canceled, :trace_live) } - before do - build.update(finished_at: 2.hours.ago) - end - it_behaves_like 'archives trace' end - context 'when a job has been finished 10 minutes ago' do - let!(:build) { create(:ci_build, :success, :trace_live) } - - before do - build.update(finished_at: 10.minutes.ago) - end - - it_behaves_like 'does not archive trace' - end - context 'when a job is running' do let!(:build) { create(:ci_build, :running, :trace_live) } diff --git a/spec/workers/stuck_ci_jobs_worker_spec.rb b/spec/workers/stuck_ci_jobs_worker_spec.rb index c3294fce5ea..2605c14334f 100644 --- a/spec/workers/stuck_ci_jobs_worker_spec.rb +++ b/spec/workers/stuck_ci_jobs_worker_spec.rb @@ -134,8 +134,8 @@ describe StuckCiJobsWorker do it 'cancels exclusive lease after worker perform' do worker.perform - expect(Gitlab::ExclusiveLease.new(described_class::EXCLUSIVE_LEASE_KEY, timeout: 1.hour).exists?) - .to be_falsy + expect(Gitlab::ExclusiveLease.new(described_class::EXCLUSIVE_LEASE_KEY, timeout: 1.hour)) + .not_to be_exists end end end -- cgit v1.2.1 From f9b821f08dfc8feee2ed8a06c8f21c85c3c9d2ec Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Sat, 2 Jun 2018 14:09:24 +0900 Subject: Fix specs for exclusive lease --- .../shared_examples/ci_trace_shared_examples.rb | 29 ++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'spec') diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index a3a0b2d0eb5..59efce1b5f0 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -234,20 +234,29 @@ shared_examples_for 'common trace features' do context 'when build status is success' do let!(:build) { create(:ci_build, :success, :trace_live) } - it 'archives a trace' do - subject - - expect(build.job_artifacts_trace).to be_exist + it 'does not have an archived trace yet' do + expect(build.job_artifacts_trace).to be_nil end - context 'when anothe process had already been archiving', :clean_gitlab_redis_shared_state do - before do - Gitlab::ExclusiveLease.new("trace:archive:#{trace.job.id}", timeout: 1.hour).try_obtain - end + context 'when archives' do + it 'has an archived trace' do + subject - it 'prevents multiple archiving' do build.reload - expect(build.job_artifacts_trace).to be_nil + expect(build.job_artifacts_trace).to be_exist + end + + context 'when anothe process had already been archiving', :clean_gitlab_redis_shared_state do + before do + Gitlab::ExclusiveLease.new("trace:archive:#{trace.job.id}", timeout: 1.hour).try_obtain + end + + it 'prevents to archive concurently' do + subject + + build.reload + expect(build.job_artifacts_trace).to be_nil + end end end end -- cgit v1.2.1 From cae17352761d9c34de444cb95e77d8fa1a8bd56a Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 4 Jun 2018 14:12:45 +0900 Subject: Fix typos and add a small spec --- spec/models/ci/build_spec.rb | 2 +- spec/support/shared_examples/ci_trace_shared_examples.rb | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 1b26c8d3b49..0a0d7d3fea9 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -130,7 +130,7 @@ describe Ci::Build do context 'when build does not have live trace' do let!(:build) { create(:ci_build, :success, :trace_artifact) } - it 'selects the build' do + it 'does not select the build' do is_expected.to be_empty end end diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index 59efce1b5f0..6dbe0f6f980 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -246,12 +246,14 @@ shared_examples_for 'common trace features' do expect(build.job_artifacts_trace).to be_exist end - context 'when anothe process had already been archiving', :clean_gitlab_redis_shared_state do + context 'when another process has already been archiving', :clean_gitlab_redis_shared_state do before do Gitlab::ExclusiveLease.new("trace:archive:#{trace.job.id}", timeout: 1.hour).try_obtain end - it 'prevents to archive concurently' do + it 'blocks concurrent archiving' do + expect(Rails.logger).to receive(:error).with('Cannot obtain an exclusive lease. There must be another instance already in execution.') + subject build.reload -- cgit v1.2.1 From 9b65d4bb417fb4939289eab94487c894f0a62db6 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Wed, 23 May 2018 09:55:14 +0200 Subject: Initial setup GraphQL using graphql-ruby 1.8 - All definitions have been replaced by classes: http://graphql-ruby.org/schema/class_based_api.html - Authorization & Presentation have been refactored to work in the class based system - Loaders have been replaced by resolvers - Times are now coersed as ISO 8601 --- spec/graphql/gitlab_schema_spec.rb | 15 +++--- spec/graphql/loaders/full_path_loader_spec.rb | 32 ------------ spec/graphql/loaders/iid_loader_spec.rb | 58 ---------------------- .../resolvers/merge_request_resolver_spec.rb | 58 ++++++++++++++++++++++ spec/graphql/resolvers/project_resolver_spec.rb | 32 ++++++++++++ spec/graphql/types/project_type_spec.rb | 5 ++ spec/graphql/types/query_type_spec.rb | 10 ++-- spec/graphql/types/time_type_spec.rb | 14 +++--- .../api/graphql/merge_request_query_spec.rb | 49 ++++++++++++++++++ spec/requests/api/graphql/project_query_spec.rb | 39 +++++++++++++++ spec/requests/graphql/merge_request_query_spec.rb | 24 --------- spec/requests/graphql/project_query_spec.rb | 23 --------- spec/routing/api_routing_spec.rb | 31 ++++++++++++ spec/support/helpers/graphql_helpers.rb | 47 +++++++++++++++--- spec/support/matchers/graphql_matchers.rb | 23 ++++++--- .../requests/graphql_shared_examples.rb | 11 +--- 16 files changed, 292 insertions(+), 179 deletions(-) delete mode 100644 spec/graphql/loaders/full_path_loader_spec.rb delete mode 100644 spec/graphql/loaders/iid_loader_spec.rb create mode 100644 spec/graphql/resolvers/merge_request_resolver_spec.rb create mode 100644 spec/graphql/resolvers/project_resolver_spec.rb create mode 100644 spec/graphql/types/project_type_spec.rb create mode 100644 spec/requests/api/graphql/merge_request_query_spec.rb create mode 100644 spec/requests/api/graphql/project_query_spec.rb delete mode 100644 spec/requests/graphql/merge_request_query_spec.rb delete mode 100644 spec/requests/graphql/project_query_spec.rb create mode 100644 spec/routing/api_routing_spec.rb (limited to 'spec') diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index f25cc2fd6c9..b892f6b44ed 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -6,26 +6,25 @@ describe GitlabSchema do end it 'enables the preload instrumenter' do - expect(field_instrumenters).to include(instance_of(::GraphQL::Preload::Instrument)) + expect(field_instrumenters).to include(BatchLoader::GraphQL) end it 'enables the authorization instrumenter' do - expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Authorize)) + expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Authorize::Instrumentation)) end it 'enables using presenters' do - expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Present)) + expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Present::Instrumentation)) end it 'has the base mutation' do - pending <<~REASON - Having empty mutations breaks the automatic documentation in Graphiql, so removed for now." - REASON - expect(described_class.mutation).to eq(::Types::MutationType) + pending('Adding an empty mutation breaks the documentation explorer') + + expect(described_class.mutation).to eq(::Types::MutationType.to_graphql) end it 'has the base query' do - expect(described_class.query).to eq(::Types::QueryType) + expect(described_class.query).to eq(::Types::QueryType.to_graphql) end def field_instrumenters diff --git a/spec/graphql/loaders/full_path_loader_spec.rb b/spec/graphql/loaders/full_path_loader_spec.rb deleted file mode 100644 index 2732dd8c9da..00000000000 --- a/spec/graphql/loaders/full_path_loader_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'spec_helper' - -describe Loaders::FullPathLoader do - include GraphqlHelpers - - set(:project1) { create(:project) } - set(:project2) { create(:project) } - - set(:other_project) { create(:project) } - - describe '.project' do - it 'batch-resolves projects by full path' do - paths = [project1.full_path, project2.full_path] - - result = batch(max_queries: 1) do - paths.map { |path| resolve_project(path) } - end - - expect(result).to contain_exactly(project1, project2) - end - - it 'resolves an unknown full_path to nil' do - result = batch { resolve_project('unknown/project') } - - expect(result).to be_nil - end - end - - def resolve_project(full_path) - resolve(described_class, :project, args: { full_path: full_path }) - end -end diff --git a/spec/graphql/loaders/iid_loader_spec.rb b/spec/graphql/loaders/iid_loader_spec.rb deleted file mode 100644 index 0a57d7c4ed4..00000000000 --- a/spec/graphql/loaders/iid_loader_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'spec_helper' - -describe Loaders::IidLoader do - include GraphqlHelpers - - set(:project) { create(:project, :repository) } - set(:merge_request_1) { create(:merge_request, :simple, source_project: project, target_project: project) } - set(:merge_request_2) { create(:merge_request, :rebased, source_project: project, target_project: project) } - - set(:other_project) { create(:project, :repository) } - set(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) } - - let(:full_path) { project.full_path } - let(:iid_1) { merge_request_1.iid } - let(:iid_2) { merge_request_2.iid } - - let(:other_full_path) { other_project.full_path } - let(:other_iid) { other_merge_request.iid } - - describe '.merge_request' do - it 'batch-resolves merge requests by target project full path and IID' do - path = full_path # avoid database query - - result = batch(max_queries: 2) do - [resolve_mr(path, iid_1), resolve_mr(path, iid_2)] - end - - expect(result).to contain_exactly(merge_request_1, merge_request_2) - end - - it 'can batch-resolve merge requests from different projects' do - path = project.full_path # avoid database queries - other_path = other_full_path - - result = batch(max_queries: 3) do - [resolve_mr(path, iid_1), resolve_mr(path, iid_2), resolve_mr(other_path, other_iid)] - end - - expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request) - end - - it 'resolves an unknown iid to nil' do - result = batch { resolve_mr(full_path, -1) } - - expect(result).to be_nil - end - - it 'resolves a known iid for an unknown full_path to nil' do - result = batch { resolve_mr('unknown/project', iid_1) } - - expect(result).to be_nil - end - end - - def resolve_mr(full_path, iid) - resolve(described_class, :merge_request, args: { project: full_path, iid: iid }) - end -end diff --git a/spec/graphql/resolvers/merge_request_resolver_spec.rb b/spec/graphql/resolvers/merge_request_resolver_spec.rb new file mode 100644 index 00000000000..af015533209 --- /dev/null +++ b/spec/graphql/resolvers/merge_request_resolver_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe Resolvers::MergeRequestResolver do + include GraphqlHelpers + + set(:project) { create(:project, :repository) } + set(:merge_request_1) { create(:merge_request, :simple, source_project: project, target_project: project) } + set(:merge_request_2) { create(:merge_request, :rebased, source_project: project, target_project: project) } + + set(:other_project) { create(:project, :repository) } + set(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) } + + let(:full_path) { project.full_path } + let(:iid_1) { merge_request_1.iid } + let(:iid_2) { merge_request_2.iid } + + let(:other_full_path) { other_project.full_path } + let(:other_iid) { other_merge_request.iid } + + describe '#resolve' do + it 'batch-resolves merge requests by target project full path and IID' do + path = full_path # avoid database query + + result = batch(max_queries: 2) do + [resolve_mr(path, iid_1), resolve_mr(path, iid_2)] + end + + expect(result).to contain_exactly(merge_request_1, merge_request_2) + end + + it 'can batch-resolve merge requests from different projects' do + path = project.full_path # avoid database queries + other_path = other_full_path + + result = batch(max_queries: 3) do + [resolve_mr(path, iid_1), resolve_mr(path, iid_2), resolve_mr(other_path, other_iid)] + end + + expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request) + end + + it 'resolves an unknown iid to nil' do + result = batch { resolve_mr(full_path, -1) } + + expect(result).to be_nil + end + + it 'resolves a known iid for an unknown full_path to nil' do + result = batch { resolve_mr('unknown/project', iid_1) } + + expect(result).to be_nil + end + end + + def resolve_mr(full_path, iid) + resolve(described_class, args: { full_path: full_path, iid: iid }) + end +end diff --git a/spec/graphql/resolvers/project_resolver_spec.rb b/spec/graphql/resolvers/project_resolver_spec.rb new file mode 100644 index 00000000000..d4990c6492c --- /dev/null +++ b/spec/graphql/resolvers/project_resolver_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Resolvers::ProjectResolver do + include GraphqlHelpers + + set(:project1) { create(:project) } + set(:project2) { create(:project) } + + set(:other_project) { create(:project) } + + describe '#resolve' do + it 'batch-resolves projects by full path' do + paths = [project1.full_path, project2.full_path] + + result = batch(max_queries: 1) do + paths.map { |path| resolve_project(path) } + end + + expect(result).to contain_exactly(project1, project2) + end + + it 'resolves an unknown full_path to nil' do + result = batch { resolve_project('unknown/project') } + + expect(result).to be_nil + end + end + + def resolve_project(full_path) + resolve(described_class, args: { full_path: full_path }) + end +end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb new file mode 100644 index 00000000000..e0f89105b86 --- /dev/null +++ b/spec/graphql/types/project_type_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe GitlabSchema.types['Project'] do + it { expect(described_class.graphql_name).to eq('Project') } +end diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index 17d9395504c..8488252fd59 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe GitlabSchema.types['Query'] do it 'is called Query' do - expect(described_class.name).to eq('Query') + expect(described_class.graphql_name).to eq('Query') end it { is_expected.to have_graphql_fields(:project, :merge_request, :echo) } @@ -13,7 +13,7 @@ describe GitlabSchema.types['Query'] do it 'finds projects by full path' do is_expected.to have_graphql_arguments(:full_path) is_expected.to have_graphql_type(Types::ProjectType) - is_expected.to have_graphql_resolver(Loaders::FullPathLoader[:project]) + is_expected.to have_graphql_resolver(Resolvers::ProjectResolver) end it 'authorizes with read_project' do @@ -22,12 +22,12 @@ describe GitlabSchema.types['Query'] do end describe 'merge_request field' do - subject { described_class.fields['merge_request'] } + subject { described_class.fields['mergeRequest'] } it 'finds MRs by project and IID' do - is_expected.to have_graphql_arguments(:project, :iid) + is_expected.to have_graphql_arguments(:full_path, :iid) is_expected.to have_graphql_type(Types::MergeRequestType) - is_expected.to have_graphql_resolver(Loaders::IidLoader[:merge_request]) + is_expected.to have_graphql_resolver(Resolvers::MergeRequestResolver) end it 'authorizes with read_merge_request' do diff --git a/spec/graphql/types/time_type_spec.rb b/spec/graphql/types/time_type_spec.rb index 087655cc67d..4196d9d27d4 100644 --- a/spec/graphql/types/time_type_spec.rb +++ b/spec/graphql/types/time_type_spec.rb @@ -1,16 +1,16 @@ require 'spec_helper' describe GitlabSchema.types['Time'] do - let(:float) { 1504630455.96215 } - let(:time) { Time.at(float) } + let(:iso) { "2018-06-04T15:23:50+02:00" } + let(:time) { Time.parse(iso) } - it { expect(described_class.name).to eq('Time') } + it { expect(described_class.graphql_name).to eq('Time') } - it 'coerces Time into fractional seconds since epoch' do - expect(described_class.coerce_isolated_result(time)).to eq(float) + it 'coerces Time object into ISO 8601' do + expect(described_class.coerce_isolated_result(time)).to eq(iso) end - it 'coerces fractional seconds since epoch into Time' do - expect(described_class.coerce_isolated_input(float)).to eq(time) + it 'coerces an ISO-time into Time object' do + expect(described_class.coerce_isolated_input(iso)).to eq(time) end end diff --git a/spec/requests/api/graphql/merge_request_query_spec.rb b/spec/requests/api/graphql/merge_request_query_spec.rb new file mode 100644 index 00000000000..12b1d5d18a2 --- /dev/null +++ b/spec/requests/api/graphql/merge_request_query_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe 'getting merge request information' do + include GraphqlHelpers + + let(:project) { create(:project, :repository) } + let(:merge_request) { create(:merge_request, source_project: project) } + let(:current_user) { create(:user) } + + let(:query) do + attributes = { + 'fullPath' => merge_request.project.full_path, + 'iid' => merge_request.iid + } + graphql_query_for('mergeRequest', attributes) + end + + context 'when the user has access to the merge request' do + before do + project.add_developer(current_user) + post_graphql(query, current_user: current_user) + end + + it 'returns the merge request' do + expect(graphql_data['mergeRequest']).not_to be_nil + end + + # This is a field coming from the `MergeRequestPresenter` + it 'includes a web_url' do + expect(graphql_data['mergeRequest']['webUrl']).to be_present + end + + it_behaves_like 'a working graphql query' + end + + context 'when the user does not have access to the merge request' do + before do + post_graphql(query, current_user: current_user) + end + + it 'returns an empty field' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['mergeRequest']).to be_nil + end + + it_behaves_like 'a working graphql query' + end +end diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb new file mode 100644 index 00000000000..8196bcfa87c --- /dev/null +++ b/spec/requests/api/graphql/project_query_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'getting project information' do + include GraphqlHelpers + + let(:project) { create(:project, :repository) } + let(:current_user) { create(:user) } + + let(:query) do + graphql_query_for('project', 'fullPath' => project.full_path) + end + + context 'when the user has access to the project' do + before do + project.add_developer(current_user) + post_graphql(query, current_user: current_user) + end + + it 'includes the project' do + expect(graphql_data['project']).not_to be_nil + end + + it_behaves_like 'a working graphql query' + end + + context 'when the user does not have access to the project' do + before do + post_graphql(query, current_user: current_user) + end + + it 'returns an empty field' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['project']).to be_nil + end + + it_behaves_like 'a working graphql query' + end +end diff --git a/spec/requests/graphql/merge_request_query_spec.rb b/spec/requests/graphql/merge_request_query_spec.rb deleted file mode 100644 index cbc19782e2f..00000000000 --- a/spec/requests/graphql/merge_request_query_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'spec_helper' - -describe 'getting merge request information' do - include GraphqlHelpers - - let(:project) { create(:project, :repository, :public) } - let(:merge_request) { create(:merge_request, source_project: project) } - - let(:query) do - <<~QUERY - { - merge_request(project: "#{merge_request.project.full_path}", iid: "#{merge_request.iid}") { - #{all_graphql_fields_for(MergeRequest)} - } - } - QUERY - end - - it_behaves_like 'a working graphql query' do - it 'renders a merge request with all fields' do - expect(response_data['merge_request']).not_to be_nil - end - end -end diff --git a/spec/requests/graphql/project_query_spec.rb b/spec/requests/graphql/project_query_spec.rb deleted file mode 100644 index 110ed433e03..00000000000 --- a/spec/requests/graphql/project_query_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'spec_helper' - -describe 'getting project information' do - include GraphqlHelpers - - let(:project) { create(:project, :repository, :public) } - - let(:query) do - <<~QUERY - { - project(full_path: "#{project.full_path}") { - #{all_graphql_fields_for(Project)} - } - } - QUERY - end - - it_behaves_like 'a working graphql query' do - it 'renders a project with all fields' do - expect(response_data['project']).not_to be_nil - end - end -end diff --git a/spec/routing/api_routing_spec.rb b/spec/routing/api_routing_spec.rb new file mode 100644 index 00000000000..5fde4bd885b --- /dev/null +++ b/spec/routing/api_routing_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe 'api', 'routing' do + context 'when graphql is disabled' do + before do + stub_feature_flags(graphql: false) + end + + it 'does not route to the GraphqlController' do + expect(get('/api/graphql')).not_to route_to('graphql#execute') + end + + it 'does not expose graphiql' do + expect(get('/-/graphql-explorer')).not_to route_to('graphiql/rails/editors#show') + end + end + + context 'when graphql is disabled' do + before do + stub_feature_flags(graphql: true) + end + + it 'routes to the GraphqlController' do + expect(get('/api/graphql')).not_to route_to('graphql#execute') + end + + it 'exposes graphiql' do + expect(get('/-/graphql-explorer')).not_to route_to('graphiql/rails/editors#show') + end + end +end diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index ae29b16e32c..30ff9a1196a 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -1,7 +1,16 @@ module GraphqlHelpers + # makes an underscored string look like a fieldname + # "merge_request" => "mergeRequest" + def self.fieldnamerize(underscored_field_name) + graphql_field_name = underscored_field_name.to_s.camelize + graphql_field_name[0] = graphql_field_name[0].downcase + + graphql_field_name + end + # Run a loader's named resolver - def resolve(kls, name, obj: nil, args: {}, ctx: {}) - kls[name].call(obj, args, ctx) + def resolve(resolver_class, obj: nil, args: {}, ctx: {}) + resolver_class.new(object: obj, context: ctx).resolve(args) end # Runs a block inside a BatchLoader::Executor wrapper @@ -24,8 +33,20 @@ module GraphqlHelpers end end - def all_graphql_fields_for(klass) - type = GitlabSchema.types[klass.name] + def graphql_query_for(name, attributes = {}, fields = nil) + fields ||= all_graphql_fields_for(name.classify) + attributes = attributes_to_graphql(attributes) + <<~QUERY + { + #{name}(#{attributes}) { + #{fields} + } + } + QUERY + end + + def all_graphql_fields_for(class_name) + type = GitlabSchema.types[class_name.to_s] return "" unless type type.fields.map do |name, field| @@ -37,8 +58,22 @@ module GraphqlHelpers end.join("\n") end - def post_graphql(query) - post '/api/graphql', query: query + def attributes_to_graphql(attributes) + attributes.map do |name, value| + "#{GraphqlHelpers.fieldnamerize(name.to_s)}: \"#{value}\"" + end.join(", ") + end + + def post_graphql(query, current_user: nil) + post api('/', current_user, version: 'graphql'), query: query + end + + def graphql_data + json_response['data'] + end + + def graphql_errors + json_response['data'] end def scalar?(field) diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb index c0ed16ecaba..ba7a1c8cde0 100644 --- a/spec/support/matchers/graphql_matchers.rb +++ b/spec/support/matchers/graphql_matchers.rb @@ -1,31 +1,40 @@ RSpec::Matchers.define :require_graphql_authorizations do |*expected| match do |field| - authorizations = field.metadata[:authorize] - - expect(authorizations).to contain_exactly(*expected) + field_definition = field.metadata[:type_class] + expect(field_definition).to respond_to(:required_permissions) + expect(field_definition.required_permissions).to contain_exactly(*expected) end end RSpec::Matchers.define :have_graphql_fields do |*expected| match do |kls| - expect(kls.fields.keys).to contain_exactly(*expected.map(&:to_s)) + field_names = expected.map { |name| GraphqlHelpers.fieldnamerize(name) } + expect(kls.fields.keys).to contain_exactly(*field_names) end end RSpec::Matchers.define :have_graphql_arguments do |*expected| + include GraphqlHelpers + match do |field| - expect(field.arguments.keys).to contain_exactly(*expected.map(&:to_s)) + argument_names = expected.map { |name| GraphqlHelpers.fieldnamerize(name) } + expect(field.arguments.keys).to contain_exactly(*argument_names) end end RSpec::Matchers.define :have_graphql_type do |expected| match do |field| - expect(field.type).to eq(expected) + expect(field.type).to eq(expected.to_graphql) end end RSpec::Matchers.define :have_graphql_resolver do |expected| match do |field| - expect(field.resolve_proc).to eq(expected) + case expected + when Method + expect(field.metadata[:type_class].resolve_proc).to eq(expected) + else + expect(field.metadata[:type_class].resolver).to eq(expected) + end end end diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb index c143b83696e..9b2b74593a5 100644 --- a/spec/support/shared_examples/requests/graphql_shared_examples.rb +++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb @@ -3,16 +3,9 @@ require 'spec_helper' shared_examples 'a working graphql query' do include GraphqlHelpers - let(:parsed_response) { JSON.parse(response.body) } - let(:response_data) { parsed_response['data'] } - - before do - post_graphql(query) - end - it 'is returns a successfull response', :aggregate_failures do expect(response).to be_success - expect(parsed_response['errors']).to be_nil - expect(response_data).not_to be_empty + expect(graphql_errors['errors']).to be_nil + expect(json_response.keys).to include('data') end end -- cgit v1.2.1 From 332275b766283ff65d0637ada3e4080c3cd4f038 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 6 Jun 2018 01:56:50 -0700 Subject: Simplify error message handling in Projects::CreateService There's no need to add a redundant message to the errors if the model is invalid. This cleans up the message as well for the importer. --- .../githubish_import_controller_shared_examples.rb | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'spec') diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb index 1a53c5fa487..1c1b68c12a2 100644 --- a/spec/support/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -119,21 +119,6 @@ shared_examples 'a GitHub-ish import controller: POST create' do end it 'returns 422 response with the base error when the project could not be imported' do - project = build(:project) - error_message = 'This is an error' - project.errors.add(:base, error_message) - - allow(Gitlab::LegacyGithubImport::ProjectCreator) - .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider) - .and_return(double(execute: project)) - - post :create, format: :json - - expect(response).to have_gitlab_http_status(422) - expect(json_response['errors']).to eq(error_message) - end - - it 'returns 422 response with a combined error when the project could not be imported' do project = build(:project) project.errors.add(:name, 'is invalid') project.errors.add(:path, 'is old') -- cgit v1.2.1 From 29598f6e6d8ebbe9c1b64018fcb925655ccceb67 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Tue, 22 May 2018 18:37:35 +0200 Subject: find assets in test for CI and local test Due to the change in https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14583/diffs we can't use the same method to access assets in a CI and the local test environment anymore. --- spec/models/project_services/jira_service_spec.rb | 3 ++- spec/services/system_note_service_spec.rb | 3 ++- spec/support/helpers/assets_helpers.rb | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 spec/support/helpers/assets_helpers.rb (limited to 'spec') diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 50bdb80ff92..c3b4eb17a5c 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe JiraService do include Gitlab::Routing + include AssetsHelpers describe '#options' do let(:service) do @@ -164,7 +165,7 @@ describe JiraService do it "creates Remote Link reference in JIRA for comment" do @jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project)) - favicon_path = "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" + favicon_path = "http://localhost/assets/#{find_asset('favicon.png').digest_path}" # Creates comment expect(WebMock).to have_requested(:post, @comment_url) diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index e2ee421921c..57d081cffb3 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe SystemNoteService do include Gitlab::Routing include RepoHelpers + include AssetsHelpers set(:group) { create(:group) } set(:project) { create(:project, :repository, group: group) } @@ -769,7 +770,7 @@ describe SystemNoteService do end describe "new reference" do - let(:favicon_path) { "http://localhost/assets/#{Rails.application.assets.find_asset('favicon.png').digest_path}" } + let(:favicon_path) { "http://localhost/assets/#{find_asset('favicon.png').digest_path}" } before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) diff --git a/spec/support/helpers/assets_helpers.rb b/spec/support/helpers/assets_helpers.rb new file mode 100644 index 00000000000..09bbf451671 --- /dev/null +++ b/spec/support/helpers/assets_helpers.rb @@ -0,0 +1,15 @@ +module AssetsHelpers + # In a CI environment the assets are not compiled, as there is a CI job + # `compile-assets` that compiles them in the prepare stage for all following + # specs. + # Locally the assets are precompiled dynamically. + # + # Sprockets doesn't provide one method to access an asset for both cases. + def find_asset(asset_name) + if ENV['CI'] + Sprockets::Railtie.build_environment(Rails.application, true)[asset_name] + else + Rails.application.assets.find_asset(asset_name) + end + end +end -- cgit v1.2.1 From c5e44dc5d1a98a92362b93c30342ebf1f972061c Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Tue, 5 Jun 2018 22:20:37 +0200 Subject: Use Github repo visibility during import while respecting restricted visibility levels --- .../legacy_github_import/project_creator_spec.rb | 40 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb index eb1b13704ea..972b17d5b12 100644 --- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb @@ -44,16 +44,44 @@ describe Gitlab::LegacyGithubImport::ProjectCreator do end context 'when GitHub project is public' do - before do - allow_any_instance_of(ApplicationSetting).to receive(:default_project_visibility).and_return(Gitlab::VisibilityLevel::INTERNAL) - end - - it 'sets project visibility to the default project visibility' do + it 'sets project visibility to public' do repo.private = false project = service.execute - expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) + end + end + + context 'when visibility level is restricted' do + context 'when GitHub project is private' do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE]) + allow_any_instance_of(ApplicationSetting).to receive(:default_project_visibility).and_return(Gitlab::VisibilityLevel::INTERNAL) + end + + it 'sets project visibility to the default project visibility' do + repo.private = true + + project = service.execute + + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) + end + end + + context 'when GitHub project is public' do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + allow_any_instance_of(ApplicationSetting).to receive(:default_project_visibility).and_return(Gitlab::VisibilityLevel::INTERNAL) + end + + it 'sets project visibility to the default project visibility' do + repo.private = false + + project = service.execute + + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) + end end end -- cgit v1.2.1 From f8d8b63f32176975c802313d966fb81c2bb2122a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Jun 2018 08:57:13 +0100 Subject: fixed karma --- .../stores/modules/merge_requests/actions_spec.js | 84 +++++++++++++++++----- 1 file changed, 66 insertions(+), 18 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js index b571cfb963a..d178a44b76a 100644 --- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js @@ -8,7 +8,9 @@ import actions, { receiveMergeRequestsSuccess, fetchMergeRequests, resetMergeRequests, + openMergeRequest, } from '~/ide/stores/modules/merge_requests/actions'; +import router from '~/ide/ide_router'; import { mergeRequests } from '../../../mock_data'; import testAction from '../../../../helpers/vuex_action_helper'; @@ -29,9 +31,9 @@ describe('IDE merge requests actions', () => { it('should should commit request', done => { testAction( requestMergeRequests, - null, + 'created', mockedState, - [{ type: types.REQUEST_MERGE_REQUESTS }], + [{ type: types.REQUEST_MERGE_REQUESTS, payload: 'created' }], [], done, ); @@ -48,16 +50,16 @@ describe('IDE merge requests actions', () => { it('should should commit error', done => { testAction( receiveMergeRequestsError, - null, + 'created', mockedState, - [{ type: types.RECEIVE_MERGE_REQUESTS_ERROR }], + [{ type: types.RECEIVE_MERGE_REQUESTS_ERROR, payload: 'created' }], [], done, ); }); it('creates flash message', () => { - receiveMergeRequestsError({ commit() {} }); + receiveMergeRequestsError({ commit() {} }, 'created'); expect(flashSpy).toHaveBeenCalled(); }); @@ -67,9 +69,14 @@ describe('IDE merge requests actions', () => { it('should commit received data', done => { testAction( receiveMergeRequestsSuccess, - 'data', + { type: 'created', data: 'data' }, mockedState, - [{ type: types.RECEIVE_MERGE_REQUESTS_SUCCESS, payload: 'data' }], + [ + { + type: types.RECEIVE_MERGE_REQUESTS_SUCCESS, + payload: { type: 'created', data: 'data' }, + }, + ], [], done, ); @@ -86,14 +93,14 @@ describe('IDE merge requests actions', () => { mock.onGet(/\/api\/v4\/merge_requests(.*)$/).replyOnce(200, mergeRequests); }); - it('calls API with params from state', () => { + it('calls API with params', () => { const apiSpy = spyOn(axios, 'get').and.callThrough(); - fetchMergeRequests({ dispatch() {}, state: mockedState }); + fetchMergeRequests({ dispatch() {}, state: mockedState }, { type: 'created' }); expect(apiSpy).toHaveBeenCalledWith(jasmine.anything(), { params: { - scope: 'assigned-to-me', + scope: 'created-by-me', state: 'opened', search: '', }, @@ -103,11 +110,14 @@ describe('IDE merge requests actions', () => { it('calls API with search', () => { const apiSpy = spyOn(axios, 'get').and.callThrough(); - fetchMergeRequests({ dispatch() {}, state: mockedState }, 'testing search'); + fetchMergeRequests( + { dispatch() {}, state: mockedState }, + { type: 'created', search: 'testing search' }, + ); expect(apiSpy).toHaveBeenCalledWith(jasmine.anything(), { params: { - scope: 'assigned-to-me', + scope: 'created-by-me', state: 'opened', search: 'testing search', }, @@ -117,7 +127,7 @@ describe('IDE merge requests actions', () => { it('dispatches request', done => { testAction( fetchMergeRequests, - null, + { type: 'created' }, mockedState, [], [ @@ -132,13 +142,16 @@ describe('IDE merge requests actions', () => { it('dispatches success with received data', done => { testAction( fetchMergeRequests, - null, + { type: 'created' }, mockedState, [], [ { type: 'requestMergeRequests' }, { type: 'resetMergeRequests' }, - { type: 'receiveMergeRequestsSuccess', payload: mergeRequests }, + { + type: 'receiveMergeRequestsSuccess', + payload: { type: 'created', data: mergeRequests }, + }, ], done, ); @@ -153,7 +166,7 @@ describe('IDE merge requests actions', () => { it('dispatches error', done => { testAction( fetchMergeRequests, - null, + { type: 'created' }, mockedState, [], [ @@ -171,12 +184,47 @@ describe('IDE merge requests actions', () => { it('commits reset', done => { testAction( resetMergeRequests, - null, + 'created', mockedState, - [{ type: types.RESET_MERGE_REQUESTS }], + [{ type: types.RESET_MERGE_REQUESTS, payload: 'created' }], [], done, ); }); }); + + describe('openMergeRequest', () => { + beforeEach(() => { + spyOn(router, 'push'); + }); + + it('commits reset mutations and actions', done => { + testAction( + openMergeRequest, + { projectPath: 'gitlab-org/gitlab-ce', id: '1' }, + mockedState, + [ + { type: 'CLEAR_PROJECTS' }, + { type: 'SET_CURRENT_MERGE_REQUEST', payload: '1' }, + { type: 'RESET_OPEN_FILES' }, + ], + [ + { type: 'pipelines/stopPipelinePolling' }, + { type: 'pipelines/clearEtagPoll' }, + { type: 'pipelines/resetLatestPipeline' }, + { type: 'setCurrentBranchId', payload: '' }, + ], + done, + ); + }); + + it('pushes new route', () => { + openMergeRequest( + { commit() {}, dispatch() {} }, + { projectPath: 'gitlab-org/gitlab-ce', id: '1' }, + ); + + expect(router.push).toHaveBeenCalledWith('/project/gitlab-org/gitlab-ce/merge_requests/1'); + }); + }); }); -- cgit v1.2.1 From 95b3ff056fb9edabef6c512c5de353c5c4a8bcb3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 5 Jun 2018 09:50:48 +0100 Subject: fixed mutations spec --- .../stores/modules/merge_requests/mutations_spec.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js index 664d3914564..ea03131d90d 100644 --- a/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js +++ b/spec/javascripts/ide/stores/modules/merge_requests/mutations_spec.js @@ -12,26 +12,29 @@ describe('IDE merge requests mutations', () => { describe(types.REQUEST_MERGE_REQUESTS, () => { it('sets loading to true', () => { - mutations[types.REQUEST_MERGE_REQUESTS](mockedState); + mutations[types.REQUEST_MERGE_REQUESTS](mockedState, 'created'); - expect(mockedState.isLoading).toBe(true); + expect(mockedState.created.isLoading).toBe(true); }); }); describe(types.RECEIVE_MERGE_REQUESTS_ERROR, () => { it('sets loading to false', () => { - mutations[types.RECEIVE_MERGE_REQUESTS_ERROR](mockedState); + mutations[types.RECEIVE_MERGE_REQUESTS_ERROR](mockedState, 'created'); - expect(mockedState.isLoading).toBe(false); + expect(mockedState.created.isLoading).toBe(false); }); }); describe(types.RECEIVE_MERGE_REQUESTS_SUCCESS, () => { it('sets merge requests', () => { gon.gitlab_url = gl.TEST_HOST; - mutations[types.RECEIVE_MERGE_REQUESTS_SUCCESS](mockedState, mergeRequests); + mutations[types.RECEIVE_MERGE_REQUESTS_SUCCESS](mockedState, { + type: 'created', + data: mergeRequests, + }); - expect(mockedState.mergeRequests).toEqual([ + expect(mockedState.created.mergeRequests).toEqual([ { id: 1, iid: 1, @@ -47,9 +50,9 @@ describe('IDE merge requests mutations', () => { it('clears merge request array', () => { mockedState.mergeRequests = ['test']; - mutations[types.RESET_MERGE_REQUESTS](mockedState); + mutations[types.RESET_MERGE_REQUESTS](mockedState, 'created'); - expect(mockedState.mergeRequests).toEqual([]); + expect(mockedState.created.mergeRequests).toEqual([]); }); }); }); -- cgit v1.2.1 From c604c43782c5f2ac915f934d566f0502ae41f6d4 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 05:29:52 -0500 Subject: fix broken specs --- .../projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb index d15391911c1..cb1b9e6f5fb 100644 --- a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb +++ b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb @@ -12,7 +12,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'shows warning message' do render - expect(rendered).to have_css('.settings-message') + expect(rendered).to have_css('.auto-devops-warning-message') expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name and a') expect(rendered).to have_link('Kubernetes cluster') end @@ -26,7 +26,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'shows warning message' do render - expect(rendered).to have_css('.settings-message') + expect(rendered).to have_css('.auto-devops-warning-message') expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a') expect(rendered).to have_link('Kubernetes cluster') end @@ -42,7 +42,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'shows warning message' do render - expect(rendered).to have_css('.settings-message') + expect(rendered).to have_css('.auto-devops-warning-message') expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name to work correctly.') end end @@ -55,7 +55,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do it 'does not show warning message' do render - expect(rendered).not_to have_css('.settings-message') + expect(rendered).not_to have_css('.auto-devops-warning-message') end end end -- cgit v1.2.1 From 56efb9ee92cb96c6a378ae5d2f6510c7a870d896 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 6 Jun 2018 11:02:47 +0000 Subject: Adjust monitoring graphs to support widgets in EE --- spec/javascripts/monitoring/graph_spec.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js index 220228e5c08..a46a387a534 100644 --- a/spec/javascripts/monitoring/graph_spec.js +++ b/spec/javascripts/monitoring/graph_spec.js @@ -18,9 +18,7 @@ const createComponent = propsData => { }).$mount(); }; -const convertedMetrics = convertDatesMultipleSeries( - singleRowMetricsMultipleSeries, -); +const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); describe('Graph', () => { beforeEach(() => { @@ -36,7 +34,7 @@ describe('Graph', () => { projectPath, }); - expect(component.$el.querySelector('.text-center').innerText.trim()).toBe( + expect(component.$el.querySelector('.prometheus-graph-title').innerText.trim()).toBe( component.graphData.title, ); }); @@ -52,9 +50,7 @@ describe('Graph', () => { }); const transformedHeight = `${component.graphHeight - 100}`; - expect(component.axisTransform.indexOf(transformedHeight)).not.toEqual( - -1, - ); + expect(component.axisTransform.indexOf(transformedHeight)).not.toEqual(-1); }); it('outerViewBox gets a width and height property based on the DOM size of the element', () => { -- cgit v1.2.1 From dfb0d45ddb0747f5b72e7188d930737d57dabc4c Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Wed, 6 Jun 2018 20:18:32 +0900 Subject: Rename worker to ArchiveTracesCronWorker --- spec/workers/ci/archive_traces_cron_worker_spec.rb | 59 ++++++++++++++++++++++ .../ci/rescue_stale_live_trace_worker_spec.rb | 59 ---------------------- 2 files changed, 59 insertions(+), 59 deletions(-) create mode 100644 spec/workers/ci/archive_traces_cron_worker_spec.rb delete mode 100644 spec/workers/ci/rescue_stale_live_trace_worker_spec.rb (limited to 'spec') diff --git a/spec/workers/ci/archive_traces_cron_worker_spec.rb b/spec/workers/ci/archive_traces_cron_worker_spec.rb new file mode 100644 index 00000000000..9af51b7d4d8 --- /dev/null +++ b/spec/workers/ci/archive_traces_cron_worker_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Ci::ArchiveTracesCronWorker do + subject { described_class.new.perform } + + before do + stub_feature_flags(ci_enable_live_trace: true) + end + + shared_examples_for 'archives trace' do + it do + subject + + build.reload + expect(build.job_artifacts_trace).to be_exist + end + end + + shared_examples_for 'does not archive trace' do + it do + subject + + build.reload + expect(build.job_artifacts_trace).to be_nil + end + end + + context 'when a job was succeeded' do + let!(:build) { create(:ci_build, :success, :trace_live) } + + it_behaves_like 'archives trace' + + context 'when archive raised an exception' do + let!(:build) { create(:ci_build, :success, :trace_artifact, :trace_live) } + let!(:build2) { create(:ci_build, :success, :trace_live) } + + it 'archives valid targets' do + expect(Rails.logger).to receive(:error).with("Failed to archive stale live trace. id: #{build.id} message: Already archived") + + subject + + build2.reload + expect(build2.job_artifacts_trace).to be_exist + end + end + end + + context 'when a job was cancelled' do + let!(:build) { create(:ci_build, :canceled, :trace_live) } + + it_behaves_like 'archives trace' + end + + context 'when a job is running' do + let!(:build) { create(:ci_build, :running, :trace_live) } + + it_behaves_like 'does not archive trace' + end +end diff --git a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb b/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb deleted file mode 100644 index 0e3fbe2d3a9..00000000000 --- a/spec/workers/ci/rescue_stale_live_trace_worker_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'spec_helper' - -describe Ci::RescueStaleLiveTraceWorker do - subject { described_class.new.perform } - - before do - stub_feature_flags(ci_enable_live_trace: true) - end - - shared_examples_for 'archives trace' do - it do - subject - - build.reload - expect(build.job_artifacts_trace).to be_exist - end - end - - shared_examples_for 'does not archive trace' do - it do - subject - - build.reload - expect(build.job_artifacts_trace).to be_nil - end - end - - context 'when a job was succeeded' do - let!(:build) { create(:ci_build, :success, :trace_live) } - - it_behaves_like 'archives trace' - - context 'when archive raised an exception' do - let!(:build) { create(:ci_build, :success, :trace_artifact, :trace_live) } - let!(:build2) { create(:ci_build, :success, :trace_live) } - - it 'archives valid targets' do - expect(Rails.logger).to receive(:error).with("Failed to archive stale live trace. id: #{build.id} message: Already archived") - - subject - - build2.reload - expect(build2.job_artifacts_trace).to be_exist - end - end - end - - context 'when a job was cancelled' do - let!(:build) { create(:ci_build, :canceled, :trace_live) } - - it_behaves_like 'archives trace' - end - - context 'when a job is running' do - let!(:build) { create(:ci_build, :running, :trace_live) } - - it_behaves_like 'does not archive trace' - end -end -- cgit v1.2.1 From 3b78b2d327ec00d201b74e74a81a9edb65a27a4c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 6 Jun 2018 12:40:21 +0100 Subject: component specs --- .../ide/components/merge_requests/dropdown_spec.js | 47 ++++++++ .../ide/components/merge_requests/item_spec.js | 61 ++++++++++ .../ide/components/merge_requests/list_spec.js | 126 +++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 spec/javascripts/ide/components/merge_requests/dropdown_spec.js create mode 100644 spec/javascripts/ide/components/merge_requests/item_spec.js create mode 100644 spec/javascripts/ide/components/merge_requests/list_spec.js (limited to 'spec') diff --git a/spec/javascripts/ide/components/merge_requests/dropdown_spec.js b/spec/javascripts/ide/components/merge_requests/dropdown_spec.js new file mode 100644 index 00000000000..74884c9a362 --- /dev/null +++ b/spec/javascripts/ide/components/merge_requests/dropdown_spec.js @@ -0,0 +1,47 @@ +import Vue from 'vue'; +import { createStore } from '~/ide/stores'; +import Dropdown from '~/ide/components/merge_requests/dropdown.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { mergeRequests } from '../../mock_data'; + +describe('IDE merge requests dropdown', () => { + const Component = Vue.extend(Dropdown); + let vm; + + beforeEach(() => { + const store = createStore(); + + vm = createComponentWithStore(Component, store, { show: false }).$mount(); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('does not render tabs when show is false', () => { + expect(vm.$el.querySelector('.nav-links')).toBe(null); + }); + + describe('when show is true', () => { + beforeEach(done => { + vm.show = true; + vm.$store.state.mergeRequests.assigned.mergeRequests.push(mergeRequests[0]); + + vm.$nextTick(done); + }); + + it('renders tabs', () => { + expect(vm.$el.querySelector('.nav-links')).not.toBe(null); + }); + + it('renders count for assigned & created data', () => { + expect(vm.$el.querySelector('.nav-links a').textContent).toContain('Created by me'); + expect(vm.$el.querySelector('.nav-links a .badge').textContent).toContain('0'); + + expect(vm.$el.querySelectorAll('.nav-links a')[1].textContent).toContain('Assigned to me'); + expect( + vm.$el.querySelectorAll('.nav-links a')[1].querySelector('.badge').textContent, + ).toContain('1'); + }); + }); +}); diff --git a/spec/javascripts/ide/components/merge_requests/item_spec.js b/spec/javascripts/ide/components/merge_requests/item_spec.js new file mode 100644 index 00000000000..51c4cddef2f --- /dev/null +++ b/spec/javascripts/ide/components/merge_requests/item_spec.js @@ -0,0 +1,61 @@ +import Vue from 'vue'; +import Item from '~/ide/components/merge_requests/item.vue'; +import mountCompontent from '../../../helpers/vue_mount_component_helper'; + +describe('IDE merge request item', () => { + const Component = Vue.extend(Item); + let vm; + + beforeEach(() => { + vm = mountCompontent(Component, { + item: { + iid: 1, + projectPathWithNamespace: 'gitlab-org/gitlab-ce', + title: 'Merge request title', + }, + currentId: '1', + currentProjectId: 'gitlab-org/gitlab-ce', + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders merge requests data', () => { + expect(vm.$el.textContent).toContain('Merge request title'); + expect(vm.$el.textContent).toContain('gitlab-org/gitlab-ce!1'); + }); + + it('renders icon if ID matches currentId', () => { + expect(vm.$el.querySelector('.ic-mobile-issue-close')).not.toBe(null); + }); + + it('does not render icon if ID does not match currentId', done => { + vm.currentId = '2'; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null); + + done(); + }); + }); + + it('does not render icon if project ID does not match', done => { + vm.currentProjectId = 'test/test'; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null); + + done(); + }); + }); + + it('emits click event on click', () => { + spyOn(vm, '$emit'); + + vm.$el.click(); + + expect(vm.$emit).toHaveBeenCalledWith('click', vm.item); + }); +}); diff --git a/spec/javascripts/ide/components/merge_requests/list_spec.js b/spec/javascripts/ide/components/merge_requests/list_spec.js new file mode 100644 index 00000000000..f4b393778dc --- /dev/null +++ b/spec/javascripts/ide/components/merge_requests/list_spec.js @@ -0,0 +1,126 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import List from '~/ide/components/merge_requests/list.vue'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { mergeRequests } from '../../mock_data'; +import { resetStore } from '../../helpers'; + +describe('IDE merge requests list', () => { + const Component = Vue.extend(List); + let vm; + + beforeEach(() => { + vm = createComponentWithStore(Component, store, { + type: 'created', + emptyText: 'empty text', + }); + + spyOn(vm, 'fetchMergeRequests'); + + vm.$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); + + it('calls fetch on mounted', () => { + expect(vm.fetchMergeRequests).toHaveBeenCalledWith({ + type: 'created', + search: '', + }); + }); + + it('renders loading icon', done => { + vm.$store.state.mergeRequests.created.isLoading = true; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.loading-container')).not.toBe(null); + + done(); + }); + }); + + it('renders empty text when no merge requests exist', () => { + expect(vm.$el.textContent).toContain('empty text'); + }); + + it('renders no search results text when search is not empty', done => { + vm.search = 'testing'; + + vm.$nextTick(() => { + expect(vm.$el.textContent).toContain('No merge requests found'); + + done(); + }); + }); + + describe('with merge requests', () => { + beforeEach(done => { + vm.$store.state.mergeRequests.created.mergeRequests.push({ + ...mergeRequests[0], + projectPathWithNamespace: 'gitlab-org/gitlab-ce', + }); + + vm.$nextTick(done); + }); + + it('renders list', () => { + expect(vm.$el.querySelectorAll('li').length).toBe(1); + expect(vm.$el.querySelector('li').textContent).toContain(mergeRequests[0].title); + }); + + it('calls openMergeRequest when clicking merge request', done => { + spyOn(vm, 'openMergeRequest'); + vm.$el.querySelector('li button').click(); + + vm.$nextTick(() => { + expect(vm.openMergeRequest).toHaveBeenCalledWith({ + projectPath: 'gitlab-org/gitlab-ce', + id: 1, + }); + + done(); + }); + }); + }); + + describe('focusSearch', () => { + it('focuses search input when loading is false', done => { + spyOn(vm.$refs.searchInput, 'focus'); + + vm.$store.state.mergeRequests.created.isLoading = false; + vm.focusSearch(); + + vm.$nextTick(() => { + expect(vm.$refs.searchInput.focus).toHaveBeenCalled(); + + done(); + }); + }); + }); + + describe('searchMergeRequests', () => { + beforeEach(() => { + spyOn(vm, 'loadMergeRequests'); + + jasmine.clock().install(); + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('calls loadMergeRequests on input in search field', () => { + const event = new Event('input'); + + vm.$el.querySelector('input').dispatchEvent(event); + + jasmine.clock().tick(300); + + expect(vm.loadMergeRequests).toHaveBeenCalled(); + }); + }); +}); -- cgit v1.2.1 From cfcc7043fe614cbc5fc82f3021a0c4cd8519e24b Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Tue, 22 May 2018 19:54:50 +0800 Subject: =?UTF-8?q?Rename=20=E2=80=9CDevelopers=20+=20Masters=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/features/protected_branches_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 0c28a853b54..4c0f9971425 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -72,7 +72,7 @@ feature 'Protected Branches', :js do click_link 'No one' find(".js-allowed-to-push").click wait_for_requests - click_link 'Developers + Masters' + click_link 'Developers + Maintainers' end visit project_protected_branches_path(project) @@ -82,7 +82,7 @@ feature 'Protected Branches', :js do expect(page.find(".dropdown-toggle-text")).to have_content("No one") end page.within(".js-allowed-to-push") do - expect(page.find(".dropdown-toggle-text")).to have_content("Developers + Masters") + expect(page.find(".dropdown-toggle-text")).to have_content("Developers + Maintainers") end end end -- cgit v1.2.1 From 7c965a24f51ca93196c27880843a6d187ea063cc Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Tue, 22 May 2018 19:55:07 +0800 Subject: Fix spec --- spec/features/projects/settings/user_manages_group_links_spec.rb | 2 +- spec/features/projects/settings/user_manages_project_members_spec.rb | 2 +- spec/javascripts/notes/components/note_actions_spec.js | 4 ++-- spec/models/project_team_spec.rb | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/settings/user_manages_group_links_spec.rb b/spec/features/projects/settings/user_manages_group_links_spec.rb index fdf42797091..92ce2ca83c7 100644 --- a/spec/features/projects/settings/user_manages_group_links_spec.rb +++ b/spec/features/projects/settings/user_manages_group_links_spec.rb @@ -30,7 +30,7 @@ describe 'Projects > Settings > User manages group links' do click_link('Share with group') select2(group_market.id, from: '#link_group_id') - select('Master', from: 'link_group_access') + select('Maintainer', from: 'link_group_access') click_button('Share') diff --git a/spec/features/projects/settings/user_manages_project_members_spec.rb b/spec/features/projects/settings/user_manages_project_members_spec.rb index 8af95522165..d3003753ae6 100644 --- a/spec/features/projects/settings/user_manages_project_members_spec.rb +++ b/spec/features/projects/settings/user_manages_project_members_spec.rb @@ -62,7 +62,7 @@ describe 'Projects > Settings > User manages project members' do page.within('.project-members-groups') do expect(page).to have_content('OpenSource') - expect(first('.group_member')).to have_content('Master') + expect(first('.group_member')).to have_content('Maintainer') end end end diff --git a/spec/javascripts/notes/components/note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js index 1dfe890e05e..c9e549d2096 100644 --- a/spec/javascripts/notes/components/note_actions_spec.js +++ b/spec/javascripts/notes/components/note_actions_spec.js @@ -20,7 +20,7 @@ describe('issue_note_actions component', () => { beforeEach(() => { props = { - accessLevel: 'Master', + accessLevel: 'Maintainer', authorId: 26, canDelete: true, canEdit: true, @@ -67,7 +67,7 @@ describe('issue_note_actions component', () => { beforeEach(() => { store.dispatch('setUserData', {}); props = { - accessLevel: 'Master', + accessLevel: 'Maintainer', authorId: 26, canDelete: false, canEdit: false, diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index e07c522800a..9978f3e9566 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -179,14 +179,14 @@ describe ProjectTeam do end describe "#human_max_access" do - it 'returns Master role' do + it 'returns Maintainer role' do user = create(:user) group = create(:group) project = create(:project, namespace: group) group.add_master(user) - expect(project.team.human_max_access(user.id)).to eq 'Master' + expect(project.team.human_max_access(user.id)).to eq 'Maintainer' end it 'returns Owner role' do -- cgit v1.2.1 From a6c15c5b9750b3b6b16694974eab868a21b8683a Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Mon, 4 Jun 2018 10:25:38 +0900 Subject: change wording --- spec/lib/gitlab/checks/change_access_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb index 48e9902027c..1cb8143a9e9 100644 --- a/spec/lib/gitlab/checks/change_access_spec.rb +++ b/spec/lib/gitlab/checks/change_access_spec.rb @@ -52,7 +52,7 @@ describe Gitlab::Checks::ChangeAccess do context 'with protected tag' do let!(:protected_tag) { create(:protected_tag, project: project, name: 'v*') } - context 'as master' do + context 'as maintainer' do before do project.add_master(user) end @@ -138,7 +138,7 @@ describe Gitlab::Checks::ChangeAccess do context 'if the user is not allowed to delete protected branches' do it 'raises an error' do - expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to delete protected branches from this project. Only a project master or owner can delete a protected branch.') + expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to delete protected branches from this project. Only a project maintainer or owner can delete a protected branch.') end end -- cgit v1.2.1 From 748f8fef457f2795ab2592bf6041055cf613bb10 Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Mon, 4 Jun 2018 10:32:57 +0900 Subject: group runners --- spec/features/runners_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 9ce7d538004..fe0b03a7e00 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -184,7 +184,7 @@ feature 'Runners' do given(:group) { create :group } - context 'as project and group master' do + context 'as project and group maintainer' do background do group.add_master(user) end @@ -197,13 +197,13 @@ feature 'Runners' do expect(page).to have_content 'This group does not provide any group Runners yet' - expect(page).to have_content 'Group masters can register group runners in the Group CI/CD settings' - expect(page).not_to have_content 'Ask your group master to setup a group Runner' + expect(page).to have_content 'Group maintainers can register group runners in the Group CI/CD settings' + expect(page).not_to have_content 'Ask your group maintainer to setup a group Runner' end end end - context 'as project master' do + context 'as project maintainer' do context 'project without a group' do given(:project) { create :project } @@ -223,8 +223,8 @@ feature 'Runners' do expect(page).to have_content 'This group does not provide any group Runners yet.' - expect(page).not_to have_content 'Group masters can register group runners in the Group CI/CD settings' - expect(page).to have_content 'Ask your group master to setup a group Runner.' + expect(page).not_to have_content 'Group maintainers can register group runners in the Group CI/CD settings' + expect(page).to have_content 'Ask your group maintainer to setup a group Runner.' end end -- cgit v1.2.1 From 9fa70462296c85deb48574bf3e3ceed0cb2dd493 Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Mon, 4 Jun 2018 12:50:28 +0900 Subject: fix lock spec --- spec/services/lfs/unlock_file_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/services/lfs/unlock_file_service_spec.rb b/spec/services/lfs/unlock_file_service_spec.rb index 4bea112b9c6..948401d7bdc 100644 --- a/spec/services/lfs/unlock_file_service_spec.rb +++ b/spec/services/lfs/unlock_file_service_spec.rb @@ -80,12 +80,12 @@ describe Lfs::UnlockFileService do result = subject.execute expect(result[:status]).to eq(:error) - expect(result[:message]).to match(/You must have master access/) + expect(result[:message]).to match(/You must have maintainer access/) expect(result[:http_status]).to eq(403) end end - context 'by a master user' do + context 'by a maintainer user' do let(:current_user) { master } let(:params) do { id: lock.id, -- cgit v1.2.1 From 51a9f71118e9cd4bec50b6a5872d035f219e4eaf Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Wed, 6 Jun 2018 15:21:13 +0200 Subject: Add a custom storage config instead of overwriting Otherwise the ApplicationSetting that is currently stored would be invalid as it specifies a storage that is not configured --- spec/requests/api/settings_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index aead8978dd4..57adc3ca7a6 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -35,7 +35,9 @@ describe API::Settings, 'Settings' do context "custom repository storage type set in the config" do before do - storages = { 'custom' => 'tmp/tests/custom_repositories' } + # Add a possible storage to the config + storages = Gitlab.config.repositories.storages + .merge({ 'custom' => 'tmp/tests/custom_repositories' }) allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) end -- cgit v1.2.1 From cc9468e4fa276cef5673750464889f2d2791a9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Carlb=C3=A4cker?= Date: Wed, 6 Jun 2018 14:28:03 +0000 Subject: Move GC/Repack to OptOut --- spec/workers/git_garbage_collect_worker_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 74539a7e493..f44b4edc305 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -104,7 +104,7 @@ describe GitGarbageCollectWorker do it_should_behave_like 'flushing ref caches', true end - context "with Gitaly turned off", :skip_gitaly_mock do + context "with Gitaly turned off", :disable_gitaly do it_should_behave_like 'flushing ref caches', false end -- cgit v1.2.1 From e8f49b4bee8d803953b852685889a2912609ae84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Wed, 6 Jun 2018 16:42:18 +0000 Subject: Support LFS objects when creating a project by import --- spec/lib/gitlab/git/rev_list_spec.rb | 2 +- .../importer/lfs_object_importer_spec.rb | 23 ++++ .../importer/lfs_objects_importer_spec.rb | 94 +++++++++++++ .../importer/repository_importer_spec.rb | 3 +- spec/lib/gitlab/import_sources_spec.rb | 19 +++ spec/services/projects/import_service_spec.rb | 72 +++++++++- .../lfs_download_link_list_service_spec.rb | 102 ++++++++++++++ .../lfs_pointers/lfs_download_service_spec.rb | 69 ++++++++++ .../lfs_pointers/lfs_import_service_spec.rb | 146 +++++++++++++++++++++ .../projects/lfs_pointers/lfs_link_service_spec.rb | 33 +++++ .../stage/import_lfs_objects_worker_spec.rb | 28 ++++ .../stage/import_notes_worker_spec.rb | 2 +- 12 files changed, 589 insertions(+), 4 deletions(-) create mode 100644 spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb create mode 100644 spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb create mode 100644 spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb create mode 100644 spec/services/projects/lfs_pointers/lfs_download_service_spec.rb create mode 100644 spec/services/projects/lfs_pointers/lfs_import_service_spec.rb create mode 100644 spec/services/projects/lfs_pointers/lfs_link_service_spec.rb create mode 100644 spec/workers/gitlab/github_import/stage/import_lfs_objects_worker_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 95dc47e2a00..70e90659b0f 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -88,7 +88,7 @@ describe Gitlab::Git::RevList do context '#all_objects' do it 'fetches list of all pushed objects using rev-list' do - stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2") + stub_popen_rev_list('--all', '--objects', '--filter=blob:limit=200', output: "sha1\nsha2") expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2]) end diff --git a/spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb b/spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb new file mode 100644 index 00000000000..4857f2afbe2 --- /dev/null +++ b/spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::Importer::LfsObjectImporter do + let(:project) { create(:project) } + let(:download_link) { "http://www.gitlab.com/lfs_objects/oid" } + + let(:github_lfs_object) do + Gitlab::GithubImport::Representation::LfsObject.new( + oid: 'oid', download_link: download_link + ) + end + + let(:importer) { described_class.new(github_lfs_object, project, nil) } + + describe '#execute' do + it 'calls the LfsDownloadService with the lfs object attributes' do + expect_any_instance_of(Projects::LfsPointers::LfsDownloadService) + .to receive(:execute).with('oid', download_link) + + importer.execute + end + end +end diff --git a/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb new file mode 100644 index 00000000000..5f5c6b803c0 --- /dev/null +++ b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb @@ -0,0 +1,94 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::Importer::LfsObjectsImporter do + let(:project) { double(:project, id: 4, import_source: 'foo/bar') } + let(:client) { double(:client) } + let(:download_link) { "http://www.gitlab.com/lfs_objects/oid" } + + let(:github_lfs_object) { ['oid', download_link] } + + describe '#parallel?' do + it 'returns true when running in parallel mode' do + importer = described_class.new(project, client) + expect(importer).to be_parallel + end + + it 'returns false when running in sequential mode' do + importer = described_class.new(project, client, parallel: false) + expect(importer).not_to be_parallel + end + end + + describe '#execute' do + context 'when running in parallel mode' do + it 'imports lfs objects in parallel' do + importer = described_class.new(project, client) + + expect(importer).to receive(:parallel_import) + + importer.execute + end + end + + context 'when running in sequential mode' do + it 'imports lfs objects in sequence' do + importer = described_class.new(project, client, parallel: false) + + expect(importer).to receive(:sequential_import) + + importer.execute + end + end + end + + describe '#sequential_import' do + it 'imports each lfs object in sequence' do + importer = described_class.new(project, client, parallel: false) + lfs_object_importer = double(:lfs_object_importer) + + allow(importer) + .to receive(:each_object_to_import) + .and_yield(['oid', download_link]) + + expect(Gitlab::GithubImport::Importer::LfsObjectImporter) + .to receive(:new) + .with( + an_instance_of(Gitlab::GithubImport::Representation::LfsObject), + project, + client + ) + .and_return(lfs_object_importer) + + expect(lfs_object_importer).to receive(:execute) + + importer.sequential_import + end + end + + describe '#parallel_import' do + it 'imports each lfs object in parallel' do + importer = described_class.new(project, client) + + allow(importer) + .to receive(:each_object_to_import) + .and_yield(github_lfs_object) + + expect(Gitlab::GithubImport::ImportLfsObjectWorker) + .to receive(:perform_async) + .with(project.id, an_instance_of(Hash), an_instance_of(String)) + + waiter = importer.parallel_import + + expect(waiter).to be_an_instance_of(Gitlab::JobWaiter) + expect(waiter.jobs_remaining).to eq(1) + end + end + + describe '#collection_options' do + it 'returns an empty Hash' do + importer = described_class.new(project, client) + + expect(importer.collection_options).to eq({}) + end + end +end diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb index cc9e4b67e72..d8f01dcb76b 100644 --- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb @@ -14,7 +14,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do disk_path: 'foo', repository: repository, create_wiki: true, - import_state: import_state + import_state: import_state, + lfs_enabled?: true ) end diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb index f2fa315e3ec..10341486512 100644 --- a/spec/lib/gitlab/import_sources_spec.rb +++ b/spec/lib/gitlab/import_sources_spec.rb @@ -91,4 +91,23 @@ describe Gitlab::ImportSources do end end end + + describe 'imports_repository? checker' do + let(:allowed_importers) { %w[github gitlab_project] } + + it 'fails if any importer other than the allowed ones implements this method' do + current_importers = described_class.values.select { |kind| described_class.importer(kind).try(:imports_repository?) } + not_allowed_importers = current_importers - allowed_importers + + expect(not_allowed_importers).to be_empty, failure_message(not_allowed_importers) + end + + def failure_message(importers_class_names) + <<-MSG + It looks like the #{importers_class_names.join(', ')} importers implements its own way to import the repository. + That means that the lfs object download must be handled for each of them. You can use 'LfsImportService' and + 'LfsDownloadService' to implement it. After that, add the importer name to the list of allowed importers in this spec. + MSG + end + end end diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index 30c89ebd821..b3815045792 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -3,9 +3,17 @@ require 'spec_helper' describe Projects::ImportService do let!(:project) { create(:project) } let(:user) { project.creator } + let(:import_url) { 'http://www.gitlab.com/demo/repo.git' } + let(:oid_download_links) { { 'oid1' => "#{import_url}/gitlab-lfs/objects/oid1", 'oid2' => "#{import_url}/gitlab-lfs/objects/oid2" } } subject { described_class.new(project, user) } + before do + allow(project).to receive(:lfs_enabled?).and_return(true) + allow_any_instance_of(Projects::LfsPointers::LfsDownloadService).to receive(:execute) + allow_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(oid_download_links) + end + describe '#async?' do it 'returns true for an asynchronous importer' do importer_class = double(:importer, async?: true) @@ -63,6 +71,15 @@ describe Projects::ImportService do expect(result[:status]).to eq :error expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created." end + + context 'when repository creation succeeds' do + it 'does not download lfs files' do + expect_any_instance_of(Projects::LfsPointers::LfsImportService).not_to receive(:execute) + expect_any_instance_of(Projects::LfsPointers::LfsDownloadService).not_to receive(:execute) + + subject.execute + end + end end context 'with known url' do @@ -91,6 +108,15 @@ describe Projects::ImportService do expect(result[:status]).to eq :error end + + context 'when repository import scheduled' do + it 'does not download lfs objects' do + expect_any_instance_of(Projects::LfsPointers::LfsImportService).not_to receive(:execute) + expect_any_instance_of(Projects::LfsPointers::LfsDownloadService).not_to receive(:execute) + + subject.execute + end + end end context 'with a non Github repository' do @@ -99,9 +125,10 @@ describe Projects::ImportService do project.import_type = 'bitbucket' end - it 'succeeds if repository import is successfully' do + it 'succeeds if repository import is successfull' do expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_return(true) expect_any_instance_of(Gitlab::BitbucketImport::Importer).to receive(:execute).and_return(true) + expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return({}) result = subject.execute @@ -116,6 +143,29 @@ describe Projects::ImportService do expect(result[:status]).to eq :error expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository" end + + context 'when repository import scheduled' do + before do + allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_return(true) + allow(subject).to receive(:import_data) + end + + it 'downloads lfs objects if lfs_enabled is enabled for project' do + allow(project).to receive(:lfs_enabled?).and_return(true) + expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(oid_download_links) + expect_any_instance_of(Projects::LfsPointers::LfsDownloadService).to receive(:execute).twice + + subject.execute + end + + it 'does not download lfs objects if lfs_enabled is not enabled for project' do + allow(project).to receive(:lfs_enabled?).and_return(false) + expect_any_instance_of(Projects::LfsPointers::LfsImportService).not_to receive(:execute) + expect_any_instance_of(Projects::LfsPointers::LfsDownloadService).not_to receive(:execute) + + subject.execute + end + end end end @@ -147,6 +197,26 @@ describe Projects::ImportService do expect(result[:status]).to eq :error end + + context 'when importer' do + it 'has a custom repository importer it does not download lfs objects' do + allow(Gitlab::GithubImport::ParallelImporter).to receive(:imports_repository?).and_return(true) + + expect_any_instance_of(Projects::LfsPointers::LfsImportService).not_to receive(:execute) + expect_any_instance_of(Projects::LfsPointers::LfsDownloadService).not_to receive(:execute) + + subject.execute + end + + it 'does not have a custom repository importer downloads lfs objects' do + allow(Gitlab::GithubImport::ParallelImporter).to receive(:imports_repository?).and_return(false) + + expect_any_instance_of(Projects::LfsPointers::LfsImportService).to receive(:execute).and_return(oid_download_links) + expect_any_instance_of(Projects::LfsPointers::LfsDownloadService).to receive(:execute) + + subject.execute + end + end end context 'with blocked import_URL' do diff --git a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb new file mode 100644 index 00000000000..d7a2829d5f8 --- /dev/null +++ b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe Projects::LfsPointers::LfsDownloadLinkListService do + let(:import_url) { 'http://www.gitlab.com/demo/repo.git' } + let(:lfs_endpoint) { "#{import_url}/info/lfs/objects/batch" } + let!(:project) { create(:project, import_url: import_url) } + let(:new_oids) { { 'oid1' => 123, 'oid2' => 125 } } + let(:remote_uri) { URI.parse(lfs_endpoint) } + + let(:objects_response) do + body = new_oids.map do |oid, size| + { + 'oid' => oid, + 'size' => size, + 'actions' => { + 'download' => { 'href' => "#{import_url}/gitlab-lfs/objects/#{oid}" } + } + } + end + + Struct.new(:success?, :objects).new(true, body) + end + + let(:invalid_object_response) do + [ + 'oid' => 'whatever', + 'size' => 123 + ] + end + + subject { described_class.new(project, remote_uri: remote_uri) } + + before do + allow(project).to receive(:lfs_enabled?).and_return(true) + allow(Gitlab::HTTP).to receive(:post).and_return(objects_response) + end + + describe '#execute' do + it 'retrieves each download link of every non existent lfs object' do + subject.execute(new_oids).each do |oid, link| + expect(link).to eq "#{import_url}/gitlab-lfs/objects/#{oid}" + end + end + + context 'credentials' do + context 'when the download link and the lfs_endpoint have the same host' do + context 'when lfs_endpoint has credentials' do + let(:import_url) { 'http://user:password@www.gitlab.com/demo/repo.git' } + + it 'adds credentials to the download_link' do + result = subject.execute(new_oids) + + result.each do |oid, link| + expect(link.starts_with?('http://user:password@')).to be_truthy + end + end + end + + context 'when lfs_endpoint does not have any credentials' do + it 'does not add any credentials' do + result = subject.execute(new_oids) + + result.each do |oid, link| + expect(link.starts_with?('http://user:password@')).to be_falsey + end + end + end + end + + context 'when the download link and the lfs_endpoint have different hosts' do + let(:import_url_with_credentials) { 'http://user:password@www.otherdomain.com/demo/repo.git' } + let(:lfs_endpoint) { "#{import_url_with_credentials}/info/lfs/objects/batch" } + + it 'downloads without any credentials' do + result = subject.execute(new_oids) + + result.each do |oid, link| + expect(link.starts_with?('http://user:password@')).to be_falsey + end + end + end + end + end + + describe '#get_download_links' do + it 'raise errorif request fails' do + allow(Gitlab::HTTP).to receive(:post).and_return(Struct.new(:success?, :message).new(false, 'Failed request')) + + expect { subject.send(:get_download_links, new_oids) }.to raise_error(described_class::DownloadLinksError) + end + end + + describe '#parse_response_links' do + it 'does not add oid entry if href not found' do + expect(Rails.logger).to receive(:error).with("Link for Lfs Object with oid whatever not found or invalid.") + + result = subject.send(:parse_response_links, invalid_object_response) + + expect(result).to be_empty + end + end +end diff --git a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb new file mode 100644 index 00000000000..6af5bfc7689 --- /dev/null +++ b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe Projects::LfsPointers::LfsDownloadService do + let(:project) { create(:project) } + let(:oid) { '9e548e25631dd9ce6b43afd6359ab76da2819d6a5b474e66118c7819e1d8b3e8' } + let(:download_link) { "http://gitlab.com/#{oid}" } + let(:lfs_content) do + <<~HEREDOC + whatever + HEREDOC + end + + subject { described_class.new(project) } + + before do + allow(project).to receive(:lfs_enabled?).and_return(true) + WebMock.stub_request(:get, download_link).to_return(body: lfs_content) + end + + describe '#execute' do + context 'when file download succeeds' do + it 'a new lfs object is created' do + expect { subject.execute(oid, download_link) }.to change { LfsObject.count }.from(0).to(1) + end + + it 'has the same oid' do + subject.execute(oid, download_link) + + expect(LfsObject.first.oid).to eq oid + end + + it 'stores the content' do + subject.execute(oid, download_link) + + expect(File.read(LfsObject.first.file.file.file)).to eq lfs_content + end + end + + context 'when file download fails' do + it 'no lfs object is created' do + expect { subject.execute(oid, download_link) }.to change { LfsObject.count } + end + end + + context 'when credentials present' do + let(:download_link_with_credentials) { "http://user:password@gitlab.com/#{oid}" } + + before do + WebMock.stub_request(:get, download_link).with(headers: { 'Authorization' => 'Basic dXNlcjpwYXNzd29yZA==' }).to_return(body: lfs_content) + end + + it 'the request adds authorization headers' do + subject.execute(oid, download_link_with_credentials) + end + end + + context 'when an lfs object with the same oid already exists' do + before do + create(:lfs_object, oid: 'oid') + end + + it 'does not download the file' do + expect(subject).not_to receive(:download_and_save_file) + + subject.execute('oid', download_link) + end + end + end +end diff --git a/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb new file mode 100644 index 00000000000..5a75fb38dec --- /dev/null +++ b/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb @@ -0,0 +1,146 @@ +require 'spec_helper' + +describe Projects::LfsPointers::LfsImportService do + let(:import_url) { 'http://www.gitlab.com/demo/repo.git' } + let(:default_endpoint) { "#{import_url}/info/lfs/objects/batch"} + let(:group) { create(:group, lfs_enabled: true)} + let!(:project) { create(:project, namespace: group, import_url: import_url, lfs_enabled: true) } + let!(:lfs_objects_project) { create_list(:lfs_objects_project, 2, project: project) } + let!(:existing_lfs_objects) { LfsObject.pluck(:oid, :size).to_h } + let(:oids) { { 'oid1' => 123, 'oid2' => 125 } } + let(:oid_download_links) { { 'oid1' => "#{import_url}/gitlab-lfs/objects/oid1", 'oid2' => "#{import_url}/gitlab-lfs/objects/oid2" } } + let(:all_oids) { existing_lfs_objects.merge(oids) } + let(:remote_uri) { URI.parse(lfs_endpoint) } + + subject { described_class.new(project) } + + before do + allow(project.repository).to receive(:lfsconfig_for).and_return(nil) + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + allow_any_instance_of(Projects::LfsPointers::LfsListService).to receive(:execute).and_return(all_oids) + end + + describe '#execute' do + context 'when no lfs pointer is linked' do + before do + allow_any_instance_of(Projects::LfsPointers::LfsLinkService).to receive(:execute).and_return([]) + allow_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).and_return(oid_download_links) + expect(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:new).with(project, remote_uri: URI.parse(default_endpoint)).and_call_original + end + + it 'retrieves all lfs pointers in the project repository' do + expect_any_instance_of(Projects::LfsPointers::LfsListService).to receive(:execute) + + subject.execute + end + + it 'links existent lfs objects to the project' do + expect_any_instance_of(Projects::LfsPointers::LfsLinkService).to receive(:execute) + + subject.execute + end + + it 'retrieves the download links of non existent objects' do + expect_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).with(all_oids) + + subject.execute + end + end + + context 'when some lfs objects are linked' do + before do + allow_any_instance_of(Projects::LfsPointers::LfsLinkService).to receive(:execute).and_return(existing_lfs_objects.keys) + allow_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).and_return(oid_download_links) + end + + it 'retrieves the download links of non existent objects' do + expect_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).with(oids) + + subject.execute + end + end + + context 'when all lfs objects are linked' do + before do + allow_any_instance_of(Projects::LfsPointers::LfsLinkService).to receive(:execute).and_return(all_oids.keys) + allow_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute) + end + + it 'retrieves no download links' do + expect_any_instance_of(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:execute).with({}).and_call_original + + expect(subject.execute).to be_empty + end + end + + context 'when lfsconfig file exists' do + before do + allow(project.repository).to receive(:lfsconfig_for).and_return("[lfs]\n\turl = #{lfs_endpoint}\n") + end + + context 'when url points to the same import url host' do + let(:lfs_endpoint) { "#{import_url}/different_endpoint" } + let(:service) { double } + + before do + allow(service).to receive(:execute) + end + it 'downloads lfs object using the new endpoint' do + expect(Projects::LfsPointers::LfsDownloadLinkListService).to receive(:new).with(project, remote_uri: remote_uri).and_return(service) + + subject.execute + end + + context 'when import url has credentials' do + let(:import_url) { 'http://user:password@www.gitlab.com/demo/repo.git'} + + it 'adds the credentials to the new endpoint' do + expect(Projects::LfsPointers::LfsDownloadLinkListService) + .to receive(:new).with(project, remote_uri: URI.parse("http://user:password@www.gitlab.com/demo/repo.git/different_endpoint")) + .and_return(service) + + subject.execute + end + + context 'when url has its own credentials' do + let(:lfs_endpoint) { "http://user1:password1@www.gitlab.com/demo/repo.git/different_endpoint" } + + it 'does not add the import url credentials' do + expect(Projects::LfsPointers::LfsDownloadLinkListService) + .to receive(:new).with(project, remote_uri: remote_uri) + .and_return(service) + + subject.execute + end + end + end + end + + context 'when url points to a third party service' do + let(:lfs_endpoint) { 'http://third_party_service.com/info/lfs/objects/' } + + it 'disables lfs from the project' do + expect(project.lfs_enabled?).to be_truthy + + subject.execute + + expect(project.lfs_enabled?).to be_falsey + end + + it 'does not download anything' do + expect_any_instance_of(Projects::LfsPointers::LfsListService).not_to receive(:execute) + + subject.execute + end + end + end + end + + describe '#default_endpoint_uri' do + let(:import_url) { 'http://www.gitlab.com/demo/repo' } + + it 'adds suffix .git if the url does not have it' do + expect(subject.send(:default_endpoint_uri).path).to match(/repo.git/) + end + end +end diff --git a/spec/services/projects/lfs_pointers/lfs_link_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_link_service_spec.rb new file mode 100644 index 00000000000..b7b153655db --- /dev/null +++ b/spec/services/projects/lfs_pointers/lfs_link_service_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Projects::LfsPointers::LfsLinkService do + let!(:project) { create(:project, lfs_enabled: true) } + let!(:lfs_objects_project) { create_list(:lfs_objects_project, 2, project: project) } + let(:new_oids) { { 'oid1' => 123, 'oid2' => 125 } } + let(:all_oids) { LfsObject.pluck(:oid, :size).to_h.merge(new_oids) } + let(:new_lfs_object) { create(:lfs_object) } + let(:new_oid_list) { all_oids.merge(new_lfs_object.oid => new_lfs_object.size) } + + subject { described_class.new(project) } + + before do + allow(project).to receive(:lfs_enabled?).and_return(true) + end + + describe '#execute' do + it 'links existing lfs objects to the project' do + expect(project.all_lfs_objects.count).to eq 2 + + linked = subject.execute(new_oid_list.keys) + + expect(project.all_lfs_objects.count).to eq 3 + expect(linked.size).to eq 3 + end + + it 'returns linked oids' do + linked = lfs_objects_project.map(&:lfs_object).map(&:oid) << new_lfs_object.oid + + expect(subject.execute(new_oid_list.keys)).to eq linked + end + end +end diff --git a/spec/workers/gitlab/github_import/stage/import_lfs_objects_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_lfs_objects_worker_spec.rb new file mode 100644 index 00000000000..b19884d7991 --- /dev/null +++ b/spec/workers/gitlab/github_import/stage/import_lfs_objects_worker_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::Stage::ImportLfsObjectsWorker do + let(:project) { create(:project) } + let(:worker) { described_class.new } + + describe '#import' do + it 'imports all the lfs objects' do + importer = double(:importer) + waiter = Gitlab::JobWaiter.new(2, '123') + + expect(Gitlab::GithubImport::Importer::LfsObjectsImporter) + .to receive(:new) + .with(project, nil) + .and_return(importer) + + expect(importer) + .to receive(:execute) + .and_return(waiter) + + expect(Gitlab::GithubImport::AdvanceStageWorker) + .to receive(:perform_async) + .with(project.id, { '123' => 2 }, :finish) + + worker.import(project) + end + end +end diff --git a/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb index 098d2d55386..94cff9e4e80 100644 --- a/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb +++ b/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::GithubImport::Stage::ImportNotesWorker do expect(Gitlab::GithubImport::AdvanceStageWorker) .to receive(:perform_async) - .with(project.id, { '123' => 2 }, :finish) + .with(project.id, { '123' => 2 }, :lfs_objects) worker.import(client, project) end -- cgit v1.2.1 From 78ae23045bae8a75775a11675afd5836fb8f318b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 6 Jun 2018 19:09:46 +0200 Subject: Reverse logic of manual and continuous deploy strategies --- spec/models/project_auto_devops_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 37cd1f571e5..040e76e28fc 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -69,11 +69,11 @@ describe ProjectAutoDevops do end end - context 'when deploy_strategy is continuous' do + context 'when deploy_strategy is manual' do let(:domain) { 'example.com' } before do - auto_devops.deploy_strategy = 'continuous' + auto_devops.deploy_strategy = 'manual' end it do -- cgit v1.2.1 From 86d39016a26f73e82437d38fcf723677b97c1c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Wed, 6 Jun 2018 21:07:31 +0200 Subject: Moving rev-list lfs options to Lfschanges --- spec/lib/gitlab/git/rev_list_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 70e90659b0f..95dc47e2a00 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -88,7 +88,7 @@ describe Gitlab::Git::RevList do context '#all_objects' do it 'fetches list of all pushed objects using rev-list' do - stub_popen_rev_list('--all', '--objects', '--filter=blob:limit=200', output: "sha1\nsha2") + stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2") expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2]) end -- cgit v1.2.1 From 5d3abdf9a7c0260c708a46e2fd232b0490940f80 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 6 Jun 2018 00:47:53 -0700 Subject: Log response body to production_json.log when a controller responds with a 422 status We have a number of import errors occurring with 422 errors, and it's hard to determine why they are happening. This change will surface the errors in the log lines. Relates to #47365 --- spec/controllers/application_controller_spec.rb | 58 +++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'spec') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 683c57c96f8..773bf25ed44 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require 'spec_helper' describe ApplicationController do @@ -478,6 +479,63 @@ describe ApplicationController do end end + describe '#append_info_to_payload' do + controller(described_class) do + attr_reader :last_payload + + def index + render text: 'authenticated' + end + + def append_info_to_payload(payload) + super + + @last_payload = payload + end + end + + it 'does not log errors with a 200 response' do + get :index + + expect(controller.last_payload.has_key?(:response)).to be_falsey + end + + context '422 errors' do + it 'logs a response with a string' do + response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload[:response]).to eq('Hello world') + end + + it 'logs a response with an array' do + body = ['I want', 'my hat back'] + response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload[:response]).to eq(body) + end + + it 'does not log a string with an empty body' do + response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload.has_key?(:response)).to be_falsey + end + + it 'does not log an HTML body' do + response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html') + allow(controller).to receive(:response).and_return(response) + get :index + + expect(controller.last_payload.has_key?(:response)).to be_falsey + end + end + end + describe '#access_denied' do controller(described_class) do def index -- cgit v1.2.1 From 854c9636ec6aabd8941b31f0f2aa4e89c9c072ce Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Wed, 30 May 2018 12:12:42 +0200 Subject: Enforce UTF-8 encoding on user input in LogrageWithTimestamp formatter and filter out file content from logs --- spec/requests/api/commits_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'spec') diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 8ad19e3f0f5..7e3277c4cab 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -247,6 +247,19 @@ describe API::Commits do ] } end + let!(:valid_utf8_c_params) do + { + branch: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'foo/bar/baz.txt', + content: 'puts 🦊' + } + ] + } + end it 'a new file in project repo' do post api(url, user), valid_c_params @@ -257,6 +270,15 @@ describe API::Commits do expect(json_response['committer_email']).to eq(user.email) end + it 'a new file with utf8 chars in project repo' do + post api(url, user), valid_utf8_c_params + + expect(response).to have_gitlab_http_status(201) + expect(json_response['title']).to eq(message) + expect(json_response['committer_name']).to eq(user.name) + expect(json_response['committer_email']).to eq(user.email) + end + it 'returns a 400 bad request if file exists' do post api(url, user), invalid_c_params -- cgit v1.2.1 From 38c2e480bfa180241e94e77c049b1f5256d83bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mica=C3=ABl=20Bergeron?= Date: Wed, 6 Jun 2018 16:45:42 -0400 Subject: shave off 30% of the query count --- .../object_storage/migrate_uploads_worker_spec.rb | 50 ++++++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'spec') diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb index aed62f97448..18da0c6d39a 100644 --- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb +++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb @@ -1,5 +1,7 @@ require 'spec_helper' +MIGRATION_QUERIES = 7 + describe ObjectStorage::MigrateUploadsWorker, :sidekiq do shared_context 'sanity_check! fails' do before do @@ -11,6 +13,13 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do let(:uploads) { Upload.all } let(:to_store) { ObjectStorage::Store::REMOTE } + def perform(uploads) + binding.pry + described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, to_store) + rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures + # swallow + end + shared_examples "uploads migration worker" do describe '.enqueue!' do def enqueue! @@ -69,12 +78,6 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do end describe '#perform' do - def perform - described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, to_store) - rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures - # swallow - end - shared_examples 'outputs correctly' do |success: 0, failures: 0| total = success + failures @@ -82,7 +85,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do it 'outputs the reports' do expect(Rails.logger).to receive(:info).with(%r{Migrated #{success}/#{total} files}) - perform + perform(uploads) end end @@ -90,7 +93,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do it 'outputs upload failures' do expect(Rails.logger).to receive(:warn).with(/Error .* I am a teapot/) - perform + perform(uploads) end end end @@ -98,7 +101,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do it_behaves_like 'outputs correctly', success: 10 it 'migrates files' do - perform + perform(uploads) expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0) end @@ -123,6 +126,18 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do end it_behaves_like "uploads migration worker" + + describe "limits N+1 queries" do + let!(:projects) { create_list(:project, 10, :with_avatar) } + + it do + query_count = ActiveRecord::QueryRecorder.new { perform(uploads) } + + more_projects = create_list(:project, 100, :with_avatar) + expected_queries_per_migration = MIGRATION_QUERIES * more_projects.count + expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration) + end + end end context "for FileUploader" do @@ -140,5 +155,22 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do end it_behaves_like "uploads migration worker" + + describe "limits N+1 queries" do + let!(:projects) { create_list(:project, 10) } + + it do + query_count = ActiveRecord::QueryRecorder.new { perform(uploads) } + + more_projects = create_list(:project, 100) + more_projects.map do |project| + uploader = FileUploader.new(project) + uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt')) + end + expected_queries_per_migration = MIGRATION_QUERIES * more_projects.count + + expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration) + end + end end end -- cgit v1.2.1 From 164dbc6b77558e29fd9a1553048d314457dc6990 Mon Sep 17 00:00:00 2001 From: Annabel Gray Date: Thu, 7 Jun 2018 07:37:02 +0000 Subject: Resolve "Introduce new navigation themes in GitLab 11.0" --- spec/helpers/preferences_helper_spec.rb | 6 +++--- spec/lib/gitlab/themes_spec.rb | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index c9d2ec8a4ae..9940656fb68 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -31,9 +31,9 @@ describe PreferencesHelper do describe '#user_application_theme' do context 'with a user' do it "returns user's theme's css_class" do - stub_user(theme_id: 3) + stub_user(theme_id: 10) - expect(helper.user_application_theme).to eq 'ui_light' + expect(helper.user_application_theme).to eq 'ui-light' end it 'returns the default when id is invalid' do @@ -41,7 +41,7 @@ describe PreferencesHelper do allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(1) - expect(helper.user_application_theme).to eq 'ui_indigo' + expect(helper.user_application_theme).to eq 'ui-indigo' end end diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb index ecacea6bb35..af2f4568017 100644 --- a/spec/lib/gitlab/themes_spec.rb +++ b/spec/lib/gitlab/themes_spec.rb @@ -5,16 +5,16 @@ describe Gitlab::Themes, lib: true do it 'returns a space-separated list of class names' do css = described_class.body_classes - expect(css).to include('ui_indigo') - expect(css).to include(' ui_dark ') - expect(css).to include(' ui_blue') + expect(css).to include('ui-indigo') + expect(css).to include('ui-dark ') + expect(css).to include('ui-blue') end end describe '.by_id' do it 'returns a Theme by its ID' do expect(described_class.by_id(1).name).to eq 'Indigo' - expect(described_class.by_id(3).name).to eq 'Light' + expect(described_class.by_id(10).name).to eq 'Light' end end -- cgit v1.2.1 From 0206476ae2a2d659fd0fb42338050253a9a91439 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 7 Jun 2018 12:14:27 +0100 Subject: Fix some N+1s when calculating notification recipients First N+1: we may have loaded a user's notification settings already, but not have loaded their sources. Because we're iterating through, we'd potentially load sources that are completely unrelated, just because they belong to this user. Second N+1: we do a separate query for each user who could be subscribed to or unsubcribed from the target. It's actually more efficient in this case to get all subscriptions at once, as we will need to check most of them. We can fix both by the slightly unpleasant means of checking IDs manually, rather than object equality. --- .../notification_recipient_service_spec.rb | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 spec/services/notification_recipient_service_spec.rb (limited to 'spec') diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb new file mode 100644 index 00000000000..340d4585e0c --- /dev/null +++ b/spec/services/notification_recipient_service_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe NotificationRecipientService do + let(:service) { described_class } + let(:assignee) { create(:user) } + let(:project) { create(:project, :public) } + let(:other_projects) { create_list(:project, 5, :public) } + + describe '#build_new_note_recipients' do + let(:issue) { create(:issue, project: project, assignees: [assignee]) } + let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) } + + def create_watcher + watcher = create(:user) + create(:notification_setting, project: project, user: watcher, level: :watch) + + other_projects.each do |other_project| + create(:notification_setting, project: other_project, user: watcher, level: :watch) + end + end + + it 'avoids N+1 queries' do + create_watcher + + service.build_new_note_recipients(note) + + control_count = ActiveRecord::QueryRecorder.new do + service.build_new_note_recipients(note) + end + + create_watcher + + expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count) + end + end +end -- cgit v1.2.1 From 93c7976ae26370a6d65c3d5038323911592c20ce Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Thu, 7 Jun 2018 12:24:57 +0000 Subject: Restore navigation theme order --- spec/helpers/preferences_helper_spec.rb | 2 +- spec/lib/gitlab/themes_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 9940656fb68..363ebc88afd 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -31,7 +31,7 @@ describe PreferencesHelper do describe '#user_application_theme' do context 'with a user' do it "returns user's theme's css_class" do - stub_user(theme_id: 10) + stub_user(theme_id: 3) expect(helper.user_application_theme).to eq 'ui-light' end diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb index af2f4568017..a8213988f70 100644 --- a/spec/lib/gitlab/themes_spec.rb +++ b/spec/lib/gitlab/themes_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::Themes, lib: true do css = described_class.body_classes expect(css).to include('ui-indigo') - expect(css).to include('ui-dark ') + expect(css).to include('ui-dark') expect(css).to include('ui-blue') end end @@ -14,7 +14,7 @@ describe Gitlab::Themes, lib: true do describe '.by_id' do it 'returns a Theme by its ID' do expect(described_class.by_id(1).name).to eq 'Indigo' - expect(described_class.by_id(10).name).to eq 'Light' + expect(described_class.by_id(3).name).to eq 'Light' end end -- cgit v1.2.1 From 616dd00abbd09e6ef550e2eb877ae9fe2b28812b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 14:56:48 +0200 Subject: Add trait for manual deploy strategy --- spec/factories/project_auto_devops.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index a59087cd7eb..189d097d5e6 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -4,5 +4,9 @@ FactoryBot.define do enabled true domain "example.com" deploy_strategy :continuous + + trait :manual do + deploy_strategy :manual + end end end -- cgit v1.2.1 From 637b90db6edbd13d15289bf0d8d2c442d05442f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 7 Jun 2018 14:58:09 +0200 Subject: Add spec for continuous deploy strategy --- spec/models/project_auto_devops_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'spec') diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 040e76e28fc..f9ee9fe98bd 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -82,6 +82,19 @@ describe ProjectAutoDevops do end end + context 'when deploy_strategy is continuous' do + let(:domain) { 'example.com' } + + before do + auto_devops.deploy_strategy = 'continuous' + end + + it do + expect(auto_devops.predefined_variables.map { |var| var[:key] }) + .not_to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED") + end + end + def domain_variable { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } end -- cgit v1.2.1 From 435e661a2ec060c4b7349b26b506243c27e8cc30 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jun 2018 15:17:44 +0200 Subject: Introduce new keep-alive API entrypoint for CI job --- spec/requests/api/runner_spec.rb | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'spec') diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index c981a10ac38..c93612d7ada 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -849,6 +849,68 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end + describe 'POST /api/v4/jobs/:id/keep-alive' do + let(:job) { create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) } + let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } + let(:update_interval) { 30.seconds } + + it 'returns correct response' do + keep_alive_job + + expect(response.status).to eq 200 + expect(response.header).to have_key 'Job-Status' + end + + it 'updates updated_at value' do + expect { keep_alive_job }.to change { job.updated_at } + end + + context 'when project for the build has been deleted' do + let(:job) do + create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) do |job| + job.project.update(pending_delete: true) + end + end + + it 'responds with forbidden' do + keep_alive_job + + expect(response.status).to eq(403) + end + end + + context 'when job has been canceled' do + before do + job.cancel + end + + it 'returns job-status=canceled header' do + keep_alive_job + + expect(response.status).to eq 200 + expect(response.header['Job-Status']).to eq('canceled') + end + end + + context 'when job has been errased' do + let(:job) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) } + + it 'rresponds with forbidden' do + keep_alive_job + + expect(response.status).to eq 403 + end + end + + def keep_alive_job(token = job.token, **params) + new_params = params.merge(token: token) + Timecop.travel(job.updated_at + update_interval) do + post api("/jobs/#{job.id}/keep-alive"), new_params + job.reload + end + end + end + describe 'PATCH /api/v4/jobs/:id/trace' do let(:job) { create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) } let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } -- cgit v1.2.1 From 02a2ca6ae4ab6f2b64ff00f5750a68e9371a4fb7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 7 Jun 2018 14:47:09 +0100 Subject: Fixes IDE button on merge requests not working with relative URL config Closes #46438 --- spec/javascripts/lib/utils/url_utility_spec.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 spec/javascripts/lib/utils/url_utility_spec.js (limited to 'spec') diff --git a/spec/javascripts/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js new file mode 100644 index 00000000000..8ed4950ee09 --- /dev/null +++ b/spec/javascripts/lib/utils/url_utility_spec.js @@ -0,0 +1,25 @@ +import { webIDEUrl } from '~/lib/utils/url_utility'; + +describe('URL utility', () => { + describe('webIDEUrl', () => { + describe('without relative_url_root', () => { + it('returns IDE path with route', () => { + expect(webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe( + '/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1', + ); + }); + }); + + describe('with relative_url_root', () => { + beforeEach(() => { + gon.relative_url_root = '/gitlab'; + }); + + it('returns IDE path with route', () => { + expect(webIDEUrl('/gitlab/gitlab-org/gitlab-ce/merge_requests/1')).toBe( + '/gitlab/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1', + ); + }); + }); + }); +}); -- cgit v1.2.1 From 44975f8a5ad9c40c615f47f683fb46c94aa0e130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mica=C3=ABl=20Bergeron?= Date: Thu, 7 Jun 2018 10:01:47 -0400 Subject: shave off another 20% query --- spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb index 18da0c6d39a..ba01cfe53c5 100644 --- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb +++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -MIGRATION_QUERIES = 7 +MIGRATION_QUERIES = 5 describe ObjectStorage::MigrateUploadsWorker, :sidekiq do shared_context 'sanity_check! fails' do @@ -14,7 +14,6 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do let(:to_store) { ObjectStorage::Store::REMOTE } def perform(uploads) - binding.pry described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, to_store) rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures # swallow -- cgit v1.2.1 From 132db99a5b98daf7629f416ebb4ff817010cc0a9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 7 Jun 2018 15:11:10 +0100 Subject: fixed karma --- spec/javascripts/lib/utils/url_utility_spec.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js index 8ed4950ee09..c7f4092911c 100644 --- a/spec/javascripts/lib/utils/url_utility_spec.js +++ b/spec/javascripts/lib/utils/url_utility_spec.js @@ -2,6 +2,10 @@ import { webIDEUrl } from '~/lib/utils/url_utility'; describe('URL utility', () => { describe('webIDEUrl', () => { + afterEach(() => { + gon.relative_url_root = ''; + }); + describe('without relative_url_root', () => { it('returns IDE path with route', () => { expect(webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe( -- cgit v1.2.1 From cb6a16a2c6b15bbd51c02e74a6086ae33e83d3c8 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 7 Jun 2018 15:53:14 +0100 Subject: fixed more specs --- .../vue_mr_widget/components/mr_widget_header_spec.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js index 9b9c9656979..3d36e46d863 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js @@ -12,6 +12,7 @@ describe('MRWidgetHeader', () => { afterEach(() => { vm.$destroy(); + gon.relative_url_root = ''; }); describe('computed', () => { @@ -145,7 +146,16 @@ describe('MRWidgetHeader', () => { const button = vm.$el.querySelector('.js-web-ide'); expect(button.textContent.trim()).toEqual('Web IDE'); - expect(button.getAttribute('href')).toEqual('undefined/-/ide/projectabc'); + expect(button.getAttribute('href')).toEqual('/-/ide/projectabc'); + }); + + it('renders web ide button with relative URL', () => { + gon.relative_url_root = '/gitlab'; + + const button = vm.$el.querySelector('.js-web-ide'); + + expect(button.textContent.trim()).toEqual('Web IDE'); + expect(button.getAttribute('href')).toEqual('/-/ide/projectabc'); }); it('renders download dropdown with links', () => { -- cgit v1.2.1 From 6d165c740cecf6aff4c7ec0bbb962e7964c15f1b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jun 2018 16:18:06 -0500 Subject: Make all uses of `fixture_file_upload` use relative paths --- spec/controllers/boards/issues_controller_spec.rb | 2 +- spec/controllers/groups/avatars_controller_spec.rb | 2 +- spec/controllers/import/gitlab_projects_controller_spec.rb | 2 +- spec/controllers/import/google_code_controller_spec.rb | 2 +- spec/controllers/profiles/avatars_controller_spec.rb | 2 +- spec/controllers/projects/avatars_controller_spec.rb | 2 +- spec/controllers/projects_controller_spec.rb | 4 ++-- spec/controllers/uploads_controller_spec.rb | 14 +++++++------- spec/factories/lfs_objects.rb | 2 +- spec/factories/notes.rb | 4 ++-- spec/features/commits_spec.rb | 2 +- .../merge_request/user_sees_mini_pipeline_graph_spec.rb | 4 ++-- spec/features/projects/jobs_spec.rb | 2 +- spec/features/projects/pages_spec.rb | 4 ++-- spec/lib/gitlab/import_export/avatar_saver_spec.rb | 2 +- spec/lib/gitlab/import_export/uploads_saver_spec.rb | 2 +- spec/models/ci/job_artifact_spec.rb | 4 ++-- spec/requests/api/projects_spec.rb | 4 ++-- spec/requests/api/runner_spec.rb | 4 ++-- spec/requests/api/users_spec.rb | 2 +- spec/requests/openid_connect_spec.rb | 2 +- spec/services/projects/fork_service_spec.rb | 2 +- .../projects/gitlab_projects_import_service_spec.rb | 2 +- spec/services/projects/participants_service_spec.rb | 2 +- spec/services/projects/update_pages_service_spec.rb | 10 +++++----- spec/services/upload_service_spec.rb | 10 +++++----- spec/support/rspec.rb | 2 ++ .../controllers/uploads_actions_shared_examples.rb | 4 ++-- spec/uploaders/attachment_uploader_spec.rb | 2 +- spec/uploaders/file_mover_spec.rb | 2 +- spec/uploaders/file_uploader_spec.rb | 2 +- spec/uploaders/job_artifact_uploader_spec.rb | 2 +- spec/uploaders/namespace_file_uploader_spec.rb | 2 +- spec/uploaders/object_storage_spec.rb | 2 +- spec/uploaders/personal_file_uploader_spec.rb | 2 +- spec/uploaders/records_uploads_spec.rb | 2 +- spec/uploaders/uploader_helper_spec.rb | 2 +- 37 files changed, 60 insertions(+), 58 deletions(-) (limited to 'spec') diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index dcb0faffbd4..e47ff8661a2 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -18,7 +18,7 @@ describe Boards::IssuesController do end describe 'GET index', :request_store do - let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) } + let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join('spec/fixtures/dk.png'))) } context 'with invalid board id' do it 'returns a not found 404 response' do diff --git a/spec/controllers/groups/avatars_controller_spec.rb b/spec/controllers/groups/avatars_controller_spec.rb index 506aeee7d2a..7feecd0c380 100644 --- a/spec/controllers/groups/avatars_controller_spec.rb +++ b/spec/controllers/groups/avatars_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Groups::AvatarsController do let(:user) { create(:user) } - let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:group) { create(:group, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } before do group.add_owner(user) diff --git a/spec/controllers/import/gitlab_projects_controller_spec.rb b/spec/controllers/import/gitlab_projects_controller_spec.rb index 8759d3c0b97..d624659bce9 100644 --- a/spec/controllers/import/gitlab_projects_controller_spec.rb +++ b/spec/controllers/import/gitlab_projects_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Import::GitlabProjectsController do set(:namespace) { create(:namespace) } set(:user) { namespace.owner } - let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } before do sign_in(user) diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb index 4241db6e771..0763492d88a 100644 --- a/spec/controllers/import/google_code_controller_spec.rb +++ b/spec/controllers/import/google_code_controller_spec.rb @@ -4,7 +4,7 @@ describe Import::GoogleCodeController do include ImportSpecHelper let(:user) { create(:user) } - let(:dump_file) { fixture_file_upload(Rails.root + 'spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') } + let(:dump_file) { fixture_file_upload('spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') } before do sign_in(user) diff --git a/spec/controllers/profiles/avatars_controller_spec.rb b/spec/controllers/profiles/avatars_controller_spec.rb index 4fa0462ccdf..909709e1103 100644 --- a/spec/controllers/profiles/avatars_controller_spec.rb +++ b/spec/controllers/profiles/avatars_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Profiles::AvatarsController do - let(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png")) } + let(:user) { create(:user, avatar: fixture_file_upload("spec/fixtures/dk.png")) } before do sign_in(user) diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb index 6a41c4d23ea..acfa2730d94 100644 --- a/spec/controllers/projects/avatars_controller_spec.rb +++ b/spec/controllers/projects/avatars_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::AvatarsController do - let(:project) { create(:project, :repository, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:project) { create(:project, :repository, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } let(:user) { create(:user) } before do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 994da3cd159..5bd22ea803c 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -6,8 +6,8 @@ describe ProjectsController do let(:project) { create(:project) } let(:public_project) { create(:project, :public) } let(:user) { create(:user) } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } - let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } describe 'GET new' do context 'with an authenticated user' do diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 376b229ffc9..5892daa1a94 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -6,13 +6,13 @@ shared_examples 'content not cached without revalidation' do end describe UploadsController do - let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:user) { create(:user, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } describe 'POST create' do let(:model) { 'personal_snippet' } let(:snippet) { create(:personal_snippet, :public) } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } - let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } context 'when a user does not have permissions to upload a file' do it "returns 401 when the user is not logged in" do @@ -205,7 +205,7 @@ describe UploadsController do end context "when viewing a project avatar" do - let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:project) { create(:project, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } context "when the project is public" do before do @@ -314,7 +314,7 @@ describe UploadsController do end context "when viewing a group avatar" do - let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:group) { create(:group, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } context "when the group is public" do context "when not signed in" do @@ -521,7 +521,7 @@ describe UploadsController do context 'Appearance' do context 'when viewing a custom header logo' do - let!(:appearance) { create :appearance, header_logo: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + let!(:appearance) { create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png', 'image/png') } context 'when not signed in' do it 'responds with status 200' do @@ -541,7 +541,7 @@ describe UploadsController do end context 'when viewing a custom logo' do - let!(:appearance) { create :appearance, logo: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + let!(:appearance) { create :appearance, logo: fixture_file_upload('spec/fixtures/dk.png', 'image/png') } context 'when not signed in' do it 'responds with status 200' do diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb index eaf3a4ed497..c909471bb55 100644 --- a/spec/factories/lfs_objects.rb +++ b/spec/factories/lfs_objects.rb @@ -7,7 +7,7 @@ FactoryBot.define do end trait :with_file do - file { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") } + file { fixture_file_upload("spec/fixtures/dk.png", "`/png") } end # The uniqueness constraint means we can't use the correct OID for all LFS diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 40f3fa7d69b..9fdc3e616a6 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -130,11 +130,11 @@ FactoryBot.define do end trait :with_attachment do - attachment { fixture_file_upload(Rails.root.join( "spec/fixtures/dk.png"), "image/png") } + attachment { fixture_file_upload("spec/fixtures/dk.png", "image/png") } end trait :with_svg_attachment do - attachment { fixture_file_upload(Rails.root.join("spec/fixtures/unsanitized.svg"), "image/svg+xml") } + attachment { fixture_file_upload("spec/fixtures/unsanitized.svg", "image/svg+xml") } end transient do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 62a2ec55b00..87fa3f60826 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -47,7 +47,7 @@ describe 'Commits' do context 'commit status is Ci Build' do let!(:build) { create(:ci_build, pipeline: pipeline) } - let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + let(:artifacts_file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } context 'when logged as developer' do before do diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb index fd1629746ef..d3104b448e0 100644 --- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb +++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb @@ -23,8 +23,8 @@ describe 'Merge request < User sees mini pipeline graph', :js do end context 'as json' do - let(:artifacts_file1) { fixture_file_upload(Rails.root.join('spec/fixtures/banana_sample.gif'), 'image/gif') } - let(:artifacts_file2) { fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + let(:artifacts_file1) { fixture_file_upload(File.join('spec/fixtures/banana_sample.gif'), 'image/gif') } + let(:artifacts_file2) { fixture_file_upload(File.join('spec/fixtures/dk.png'), 'image/png') } before do create(:ci_build, :success, :trace_artifact, pipeline: pipeline, legacy_artifacts_file: artifacts_file1) diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 9d1c4cbad8b..d2aaf60e72c 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -11,7 +11,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do let(:job2) { create(:ci_build) } let(:artifacts_file) do - fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') end before do diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb index bdd49f731c7..a2899ec0f48 100644 --- a/spec/features/projects/pages_spec.rb +++ b/spec/features/projects/pages_spec.rb @@ -314,8 +314,8 @@ feature 'Pages' do project: project, pipeline: pipeline, ref: 'HEAD', - legacy_artifacts_file: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip')), - legacy_artifacts_metadata: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip.meta')) + legacy_artifacts_file: fixture_file_upload(File.join('spec/fixtures/pages.zip')), + legacy_artifacts_metadata: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta')) ) end diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb index f40d4bc2d08..2223f163177 100644 --- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb +++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::AvatarSaver do let(:shared) { project.import_export_shared } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } - let(:project_with_avatar) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let(:project_with_avatar) { create(:project, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } let(:project) { create(:project) } before do diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb index 1304d8fabfc..095687fa89d 100644 --- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb +++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::ImportExport::UploadsSaver do describe 'bundle a project Git repo' do let(:export_path) { "#{Dir.tmpdir}/uploads_saver_spec" } - let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } let(:shared) { project.import_export_shared } before do diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index a3e119cbc27..efddd4e7662 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -76,12 +76,12 @@ describe Ci::JobArtifact do context 'updating the artifact file' do it 'updates the artifact size' do - artifact.update!(file: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) + artifact.update!(file: fixture_file_upload('spec/fixtures/dk.png')) expect(artifact.size).to eq(1062) end it 'updates the project statistics' do - expect { artifact.update!(file: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) } + expect { artifact.update!(file: fixture_file_upload('spec/fixtures/dk.png')) } .to change { artifact.project.statistics.reload.build_artifacts_size } .by(1062 - 106365) end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 9b7c3205c1f..99103039f77 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -518,7 +518,7 @@ describe API::Projects do end it 'uploads avatar for project a project' do - project = attributes_for(:project, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')) + project = attributes_for(:project, avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif')) post api('/projects', user), project @@ -777,7 +777,7 @@ describe API::Projects do end it "uploads the file and returns its info" do - post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") + post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload("spec/fixtures/dk.png", "image/png") expect(response).to have_gitlab_http_status(201) expect(json_response['alt']).to eq("dk") diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index c981a10ac38..9b167a5ed23 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1055,8 +1055,8 @@ describe API::Runner, :clean_gitlab_redis_shared_state do let(:jwt_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } let(:headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => jwt_token } } let(:headers_with_token) { headers.merge(API::Helpers::Runner::JOB_TOKEN_HEADER => job.token) } - let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } - let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } + let(:file_upload) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } + let(:file_upload2) { fixture_file_upload('spec/fixtures/dk.png', 'image/gif') } before do stub_artifacts_object_storage diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 05637eb0729..3377d67b644 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -512,7 +512,7 @@ describe API::Users do end it 'updates user with avatar' do - put api("/users/#{user.id}", admin), { avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + put api("/users/#{user.id}", admin), { avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') } user.reload diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index be286c490fe..bcb8d6c2bfc 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -61,7 +61,7 @@ describe 'OpenID Connect requests' do email: private_email.email, public_email: public_email.email, website_url: 'https://example.com', - avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png") + avatar: fixture_file_upload('spec/fixtures/dk.png') ) end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index a93f6f1ddc2..c15f5120b8a 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -8,7 +8,7 @@ describe Projects::ForkService do before do @from_user = create(:user) @from_namespace = @from_user.namespace - avatar = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") + avatar = fixture_file_upload("spec/fixtures/dk.png", "image/png") @from_project = create(:project, :repository, creator_id: @from_user.id, diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb index ee1a886f5d6..0a898e9b89b 100644 --- a/spec/services/projects/gitlab_projects_import_service_spec.rb +++ b/spec/services/projects/gitlab_projects_import_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Projects::GitlabProjectsImportService do set(:namespace) { create(:namespace) } let(:path) { 'test-path' } - let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } let(:overwrite) { false } let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } } subject { described_class.new(namespace.owner, import_params) } diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb index 0d18ceb8ff8..6040f9100f8 100644 --- a/spec/services/projects/participants_service_spec.rb +++ b/spec/services/projects/participants_service_spec.rb @@ -4,7 +4,7 @@ describe Projects::ParticipantsService do describe '#groups' do describe 'avatar_url' do let(:project) { create(:project, :public) } - let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/dk.png')) } + let(:group) { create(:group, avatar: fixture_file_upload('spec/fixtures/dk.png')) } let(:user) { create(:user) } let!(:group_member) { create(:group_member, group: group, user: user) } diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index 347ac13828c..35a9b744b10 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -4,11 +4,11 @@ describe Projects::UpdatePagesService do set(:project) { create(:project, :repository) } set(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) } set(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') } - let(:invalid_file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png') } + let(:invalid_file) { fixture_file_upload('spec/fixtures/dk.png') } let(:extension) { 'zip' } - let(:file) { fixture_file_upload(Rails.root + "spec/fixtures/pages.#{extension}") } - let(:empty_file) { fixture_file_upload(Rails.root + "spec/fixtures/pages_empty.#{extension}") } + let(:file) { fixture_file_upload("spec/fixtures/pages.#{extension}") } + let(:empty_file) { fixture_file_upload("spec/fixtures/pages_empty.#{extension}") } let(:metadata) do filename = Rails.root + "spec/fixtures/pages.#{extension}.meta" fixture_file_upload(filename) if File.exist?(filename) @@ -196,8 +196,8 @@ describe Projects::UpdatePagesService do let(:metadata) { spy('metadata') } before do - file = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip') - metafile = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta') + file = fixture_file_upload('spec/fixtures/pages.zip') + metafile = fixture_file_upload('spec/fixtures/pages.zip.meta') build.update_attributes(legacy_artifacts_file: file) build.update_attributes(legacy_artifacts_metadata: metafile) diff --git a/spec/services/upload_service_spec.rb b/spec/services/upload_service_spec.rb index 24f3a5c5ff0..9b232a52efa 100644 --- a/spec/services/upload_service_spec.rb +++ b/spec/services/upload_service_spec.rb @@ -9,7 +9,7 @@ describe UploadService do context 'for valid gif file' do before do - gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + gif = fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') @link_to_file = upload_file(@project, gif) end @@ -21,7 +21,7 @@ describe UploadService do context 'for valid png file' do before do - png = fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', + png = fixture_file_upload('spec/fixtures/dk.png', 'image/png') @link_to_file = upload_file(@project, png) end @@ -34,7 +34,7 @@ describe UploadService do context 'for valid jpg file' do before do - jpg = fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') + jpg = fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') @link_to_file = upload_file(@project, jpg) end @@ -46,7 +46,7 @@ describe UploadService do context 'for txt file' do before do - txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') + txt = fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') @link_to_file = upload_file(@project, txt) end @@ -58,7 +58,7 @@ describe UploadService do context 'for too large a file' do before do - txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') + txt = fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') allow(txt).to receive(:size) { 1000.megabytes.to_i } @link_to_file = upload_file(@project, txt) end diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index dffab22d8b5..54b8df7aa19 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -9,4 +9,6 @@ RSpec.configure do |config| config.include StubConfiguration config.include StubObjectStorage config.include StubENV + + config.fixture_path = Rails.root if defined?(Rails) end diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index ea7dbade171..bbbad86dcd5 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -1,7 +1,7 @@ shared_examples 'handle uploads' do let(:user) { create(:user) } - let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } - let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } + let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } + let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') } let(:secret) { FileUploader.generate_secret } let(:uploader_class) { FileUploader } diff --git a/spec/uploaders/attachment_uploader_spec.rb b/spec/uploaders/attachment_uploader_spec.rb index d302c14efb9..a9415854d25 100644 --- a/spec/uploaders/attachment_uploader_spec.rb +++ b/spec/uploaders/attachment_uploader_spec.rb @@ -26,7 +26,7 @@ describe AttachmentUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt'))) + uploader.store!(fixture_file_upload(File.join('spec/fixtures/doc_sample.txt'))) stub_uploads_object_storage end diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb index 68b7e24776d..de29d0c943f 100644 --- a/spec/uploaders/file_mover_spec.rb +++ b/spec/uploaders/file_mover_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe FileMover do let(:filename) { 'banana_sample.gif' } - let(:file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) } + let(:file) { fixture_file_upload(File.join('spec', 'fixtures', filename)) } let(:temp_file_path) { File.join('uploads/-/system/temp', 'secret55', filename) } let(:temp_description) do diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb index db2810bbe1d..59013a02938 100644 --- a/spec/uploaders/file_uploader_spec.rb +++ b/spec/uploaders/file_uploader_spec.rb @@ -89,7 +89,7 @@ describe FileUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'))) + uploader.store!(fixture_file_upload('spec/fixtures/dk.png')) stub_uploads_object_storage end diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb index 42036d67f3d..179c94ec3bc 100644 --- a/spec/uploaders/job_artifact_uploader_spec.rb +++ b/spec/uploaders/job_artifact_uploader_spec.rb @@ -81,7 +81,7 @@ describe JobArtifactUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/trace/sample_trace'))) + uploader.store!(fixture_file_upload(File.join('spec/fixtures/trace/sample_trace'))) stub_artifacts_object_storage end diff --git a/spec/uploaders/namespace_file_uploader_spec.rb b/spec/uploaders/namespace_file_uploader_spec.rb index a8ba01d70b8..71fe2c353c0 100644 --- a/spec/uploaders/namespace_file_uploader_spec.rb +++ b/spec/uploaders/namespace_file_uploader_spec.rb @@ -28,7 +28,7 @@ describe NamespaceFileUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt'))) + uploader.store!(fixture_file_upload(File.join('spec/fixtures/doc_sample.txt'))) stub_uploads_object_storage end diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 4503288e410..0bc5b6751b3 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -571,7 +571,7 @@ describe ObjectStorage do context 'when local file is used' do context 'when valid file is used' do let(:uploaded_file) do - fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') + fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') end it "properly caches the file" do diff --git a/spec/uploaders/personal_file_uploader_spec.rb b/spec/uploaders/personal_file_uploader_spec.rb index c70521d90dc..7700b14ce6b 100644 --- a/spec/uploaders/personal_file_uploader_spec.rb +++ b/spec/uploaders/personal_file_uploader_spec.rb @@ -45,7 +45,7 @@ describe PersonalFileUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt'))) + uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt')) stub_uploads_object_storage end diff --git a/spec/uploaders/records_uploads_spec.rb b/spec/uploaders/records_uploads_spec.rb index 9a3e5d83e01..3592a11360d 100644 --- a/spec/uploaders/records_uploads_spec.rb +++ b/spec/uploaders/records_uploads_spec.rb @@ -16,7 +16,7 @@ describe RecordsUploads do end def upload_fixture(filename) - fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) + fixture_file_upload(File.join('spec', 'fixtures', filename)) end describe 'callbacks' do diff --git a/spec/uploaders/uploader_helper_spec.rb b/spec/uploaders/uploader_helper_spec.rb index c47f09adb6d..33da93cc9d0 100644 --- a/spec/uploaders/uploader_helper_spec.rb +++ b/spec/uploaders/uploader_helper_spec.rb @@ -12,7 +12,7 @@ describe UploaderHelper do end def upload_fixture(filename) - fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) + fixture_file_upload(File.join('spec', 'fixtures', filename)) end describe '#image_or_video?' do -- cgit v1.2.1 From b38c75b3dc2dc655377a1b4070cb6f5242594dbe Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 6 Jun 2018 16:26:00 -0500 Subject: Correct more usages of non-relative `fixture_file_upload` paths --- spec/features/projects/jobs/permissions_spec.rb | 3 +-- spec/helpers/emails_helper_spec.rb | 4 +--- spec/helpers/groups_helper_spec.rb | 8 ++++---- spec/requests/api/project_import_spec.rb | 2 +- spec/services/projects/update_pages_service_spec.rb | 2 +- spec/uploaders/gitlab_uploader_spec.rb | 2 +- spec/uploaders/job_artifact_uploader_spec.rb | 8 +++----- spec/uploaders/legacy_artifact_uploader_spec.rb | 3 +-- 8 files changed, 13 insertions(+), 19 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb index 31abadf9bd6..e9588daf37d 100644 --- a/spec/features/projects/jobs/permissions_spec.rb +++ b/spec/features/projects/jobs/permissions_spec.rb @@ -88,8 +88,7 @@ describe 'Project Jobs Permissions' do describe 'artifacts page' do context 'when recent job has artifacts available' do before do - artifacts = Rails.root.join('spec/fixtures/ci_build_artifacts.zip') - archive = fixture_file_upload(artifacts, 'application/zip') + archive = fixture_file_upload('spec/fixtures/ci_build_artifacts.zip') job.update_attributes(legacy_artifacts_file: archive) end diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index 2390c1f3e5d..139387e0b24 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -47,9 +47,7 @@ describe EmailsHelper do describe '#header_logo' do context 'there is a brand item with a logo' do it 'returns the brand header logo' do - appearance = create :appearance, header_logo: fixture_file_upload( - Rails.root.join('spec/fixtures/dk.png') - ) + appearance = create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png') expect(header_logo).to eq( %{Dk} diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index b48c252acd3..6c94bd4e504 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -4,9 +4,9 @@ describe GroupsHelper do include ApplicationHelper describe 'group_icon' do - avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') - it 'returns an url for the avatar' do + avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif') + group = create(:group) group.avatar = fixture_file_upload(avatar_file_path) group.save! @@ -17,9 +17,9 @@ describe GroupsHelper do end describe 'group_icon_url' do - avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') - it 'returns an url for the avatar' do + avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif') + group = create(:group) group.avatar = fixture_file_upload(avatar_file_path) group.save! diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index f8c64f063af..97dffdc9233 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe API::ProjectImport do let(:export_path) { "#{Dir.tmpdir}/project_export_spec" } let(:user) { create(:user) } - let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } + let(:file) { File.join('spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } let(:namespace) { create(:group) } before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index 35a9b744b10..1bffeee6790 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -10,7 +10,7 @@ describe Projects::UpdatePagesService do let(:file) { fixture_file_upload("spec/fixtures/pages.#{extension}") } let(:empty_file) { fixture_file_upload("spec/fixtures/pages_empty.#{extension}") } let(:metadata) do - filename = Rails.root + "spec/fixtures/pages.#{extension}.meta" + filename = "spec/fixtures/pages.#{extension}.meta" fixture_file_upload(filename) if File.exist?(filename) end diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb index 4fba122cce1..362f89424d4 100644 --- a/spec/uploaders/gitlab_uploader_spec.rb +++ b/spec/uploaders/gitlab_uploader_spec.rb @@ -62,7 +62,7 @@ describe GitlabUploader do expect(FileUtils).to receive(:mv).with(anything, /^#{subject.work_dir}/).and_call_original expect(FileUtils).to receive(:mv).with(/^#{subject.work_dir}/, /#{subject.cache_dir}/).and_call_original - fixture = Rails.root.join('spec', 'fixtures', 'rails_sample.jpg') + fixture = File.join('spec', 'fixtures', 'rails_sample.jpg') subject.cache!(fixture_file_upload(fixture)) expect(subject.file.path).to match(/#{subject.cache_dir}/) diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb index 179c94ec3bc..026e4356ed6 100644 --- a/spec/uploaders/job_artifact_uploader_spec.rb +++ b/spec/uploaders/job_artifact_uploader_spec.rb @@ -29,8 +29,7 @@ describe JobArtifactUploader do context 'when trace is stored in File storage' do context 'when file exists' do let(:file) do - fixture_file_upload( - Rails.root.join('spec/fixtures/trace/sample_trace'), 'text/plain') + fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain') end before do @@ -63,8 +62,7 @@ describe JobArtifactUploader do context 'file is stored in valid local_path' do let(:file) do - fixture_file_upload( - Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip') + fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip') end before do @@ -81,7 +79,7 @@ describe JobArtifactUploader do describe "#migrate!" do before do - uploader.store!(fixture_file_upload(File.join('spec/fixtures/trace/sample_trace'))) + uploader.store!(fixture_file_upload('spec/fixtures/trace/sample_trace')) stub_artifacts_object_storage end diff --git a/spec/uploaders/legacy_artifact_uploader_spec.rb b/spec/uploaders/legacy_artifact_uploader_spec.rb index eeb6fd90c9d..0589563b502 100644 --- a/spec/uploaders/legacy_artifact_uploader_spec.rb +++ b/spec/uploaders/legacy_artifact_uploader_spec.rb @@ -44,8 +44,7 @@ describe LegacyArtifactUploader do context 'file is stored in valid path' do let(:file) do - fixture_file_upload( - Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip') + fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip') end before do -- cgit v1.2.1 From 9f76632d80b5f1d9a47faa95f537e579b6ac41d6 Mon Sep 17 00:00:00 2001 From: Balasankar C Date: Thu, 7 Jun 2018 15:01:20 +0000 Subject: Add installation type to usage ping data --- spec/lib/gitlab/usage_data_spec.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index a716e6f5434..22d921716aa 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -32,6 +32,7 @@ describe Gitlab::UsageData do mattermost_enabled edition version + installation_type uuid hostname signup @@ -156,6 +157,7 @@ describe Gitlab::UsageData do it "gathers license data" do expect(subject[:uuid]).to eq(Gitlab::CurrentSettings.uuid) expect(subject[:version]).to eq(Gitlab::VERSION) + expect(subject[:installation_type]).to eq(Gitlab::INSTALLATION_TYPE) expect(subject[:active_user_count]).to eq(User.active.count) expect(subject[:recorded_at]).to be_a(Time) end -- cgit v1.2.1 From 6afe6fa6bcb3bdb09bd49ba638a37af2a8c7a6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 7 Jun 2018 17:23:38 +0200 Subject: Make Gitlab::CurrentSettings.current_application_settings return cached settings early if they exist without issuing any DB query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/lib/gitlab/current_settings_spec.rb | 143 +++++++++++++++++++------------ 1 file changed, 88 insertions(+), 55 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb index 19028495f52..55490f37ac7 100644 --- a/spec/lib/gitlab/current_settings_spec.rb +++ b/spec/lib/gitlab/current_settings_spec.rb @@ -5,6 +5,13 @@ describe Gitlab::CurrentSettings do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end + shared_context 'with settings in cache' do + before do + create(:application_setting) + described_class.current_application_settings # warm the cache + end + end + describe '#current_application_settings', :use_clean_rails_memory_store_caching do it 'allows keys to be called directly' do db_settings = create(:application_setting, @@ -31,16 +38,29 @@ describe Gitlab::CurrentSettings do end context 'with DB unavailable' do - before do - # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues - # during the initialization phase of the test suite, so instead let's mock the internals of it - allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false) + context 'and settings in cache' do + include_context 'with settings in cache' + + it 'fetches the settings from cache without issuing any query' do + expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) + end end - it 'returns an in-memory ApplicationSetting object' do - expect(ApplicationSetting).not_to receive(:current) + context 'and no settings in cache' do + before do + # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues + # during the initialization phase of the test suite, so instead let's mock the internals of it + allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false) + expect(ApplicationSetting).not_to receive(:current) + end - expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + it 'returns an in-memory ApplicationSetting object' do + expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + end + + it 'does not issue any query' do + expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) + end end end @@ -52,73 +72,86 @@ describe Gitlab::CurrentSettings do ar_wrapped_defaults.slice(*::ApplicationSetting.defaults.keys) end - before do - # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues - # during the initialization phase of the test suite, so instead let's mock the internals of it - allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true) - allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true) - end + context 'and settings in cache' do + include_context 'with settings in cache' - it 'creates default ApplicationSettings if none are present' do - settings = described_class.current_application_settings - - expect(settings).to be_a(ApplicationSetting) - expect(settings).to be_persisted - expect(settings).to have_attributes(settings_from_defaults) + it 'fetches the settings from cache' do + # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues + # during the initialization phase of the test suite, so instead let's mock the internals of it + expect(ActiveRecord::Base.connection).not_to receive(:active?) + expect(ActiveRecord::Base.connection).not_to receive(:cached_table_exists?) + expect(ActiveRecord::Migrator).not_to receive(:needs_migration?) + expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) + end end - context 'with migrations pending' do + context 'and no settings in cache' do before do - expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true) + allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true) + allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true) end - it 'returns an in-memory ApplicationSetting object' do + it 'creates default ApplicationSettings if none are present' do settings = described_class.current_application_settings - expect(settings).to be_a(Gitlab::FakeApplicationSettings) - expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled) - expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled) + expect(settings).to be_a(ApplicationSetting) + expect(settings).to be_persisted + expect(settings).to have_attributes(settings_from_defaults) end - it 'uses the existing database settings and falls back to defaults' do - db_settings = create(:application_setting, - home_page_url: 'http://mydomain.com', - signup_enabled: false) - settings = described_class.current_application_settings - app_defaults = ApplicationSetting.last - - expect(settings).to be_a(Gitlab::FakeApplicationSettings) - expect(settings.home_page_url).to eq(db_settings.home_page_url) - expect(settings.signup_enabled?).to be_falsey - expect(settings.signup_enabled).to be_falsey - - # Check that unspecified values use the defaults - settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key } - settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) } + context 'with migrations pending' do + before do + expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true) + end + + it 'returns an in-memory ApplicationSetting object' do + settings = described_class.current_application_settings + + expect(settings).to be_a(Gitlab::FakeApplicationSettings) + expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled) + expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled) + end + + it 'uses the existing database settings and falls back to defaults' do + db_settings = create(:application_setting, + home_page_url: 'http://mydomain.com', + signup_enabled: false) + settings = described_class.current_application_settings + app_defaults = ApplicationSetting.last + + expect(settings).to be_a(Gitlab::FakeApplicationSettings) + expect(settings.home_page_url).to eq(db_settings.home_page_url) + expect(settings.signup_enabled?).to be_falsey + expect(settings.signup_enabled).to be_falsey + + # Check that unspecified values use the defaults + settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key } + settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) } + end end - end - context 'when ApplicationSettings.current is present' do - it 'returns the existing application settings' do - expect(ApplicationSetting).to receive(:current).and_return(:current_settings) + context 'when ApplicationSettings.current is present' do + it 'returns the existing application settings' do + expect(ApplicationSetting).to receive(:current).and_return(:current_settings) - expect(described_class.current_application_settings).to eq(:current_settings) + expect(described_class.current_application_settings).to eq(:current_settings) + end end - end - context 'when the application_settings table does not exists' do - it 'returns an in-memory ApplicationSetting object' do - expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid) + context 'when the application_settings table does not exists' do + it 'returns an in-memory ApplicationSetting object' do + expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid) - expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + end end - end - context 'when the application_settings table is not fully migrated' do - it 'returns an in-memory ApplicationSetting object' do - expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError) + context 'when the application_settings table is not fully migrated' do + it 'returns an in-memory ApplicationSetting object' do + expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError) - expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) + end end end end -- cgit v1.2.1 From 36c337647591d964b7ef1e1fc61fc64a930fb6f4 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 7 Jun 2018 15:40:44 +0000 Subject: Resolve "Hashed Storage: Make possible to migrate single project" --- spec/lib/gitlab/hashed_storage/migrator_spec.rb | 75 +++++++++++++++++++++++++ spec/tasks/gitlab/storage_rake_spec.rb | 45 ++++++++++++--- spec/workers/storage_migrator_worker_spec.rb | 25 ++++----- 3 files changed, 121 insertions(+), 24 deletions(-) create mode 100644 spec/lib/gitlab/hashed_storage/migrator_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb new file mode 100644 index 00000000000..813ae43b4d3 --- /dev/null +++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe Gitlab::HashedStorage::Migrator do + describe '#bulk_schedule' do + it 'schedules job to StorageMigratorWorker' do + Sidekiq::Testing.fake! do + expect { subject.bulk_schedule(1, 5) }.to change(StorageMigratorWorker.jobs, :size).by(1) + end + end + end + + describe '#bulk_migrate' do + let(:projects) { create_list(:project, 2, :legacy_storage) } + let(:ids) { projects.map(&:id) } + + it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do + Sidekiq::Testing.fake! do + expect { subject.bulk_migrate(ids.min, ids.max) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(2) + end + end + + it 'sets projects as read only' do + allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice + subject.bulk_migrate(ids.min, ids.max) + + projects.each do |project| + expect(project.reload.repository_read_only?).to be_truthy + end + end + + it 'rescues and log exceptions' do + allow_any_instance_of(Project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError) + expect { subject.bulk_migrate(ids.min, ids.max) }.not_to raise_error + end + + it 'delegates each project in specified range to #migrate' do + projects.each do |project| + expect(subject).to receive(:migrate).with(project) + end + + subject.bulk_migrate(ids.min, ids.max) + end + end + + describe '#migrate' do + let(:project) { create(:project, :legacy_storage, :empty_repo) } + + it 'enqueues job to ProjectMigrateHashedStorageWorker' do + Sidekiq::Testing.fake! do + expect { subject.migrate(project) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(1) + end + end + + it 'rescues and log exceptions' do + allow(project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError) + + expect { subject.migrate(project) }.not_to raise_error + end + + it 'sets project as read only' do + allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async) + subject.migrate(project) + + expect(project.reload.repository_read_only?).to be_truthy + end + + it 'migrate project' do + Sidekiq::Testing.inline! do + subject.migrate(project) + end + + expect(project.reload.hashed_storage?(:attachments)).to be_truthy + end + end +end diff --git a/spec/tasks/gitlab/storage_rake_spec.rb b/spec/tasks/gitlab/storage_rake_spec.rb index 35e451b2f9a..233076ad6fa 100644 --- a/spec/tasks/gitlab/storage_rake_spec.rb +++ b/spec/tasks/gitlab/storage_rake_spec.rb @@ -1,6 +1,6 @@ require 'rake_helper' -describe 'gitlab:storage:*' do +describe 'rake gitlab:storage:*' do before do Rake.application.rake_require 'tasks/gitlab/storage' @@ -44,16 +44,18 @@ describe 'gitlab:storage:*' do end describe 'gitlab:storage:migrate_to_hashed' do + let(:task) { 'gitlab:storage:migrate_to_hashed' } + context '0 legacy projects' do it 'does nothing' do expect(StorageMigratorWorker).not_to receive(:perform_async) - run_rake_task('gitlab:storage:migrate_to_hashed') + run_rake_task(task) end end context '3 legacy projects' do - let(:projects) { create_list(:project, 3, storage_version: 0) } + let(:projects) { create_list(:project, 3, :legacy_storage) } context 'in batches of 1' do before do @@ -65,7 +67,7 @@ describe 'gitlab:storage:*' do expect(StorageMigratorWorker).to receive(:perform_async).with(project.id, project.id) end - run_rake_task('gitlab:storage:migrate_to_hashed') + run_rake_task(task) end end @@ -80,23 +82,48 @@ describe 'gitlab:storage:*' do expect(StorageMigratorWorker).to receive(:perform_async).with(first, last) end - run_rake_task('gitlab:storage:migrate_to_hashed') + run_rake_task(task) end end end + + context 'with same id in range' do + it 'displays message when project cant be found' do + stub_env('ID_FROM', 99999) + stub_env('ID_TO', 99999) + + expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=99999/).to_stdout + end + + it 'displays a message when project exists but its already migrated' do + project = create(:project) + stub_env('ID_FROM', project.id) + stub_env('ID_TO', project.id) + + expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=#{project.id}/).to_stdout + end + + it 'enqueues migration when project can be found' do + project = create(:project, :legacy_storage) + stub_env('ID_FROM', project.id) + stub_env('ID_TO', project.id) + + expect { run_rake_task(task) }.to output(/Enqueueing storage migration .* \(ID=#{project.id}\)/).to_stdout + end + end end describe 'gitlab:storage:legacy_projects' do it_behaves_like 'rake entities summary', 'projects', 'Legacy' do let(:task) { 'gitlab:storage:legacy_projects' } - let(:create_collection) { create_list(:project, 3, storage_version: 0) } + let(:create_collection) { create_list(:project, 3, :legacy_storage) } end end describe 'gitlab:storage:list_legacy_projects' do it_behaves_like 'rake listing entities', 'projects', 'Legacy' do let(:task) { 'gitlab:storage:list_legacy_projects' } - let(:create_collection) { create_list(:project, 3, storage_version: 0) } + let(:create_collection) { create_list(:project, 3, :legacy_storage) } end end @@ -133,7 +160,7 @@ describe 'gitlab:storage:*' do describe 'gitlab:storage:hashed_attachments' do it_behaves_like 'rake entities summary', 'attachments', 'Hashed' do let(:task) { 'gitlab:storage:hashed_attachments' } - let(:project) { create(:project, storage_version: 2) } + let(:project) { create(:project) } let(:create_collection) { create_list(:upload, 3, model: project) } end end @@ -141,7 +168,7 @@ describe 'gitlab:storage:*' do describe 'gitlab:storage:list_hashed_attachments' do it_behaves_like 'rake listing entities', 'attachments', 'Hashed' do let(:task) { 'gitlab:storage:list_hashed_attachments' } - let(:project) { create(:project, storage_version: 2) } + let(:project) { create(:project) } let(:create_collection) { create_list(:upload, 3, model: project) } end end diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb index ff625164142..815432aacce 100644 --- a/spec/workers/storage_migrator_worker_spec.rb +++ b/spec/workers/storage_migrator_worker_spec.rb @@ -2,29 +2,24 @@ require 'spec_helper' describe StorageMigratorWorker do subject(:worker) { described_class.new } - let(:projects) { create_list(:project, 2, :legacy_storage) } + let(:projects) { create_list(:project, 2, :legacy_storage, :empty_repo) } + let(:ids) { projects.map(&:id) } describe '#perform' do - let(:ids) { projects.map(&:id) } + it 'delegates to MigratorService' do + expect_any_instance_of(Gitlab::HashedStorage::Migrator).to receive(:bulk_migrate).with(5, 10) - it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do - expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice - - worker.perform(ids.min, ids.max) + worker.perform(5, 10) end - it 'sets projects as read only' do - allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice - worker.perform(ids.min, ids.max) + it 'migrates projects in the specified range' do + Sidekiq::Testing.inline! do + worker.perform(ids.min, ids.max) + end projects.each do |project| - expect(project.reload.repository_read_only?).to be_truthy + expect(project.reload.hashed_storage?(:attachments)).to be_truthy end end - - it 'rescues and log exceptions' do - allow_any_instance_of(Project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError) - expect { worker.perform(ids.min, ids.max) }.not_to raise_error - end end end -- cgit v1.2.1 From 50872bcc242a582c7e3af25df4d32e1c3e0a28f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mica=C3=ABl=20Bergeron?= Date: Thu, 7 Jun 2018 11:06:04 -0400 Subject: fix the failing spec --- .../shared_examples/uploaders/object_storage_shared_examples.rb | 4 ++-- spec/uploaders/object_storage_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb index 6352f1527cd..1ecddc14d58 100644 --- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb +++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb @@ -85,13 +85,13 @@ shared_examples "migrates" do |to_store:, from_store: nil| it 'does not execute migrate!' do expect(subject).not_to receive(:unsafe_migrate!) - expect { migrate(to) }.to raise_error('exclusive lease already taken') + expect { migrate(to) }.to raise_error(ObjectStorage::ExclusiveLeaseTaken) end it 'does not execute use_file' do expect(subject).not_to receive(:unsafe_use_file) - expect { subject.use_file }.to raise_error('exclusive lease already taken') + expect { subject.use_file }.to raise_error(ObjectStorage::ExclusiveLeaseTaken) end after do diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 4503288e410..03386bf764f 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -321,7 +321,7 @@ describe ObjectStorage do when_file_is_in_use do expect(uploader).not_to receive(:unsafe_migrate!) - expect { uploader.migrate!(described_class::Store::REMOTE) }.to raise_error('exclusive lease already taken') + expect { uploader.migrate!(described_class::Store::REMOTE) }.to raise_error(ObjectStorage::ExclusiveLeaseTaken) end end @@ -329,7 +329,7 @@ describe ObjectStorage do when_file_is_in_use do expect(uploader).not_to receive(:unsafe_use_file) - expect { uploader.use_file }.to raise_error('exclusive lease already taken') + expect { uploader.use_file }.to raise_error(ObjectStorage::ExclusiveLeaseTaken) end end end -- cgit v1.2.1 From dd7a59bf94849f578ebc8f65aa7acbd0ed7c8f8e Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 7 Jun 2018 21:38:27 +0530 Subject: Add timeframe helper methods --- spec/javascripts/datetime_utility_spec.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js index a8d09202154..e224ed46d18 100644 --- a/spec/javascripts/datetime_utility_spec.js +++ b/spec/javascripts/datetime_utility_spec.js @@ -149,23 +149,22 @@ describe('getSundays', () => { }); }); -describe('getTimeframeWindow', () => { - it('returns array of dates representing a timeframe based on provided length and date', () => { - const date = new Date(2018, 0, 1); +describe('getTimeframeWindowFrom', () => { + it('returns array of date objects upto provided length start with provided startDate', () => { + const startDate = new Date(2018, 0, 1); const mockTimeframe = [ - new Date(2017, 9, 1), - new Date(2017, 10, 1), - new Date(2017, 11, 1), new Date(2018, 0, 1), new Date(2018, 1, 1), - new Date(2018, 2, 31), + new Date(2018, 2, 1), + new Date(2018, 3, 1), + new Date(2018, 4, 31), ]; - const timeframe = datetimeUtility.getTimeframeWindow(6, date); - - expect(timeframe.length).toBe(6); + const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5); + expect(timeframe.length).toBe(5); timeframe.forEach((timeframeItem, index) => { - expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBeTruthy(); - expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBeTruthy(); + console.log(timeframeItem); + expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBe(true); + expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBe(true); expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy(); }); }); -- cgit v1.2.1 From a6a900256e194c28d8342db9c2d67df784877c14 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jun 2018 17:27:26 +0200 Subject: Change update entrypoint instead of adding new keep-alive one --- spec/requests/api/runner_spec.rb | 84 +++++++++++++--------------------------- 1 file changed, 26 insertions(+), 58 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index c93612d7ada..57d238ff79b 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -816,6 +816,18 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(job.reload.trace.raw).to eq 'BUILD TRACE' end + + context 'when running state is sent' do + it 'updates update_at value' do + expect { update_job_after_time }.to change { job.reload.updated_at } + end + end + + context 'when other state is sent' do + it "doesn't update update_at value" do + expect { update_job_after_time(20.minutes, state: 'success') }.not_to change { job.reload.updated_at } + end + end end context 'when job has been erased' do @@ -838,6 +850,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do update_job(state: 'success', trace: 'BUILD TRACE UPDATED') expect(response).to have_gitlab_http_status(403) + expect(response.header['Job-Status']).to eq 'failed' expect(job.trace.raw).to eq 'Job failed' expect(job).to be_failed end @@ -847,66 +860,10 @@ describe API::Runner, :clean_gitlab_redis_shared_state do new_params = params.merge(token: token) put api("/jobs/#{job.id}"), new_params end - end - - describe 'POST /api/v4/jobs/:id/keep-alive' do - let(:job) { create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) } - let(:headers) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token, 'Content-Type' => 'text/plain' } } - let(:update_interval) { 30.seconds } - - it 'returns correct response' do - keep_alive_job - - expect(response.status).to eq 200 - expect(response.header).to have_key 'Job-Status' - end - - it 'updates updated_at value' do - expect { keep_alive_job }.to change { job.updated_at } - end - - context 'when project for the build has been deleted' do - let(:job) do - create(:ci_build, :running, :trace_live, runner_id: runner.id, pipeline: pipeline) do |job| - job.project.update(pending_delete: true) - end - end - - it 'responds with forbidden' do - keep_alive_job - - expect(response.status).to eq(403) - end - end - context 'when job has been canceled' do - before do - job.cancel - end - - it 'returns job-status=canceled header' do - keep_alive_job - - expect(response.status).to eq 200 - expect(response.header['Job-Status']).to eq('canceled') - end - end - - context 'when job has been errased' do - let(:job) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) } - - it 'rresponds with forbidden' do - keep_alive_job - - expect(response.status).to eq 403 - end - end - - def keep_alive_job(token = job.token, **params) - new_params = params.merge(token: token) + def update_job_after_time(update_interval = 20.minutes, state = 'running') Timecop.travel(job.updated_at + update_interval) do - post api("/jobs/#{job.id}/keep-alive"), new_params - job.reload + update_job(job.token, state: state) end end end @@ -1041,6 +998,17 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end end + + context 'when the job is canceled' do + before do + job.cancel + patch_the_trace + end + + it 'receives status in header' do + expect(response.header['Job-Status']).to eq 'canceled' + end + end end context 'when Runner makes a force-patch' do -- cgit v1.2.1 From d64ab8df18db156ff44fb3c9f4ecef529c1c28e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lu=C3=ADs?= Date: Thu, 7 Jun 2018 18:06:31 +0000 Subject: Resolve "Show `failure_reason` and improve failed jobs tab in pipeline detail view" --- spec/features/projects/pipelines/pipeline_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'spec') diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 35776a5f23b..ecc7cf84138 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -344,6 +344,16 @@ describe 'Pipeline', :js do it 'shows build failure logs' do expect(page).to have_content('4 examples, 1 failure') end + + it 'shows the failure reason' do + expect(page).to have_content('There is an unknown failure, please try again') + end + + it 'shows retry button for failed build' do + page.within(find('.build-failures', match: :first)) do + expect(page).to have_link('Retry') + end + end end context 'when missing build logs' do -- cgit v1.2.1 From 5370c442dfc0f1009b557cea9ce4dafbfe821569 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Thu, 7 Jun 2018 18:09:14 +0000 Subject: Resolve "Automatically provide a Deploy Token to projects when Auto DevOps is enabled" --- spec/factories/project_auto_devops.rb | 4 ++ spec/models/project_auto_devops_spec.rb | 93 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) (limited to 'spec') diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index 5ce1988c76f..0e8b507f9ce 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -3,5 +3,9 @@ FactoryBot.define do project enabled true domain "example.com" + + trait :disabled do + enabled false + end end end diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 7545c0797e9..4778bf4052b 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -71,4 +71,97 @@ describe ProjectAutoDevops do { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true } end end + + describe '#set_gitlab_deploy_token' do + let(:auto_devops) { build(:project_auto_devops, project: project) } + + context 'when the project is public' do + let(:project) { create(:project, :repository, :public) } + + it 'should not create a gitlab deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + + context 'when the project is internal' do + let(:project) { create(:project, :repository, :internal) } + + it 'should create a gitlab deploy token' do + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when the project is private' do + let(:project) { create(:project, :repository, :private) } + + it 'should create a gitlab deploy token' do + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when autodevops is enabled at project level' do + let(:project) { create(:project, :repository, :internal) } + let(:auto_devops) { build(:project_auto_devops, project: project) } + + it 'should create a deploy token' do + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when autodevops is enabled at instancel level' do + let(:project) { create(:project, :repository, :internal) } + let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) } + + it 'should create a deploy token' do + allow(Gitlab::CurrentSettings).to receive(:auto_devops_enabled?).and_return(true) + + expect do + auto_devops.save + end.to change { DeployToken.count }.by(1) + end + end + + context 'when autodevops is disabled' do + let(:project) { create(:project, :repository, :internal) } + let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) } + + it 'should not create a deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + + context 'when the project already has an active gitlab-deploy-token' do + let(:project) { create(:project, :repository, :internal) } + let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) } + let(:auto_devops) { build(:project_auto_devops, project: project) } + + it 'should not create a deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + + context 'when the project already has a revoked gitlab-deploy-token' do + let(:project) { create(:project, :repository, :internal) } + let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) } + let(:auto_devops) { build(:project_auto_devops, project: project) } + + it 'should not create a deploy token' do + expect do + auto_devops.save + end.not_to change { DeployToken.count } + end + end + end end -- cgit v1.2.1 From afe5d7d56ee771dac6e4a97d23e69d678c03da2d Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Mon, 28 May 2018 15:43:46 -0300 Subject: Apply notification settings level of groups to all child objects --- spec/models/group_spec.rb | 24 ++++ spec/models/notification_recipient_spec.rb | 44 +++++++ spec/services/notification_service_spec.rb | 188 ++++++++++++++++++++++++----- 3 files changed, 227 insertions(+), 29 deletions(-) (limited to 'spec') diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index f83b52e8975..ff35be4a8e3 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -67,6 +67,30 @@ describe Group do end end + describe '#notification_settings', :nested_groups do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:sub_group) { create(:group, parent_id: group.id) } + + before do + group.add_developer(user) + sub_group.add_developer(user) + end + + it 'also gets notification settings from parent groups' do + expect(sub_group.notification_settings.size).to eq(2) + expect(sub_group.notification_settings).to include(group.notification_settings.first) + end + + context 'when sub group is deleted' do + it 'does not delete parent notification settings' do + expect do + sub_group.destroy + end.to change { NotificationSetting.count }.by(-1) + end + end + end + describe '#visibility_level_allowed_by_parent' do let(:parent) { create(:group, :internal) } let(:sub_group) { build(:group, parent_id: parent.id) } diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb index eda0e1da835..13fe47799ed 100644 --- a/spec/models/notification_recipient_spec.rb +++ b/spec/models/notification_recipient_spec.rb @@ -13,4 +13,48 @@ describe NotificationRecipient do expect(recipient.has_access?).to be_falsy end + + context '#notification_setting' do + context 'for child groups', :nested_groups do + let!(:moved_group) { create(:group) } + let(:group) { create(:group) } + let(:sub_group_1) { create(:group, parent: group) } + let(:sub_group_2) { create(:group, parent: sub_group_1) } + let(:project) { create(:project, namespace: moved_group) } + + before do + sub_group_2.add_owner(user) + moved_group.add_owner(user) + Groups::TransferService.new(moved_group, user).execute(sub_group_2) + + moved_group.reload + end + + context 'when notification setting is global' do + before do + user.notification_settings_for(group).global! + user.notification_settings_for(sub_group_1).mention! + user.notification_settings_for(sub_group_2).global! + user.notification_settings_for(moved_group).global! + end + + it 'considers notification setting from the first parent without global setting' do + expect(subject.notification_setting.source).to eq(sub_group_1) + end + end + + context 'when notification setting is not global' do + before do + user.notification_settings_for(group).global! + user.notification_settings_for(sub_group_1).mention! + user.notification_settings_for(sub_group_2).watch! + user.notification_settings_for(moved_group).disabled! + end + + it 'considers notification setting from lowest group member in hierarchy' do + expect(subject.notification_setting.source).to eq(moved_group) + end + end + end + end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 831678b949d..0eadc83bfe3 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -59,6 +59,20 @@ describe NotificationService, :mailer do should_email(participant) end + + context 'for subgroups', :nested_groups do + before do + build_group(project) + end + + it 'emails the participant' do + create(:note_on_issue, noteable: issuable, project_id: project.id, note: 'anything', author: @pg_participant) + + notification_trigger + + should_email_nested_group_user(@pg_participant) + end + end end shared_examples 'participating by assignee notification' do @@ -239,34 +253,56 @@ describe NotificationService, :mailer do end describe 'new note on issue in project that belongs to a group' do - let(:group) { create(:group) } - before do note.project.namespace_id = group.id - note.project.group.add_user(@u_watcher, GroupMember::MASTER) - note.project.group.add_user(@u_custom_global, GroupMember::MASTER) + group.add_user(@u_watcher, GroupMember::MASTER) + group.add_user(@u_custom_global, GroupMember::MASTER) note.project.save @u_watcher.notification_settings_for(note.project).participating! - @u_watcher.notification_settings_for(note.project.group).global! + @u_watcher.notification_settings_for(group).global! update_custom_notification(:new_note, @u_custom_global) reset_delivered_emails! end - it do - notification.new_note(note) + shared_examples 'new note notifications' do + it do + notification.new_note(note) + + should_email(note.noteable.author) + should_email(note.noteable.assignees.first) + should_email(@u_mentioned) + should_email(@u_custom_global) + should_not_email(@u_guest_custom) + should_not_email(@u_guest_watcher) + should_not_email(@u_watcher) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) + should_not_email(@u_lazy_participant) + end + end - should_email(note.noteable.author) - should_email(note.noteable.assignees.first) - should_email(@u_mentioned) - should_email(@u_custom_global) - should_not_email(@u_guest_custom) - should_not_email(@u_guest_watcher) - should_not_email(@u_watcher) - should_not_email(note.author) - should_not_email(@u_participating) - should_not_email(@u_disabled) - should_not_email(@u_lazy_participant) + let(:group) { create(:group) } + + it_behaves_like 'new note notifications' + + context 'which is a subgroup', :nested_groups do + let!(:parent) { create(:group) } + let!(:group) { create(:group, parent: parent) } + + it_behaves_like 'new note notifications' + + it 'overrides child objects with global level' do + user = create(:user) + parent.add_developer(user) + user.notification_settings_for(parent).watch! + reset_delivered_emails! + + notification.new_note(note) + + should_email(user) + end end end end @@ -301,6 +337,31 @@ describe NotificationService, :mailer do should_email(member) should_email(admin) end + + context 'on project that belongs to subgroup', :nested_groups do + let(:group_reporter) { create(:user) } + let(:group_guest) { create(:user) } + let(:parent_group) { create(:group) } + let(:child_group) { create(:group, parent: parent_group) } + let(:project) { create(:project, namespace: child_group) } + + context 'when user is group guest member' do + before do + parent_group.add_reporter(group_reporter) + parent_group.add_guest(group_guest) + group_guest.notification_settings_for(parent_group).watch! + group_reporter.notification_settings_for(parent_group).watch! + reset_delivered_emails! + end + + it 'does not email guest user' do + notification.new_note(note) + + should_email(group_reporter) + should_not_email(group_guest) + end + end + end end context 'issue note mention' do @@ -311,6 +372,7 @@ describe NotificationService, :mailer do before do build_team(note.project) + build_group(note.project) note.project.add_master(note.author) add_users_with_subscription(note.project, issue) reset_delivered_emails! @@ -336,10 +398,20 @@ describe NotificationService, :mailer do should_email(@u_guest_watcher) should_email(note.noteable.author) should_email(note.noteable.assignees.first) - should_not_email(note.author) + should_email_nested_group_user(@pg_watcher) should_email(@u_mentioned) - should_not_email(@u_disabled) should_email(@u_not_mentioned) + should_not_email(note.author) + should_not_email(@u_disabled) + should_not_email_nested_group_user(@pg_disabled) + end + + it 'notifies parent group members with mention level', :nested_groups do + note = create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: "@#{@pg_mention.username}") + + notification.new_note(note) + + should_email_nested_group_user(@pg_mention) end it 'filters out "mentioned in" notes' do @@ -352,17 +424,18 @@ describe NotificationService, :mailer do end context 'project snippet note' do - let(:project) { create(:project, :public) } + let!(:project) { create(:project, :public) } let(:snippet) { create(:project_snippet, project: project, author: create(:user)) } - let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: snippet.project.id, note: '@all mentioned') } + let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: project.id, note: '@all mentioned') } before do - build_team(note.project) + build_team(project) + build_group(project) # make sure these users can read the project snippet! project.add_guest(@u_guest_watcher) project.add_guest(@u_guest_custom) - + add_member_for_parent_group(@pg_watcher, project) note.project.add_master(note.author) reset_delivered_emails! end @@ -370,7 +443,6 @@ describe NotificationService, :mailer do describe '#new_note' do it 'notifies the team members' do notification.new_note(note) - # Notify all team members note.project.team.members.each do |member| # User with disabled notification should not be notified @@ -449,6 +521,7 @@ describe NotificationService, :mailer do before do build_team(note.project) + build_group(project) reset_delivered_emails! allow(note.noteable).to receive(:author).and_return(@u_committer) update_custom_notification(:new_note, @u_guest_custom, resource: project) @@ -463,11 +536,13 @@ describe NotificationService, :mailer do should_email(@u_guest_custom) should_email(@u_committer) should_email(@u_watcher) + should_email_nested_group_user(@pg_watcher) should_not_email(@u_mentioned) should_not_email(note.author) should_not_email(@u_participating) should_not_email(@u_disabled) should_not_email(@u_lazy_participant) + should_not_email_nested_group_user(@pg_disabled) end it do @@ -478,10 +553,12 @@ describe NotificationService, :mailer do should_email(@u_committer) should_email(@u_watcher) should_email(@u_mentioned) + should_email_nested_group_user(@pg_watcher) should_not_email(note.author) should_not_email(@u_participating) should_not_email(@u_disabled) should_not_email(@u_lazy_participant) + should_not_email_nested_group_user(@pg_disabled) end it do @@ -548,10 +625,13 @@ describe NotificationService, :mailer do should_email(@g_global_watcher) should_email(@g_watcher) should_email(@unsubscribed_mentioned) + should_email_nested_group_user(@pg_watcher) should_not_email(@u_mentioned) should_not_email(@u_participating) should_not_email(@u_disabled) should_not_email(@u_lazy_participant) + should_not_email_nested_group_user(@pg_disabled) + should_not_email_nested_group_user(@pg_mention) end it do @@ -1922,19 +2002,69 @@ describe NotificationService, :mailer do # Users in the project's group but not part of project's team # with different notification settings def build_group(project) - group = create(:group, :public) - project.group = group + group = create_nested_group + project.update(namespace_id: group.id) # Group member: global=disabled, group=watch - @g_watcher = create_user_with_notification(:watch, 'group_watcher', project.group) + @g_watcher ||= create_user_with_notification(:watch, 'group_watcher', project.group) @g_watcher.notification_settings_for(nil).disabled! # Group member: global=watch, group=global - @g_global_watcher = create_global_setting_for(create(:user), :watch) + @g_global_watcher ||= create_global_setting_for(create(:user), :watch) group.add_users([@g_watcher, @g_global_watcher], :master) + group end + # Creates a nested group only if supported + # to avoid errors on MySQL + def create_nested_group + if Group.supports_nested_groups? + parent_group = create(:group, :public) + child_group = create(:group, :public, parent: parent_group) + + # Parent group member: global=disabled, parent_group=watch, child_group=global + @pg_watcher ||= create_user_with_notification(:watch, 'parent_group_watcher', parent_group) + @pg_watcher.notification_settings_for(nil).disabled! + + # Parent group member: global=global, parent_group=disabled, child_group=global + @pg_disabled ||= create_user_with_notification(:disabled, 'parent_group_disabled', parent_group) + @pg_disabled.notification_settings_for(nil).global! + + # Parent group member: global=global, parent_group=mention, child_group=global + @pg_mention ||= create_user_with_notification(:mention, 'parent_group_mention', parent_group) + @pg_mention.notification_settings_for(nil).global! + + # Parent group member: global=global, parent_group=participating, child_group=global + @pg_participant ||= create_user_with_notification(:participating, 'parent_group_participant', parent_group) + @pg_mention.notification_settings_for(nil).global! + + child_group + else + create(:group, :public) + end + end + + def add_member_for_parent_group(user, project) + return unless Group.supports_nested_groups? + + project.reload + + project.group.parent.add_master(user) + end + + def should_email_nested_group_user(user, times: 1, recipients: email_recipients) + return unless Group.supports_nested_groups? + + should_email(user, times: 1, recipients: email_recipients) + end + + def should_not_email_nested_group_user(user, recipients: email_recipients) + return unless Group.supports_nested_groups? + + should_not_email(user, recipients: email_recipients) + end + def add_users_with_subscription(project, issuable) @subscriber = create :user @unsubscriber = create :user -- cgit v1.2.1 From b00b861863bd963224593846791ea0b6993d9b88 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 7 Jun 2018 15:46:25 -0500 Subject: Refactor schema_spec.rb for CE-EE parity and simplicity --- spec/migrations/active_record/schema_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/migrations/active_record/schema_spec.rb b/spec/migrations/active_record/schema_spec.rb index e132529d8d8..9d35b3cd642 100644 --- a/spec/migrations/active_record/schema_spec.rb +++ b/spec/migrations/active_record/schema_spec.rb @@ -5,7 +5,11 @@ require 'spec_helper' describe ActiveRecord::Schema do let(:latest_migration_timestamp) do - migrations = Dir[Rails.root.join('db', 'migrate', '*'), Rails.root.join('db', 'post_migrate', '*')] + migrations_paths = %w[db ee/db] + .product(%w[migrate post_migrate]) + .map { |path| Rails.root.join(*path, '*') } + + migrations = Dir[*migrations_paths] migrations.map { |migration| File.basename(migration).split('_').first.to_i }.max end -- cgit v1.2.1 From cf41aaba5ab9fb1d229807f77b2b77585d3550b0 Mon Sep 17 00:00:00 2001 From: Mario de la Ossa Date: Thu, 7 Jun 2018 20:54:24 +0000 Subject: Backport of "Add assignee lists to boards" --- spec/features/boards/boards_spec.rb | 2 +- spec/features/issues/form_spec.rb | 3 +++ spec/finders/group_members_finder_spec.rb | 12 ++++++++++++ spec/finders/members_finder_spec.rb | 12 ++++++++++++ spec/fixtures/api/schemas/list.json | 2 +- spec/javascripts/boards/board_card_spec.js | 2 +- spec/javascripts/boards/boards_store_spec.js | 2 +- spec/javascripts/boards/issue_card_spec.js | 2 +- spec/javascripts/boards/issue_spec.js | 2 +- spec/javascripts/boards/list_spec.js | 2 +- spec/javascripts/boards/modal_store_spec.js | 2 +- 11 files changed, 35 insertions(+), 8 deletions(-) (limited to 'spec') diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index e414345ac23..f6e0dee28c6 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -150,7 +150,7 @@ describe 'Issue Boards', :js do click_button 'Add list' wait_for_requests - find('.dropdown-menu-close').click + find('.js-new-board-list').click page.within(find('.board:nth-child(2)')) do accept_confirm { find('.board-delete').click } diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 4625a50b8d9..2cb3ae08b0e 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -143,6 +143,9 @@ describe 'New/edit issue', :js do click_link label.title click_link label2.title end + + find('.js-issuable-form-dropdown.js-label-select').click + page.within '.js-label-select' do expect(page).to have_content label.title end diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb index 9f285e28535..63e15b365a4 100644 --- a/spec/finders/group_members_finder_spec.rb +++ b/spec/finders/group_members_finder_spec.rb @@ -29,4 +29,16 @@ describe GroupMembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member3, member4]) end + + it 'returns members for descendant groups if requested', :nested_groups do + member1 = group.add_master(user2) + member2 = group.add_master(user1) + nested_group.add_master(user2) + member3 = nested_group.add_master(user3) + member4 = nested_group.add_master(user4) + + result = described_class.new(group).execute(include_descendants: true) + + expect(result.to_a).to match_array([member1, member2, member3, member4]) + end end diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb index 7bb1f45322e..2fc5299b0f4 100644 --- a/spec/finders/members_finder_spec.rb +++ b/spec/finders/members_finder_spec.rb @@ -19,4 +19,16 @@ describe MembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member2, member3]) end + + it 'includes nested group members if asked', :nested_groups do + project = create(:project, namespace: group) + nested_group.request_access(user1) + member1 = group.add_master(user2) + member2 = nested_group.add_master(user3) + member3 = project.add_master(user4) + + result = described_class.new(project, user2).execute(include_descendants: true) + + expect(result.to_a).to match_array([member1, member2, member3]) + end end diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json index 05922df6b81..b76ec115293 100644 --- a/spec/fixtures/api/schemas/list.json +++ b/spec/fixtures/api/schemas/list.json @@ -37,5 +37,5 @@ "title": { "type": "string" }, "position": { "type": ["integer", "null"] } }, - "additionalProperties": false + "additionalProperties": true } diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js index 9b4db774b63..ad263791cd4 100644 --- a/spec/javascripts/boards/board_card_spec.js +++ b/spec/javascripts/boards/board_card_spec.js @@ -5,10 +5,10 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; -import '~/boards/models/assignee'; import eventHub from '~/boards/eventhub'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/list'; import '~/boards/stores/boards_store'; import boardCard from '~/boards/components/board_card.vue'; diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js index 46fa10e1789..3f5ed4f3d07 100644 --- a/spec/javascripts/boards/boards_store_spec.js +++ b/spec/javascripts/boards/boards_store_spec.js @@ -7,9 +7,9 @@ import axios from '~/lib/utils/axios_utils'; import Cookies from 'js-cookie'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/services/board_service'; import '~/boards/stores/boards_store'; import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data'; diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index abeef272c68..05acf903933 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -5,9 +5,9 @@ import Vue from 'vue'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/stores/boards_store'; import '~/boards/components/issue_card_inner'; import { listObj } from './mock_data'; diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js index d90f9a41231..db68096e3bd 100644 --- a/spec/javascripts/boards/issue_spec.js +++ b/spec/javascripts/boards/issue_spec.js @@ -3,9 +3,9 @@ import Vue from 'vue'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/services/board_service'; import '~/boards/stores/boards_store'; import { mockBoardService } from './mock_data'; diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js index d5d1139de15..ac8bbb8f2a8 100644 --- a/spec/javascripts/boards/list_spec.js +++ b/spec/javascripts/boards/list_spec.js @@ -6,9 +6,9 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import _ from 'underscore'; import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import '~/boards/services/board_service'; import '~/boards/stores/boards_store'; import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data'; diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js index 797693a21aa..a234c81fadf 100644 --- a/spec/javascripts/boards/modal_store_spec.js +++ b/spec/javascripts/boards/modal_store_spec.js @@ -1,9 +1,9 @@ /* global ListIssue */ import '~/vue_shared/models/label'; +import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; -import '~/boards/models/assignee'; import Store from '~/boards/stores/modal_store'; describe('Modal store', () => { -- cgit v1.2.1 From ebf5f31d8c7e3381c16899ba8e64e569d3dfd3a3 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 8 Jun 2018 04:27:36 +0000 Subject: Set tooltips to have default boundary viewport --- spec/support/features/reportable_note_shared_examples.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb index 836e5e7be23..b4c71d69119 100644 --- a/spec/support/features/reportable_note_shared_examples.rb +++ b/spec/support/features/reportable_note_shared_examples.rb @@ -1,6 +1,7 @@ require 'spec_helper' shared_examples 'reportable note' do |type| + include MobileHelpers include NotesHelper let(:comment) { find("##{ActionView::RecordIdentifier.dom_id(note)}") } @@ -39,6 +40,9 @@ shared_examples 'reportable note' do |type| end def open_dropdown(dropdown) + # make window wide enough that tooltip doesn't trigger horizontal scrollbar + resize_window(1200, 800) + dropdown.find('.more-actions-toggle').click dropdown.find('.dropdown-menu li', match: :first) end -- cgit v1.2.1 From 2729205b391c22eb3a15408580261456cc030530 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 7 Jun 2018 16:34:50 -0500 Subject: remove monacoLoader and import monaco directly within Editor class --- .../javascripts/ide/components/repo_editor_spec.js | 6 +----- .../ide/lib/common/model_manager_spec.js | 11 +++-------- spec/javascripts/ide/lib/common/model_spec.js | 17 ++++++---------- .../ide/lib/decorations/controller_spec.js | 19 +++++++----------- spec/javascripts/ide/lib/diff/controller_spec.js | 23 +++++++++------------- spec/javascripts/ide/lib/editor_spec.js | 16 +++++---------- spec/javascripts/ide/monaco_loader_spec.js | 15 -------------- 7 files changed, 31 insertions(+), 76 deletions(-) delete mode 100644 spec/javascripts/ide/monaco_loader_spec.js (limited to 'spec') diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index d3f80e6f9c0..d318521d0a0 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -3,7 +3,6 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import store from '~/ide/stores'; import repoEditor from '~/ide/components/repo_editor.vue'; -import monacoLoader from '~/ide/monaco_loader'; import Editor from '~/ide/lib/editor'; import { activityBarViews } from '~/ide/constants'; import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; @@ -25,13 +24,10 @@ describe('RepoEditor', () => { f.tempFile = true; vm.$store.state.openFiles.push(f); Vue.set(vm.$store.state.entries, f.path, f); - vm.monaco = true; vm.$mount(); - monacoLoader(['vs/editor/editor.main'], () => { - setTimeout(done, 0); - }); + Vue.nextTick(() => setTimeout(done)); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/common/model_manager_spec.js b/spec/javascripts/ide/lib/common/model_manager_spec.js index c00d590c580..e3886f06f5a 100644 --- a/spec/javascripts/ide/lib/common/model_manager_spec.js +++ b/spec/javascripts/ide/lib/common/model_manager_spec.js @@ -1,18 +1,13 @@ -/* global monaco */ +import * as monaco from 'monaco-editor'; import eventHub from '~/ide/eventhub'; -import monacoLoader from '~/ide/monaco_loader'; import ModelManager from '~/ide/lib/common/model_manager'; import { file } from '../../helpers'; describe('Multi-file editor library model manager', () => { let instance; - beforeEach(done => { - monacoLoader(['vs/editor/editor.main'], () => { - instance = new ModelManager(monaco); - - done(); - }); + beforeEach(() => { + instance = new ModelManager(monaco); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/common/model_spec.js b/spec/javascripts/ide/lib/common/model_spec.js index c278bf92b08..ab753a7e5e2 100644 --- a/spec/javascripts/ide/lib/common/model_spec.js +++ b/spec/javascripts/ide/lib/common/model_spec.js @@ -1,23 +1,18 @@ -/* global monaco */ +import * as monaco from 'monaco-editor'; import eventHub from '~/ide/eventhub'; -import monacoLoader from '~/ide/monaco_loader'; import Model from '~/ide/lib/common/model'; import { file } from '../../helpers'; describe('Multi-file editor library model', () => { let model; - beforeEach(done => { + beforeEach(() => { spyOn(eventHub, '$on').and.callThrough(); - monacoLoader(['vs/editor/editor.main'], () => { - const f = file('path'); - f.mrChange = { diff: 'ABC' }; - f.baseRaw = 'test'; - model = new Model(monaco, f); - - done(); - }); + const f = file('path'); + f.mrChange = { diff: 'ABC' }; + f.baseRaw = 'test'; + model = new Model(monaco, f); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/decorations/controller_spec.js b/spec/javascripts/ide/lib/decorations/controller_spec.js index e1c4ca570b6..6f50056be73 100644 --- a/spec/javascripts/ide/lib/decorations/controller_spec.js +++ b/spec/javascripts/ide/lib/decorations/controller_spec.js @@ -1,6 +1,5 @@ -/* global monaco */ -import monacoLoader from '~/ide/monaco_loader'; -import editor from '~/ide/lib/editor'; +import * as monaco from 'monaco-editor'; +import Editor from '~/ide/lib/editor'; import DecorationsController from '~/ide/lib/decorations/controller'; import Model from '~/ide/lib/common/model'; import { file } from '../../helpers'; @@ -10,16 +9,12 @@ describe('Multi-file editor library decorations controller', () => { let controller; let model; - beforeEach(done => { - monacoLoader(['vs/editor/editor.main'], () => { - editorInstance = editor.create(monaco); - editorInstance.createInstance(document.createElement('div')); + beforeEach(() => { + editorInstance = Editor.create(); + editorInstance.createInstance(document.createElement('div')); - controller = new DecorationsController(editorInstance); - model = new Model(monaco, file('path')); - - done(); - }); + controller = new DecorationsController(editorInstance); + model = new Model(monaco, file('path')); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js index fd8ab3b4f1d..2438fb22036 100644 --- a/spec/javascripts/ide/lib/diff/controller_spec.js +++ b/spec/javascripts/ide/lib/diff/controller_spec.js @@ -1,6 +1,5 @@ -/* global monaco */ -import monacoLoader from '~/ide/monaco_loader'; -import editor from '~/ide/lib/editor'; +import * as monaco from 'monaco-editor'; +import Editor from '~/ide/lib/editor'; import ModelManager from '~/ide/lib/common/model_manager'; import DecorationsController from '~/ide/lib/decorations/controller'; import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller'; @@ -14,20 +13,16 @@ describe('Multi-file editor library dirty diff controller', () => { let decorationsController; let model; - beforeEach(done => { - monacoLoader(['vs/editor/editor.main'], () => { - editorInstance = editor.create(monaco); - editorInstance.createInstance(document.createElement('div')); + beforeEach(() => { + editorInstance = Editor.create(); + editorInstance.createInstance(document.createElement('div')); - modelManager = new ModelManager(monaco); - decorationsController = new DecorationsController(editorInstance); + modelManager = new ModelManager(monaco); + decorationsController = new DecorationsController(editorInstance); - model = modelManager.addModel(file('path')); + model = modelManager.addModel(file('path')); - controller = new DirtyDiffController(modelManager, decorationsController); - - done(); - }); + controller = new DirtyDiffController(modelManager, decorationsController); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js index b88a12264ca..cf867d021e6 100644 --- a/spec/javascripts/ide/lib/editor_spec.js +++ b/spec/javascripts/ide/lib/editor_spec.js @@ -1,6 +1,4 @@ -/* global monaco */ -import monacoLoader from '~/ide/monaco_loader'; -import editor from '~/ide/lib/editor'; +import Editor from '~/ide/lib/editor'; import { file } from '../helpers'; describe('Multi-file editor library', () => { @@ -8,18 +6,14 @@ describe('Multi-file editor library', () => { let el; let holder; - beforeEach(done => { + beforeEach(() => { el = document.createElement('div'); holder = document.createElement('div'); el.appendChild(holder); document.body.appendChild(el); - monacoLoader(['vs/editor/editor.main'], () => { - instance = editor.create(monaco); - - done(); - }); + instance = Editor.create(); }); afterEach(() => { @@ -29,11 +23,11 @@ describe('Multi-file editor library', () => { }); it('creates instance of editor', () => { - expect(editor.editorInstance).not.toBeNull(); + expect(Editor.editorInstance).not.toBeNull(); }); it('creates instance returns cached instance', () => { - expect(editor.create(monaco)).toEqual(instance); + expect(Editor.create()).toEqual(instance); }); describe('createInstance', () => { diff --git a/spec/javascripts/ide/monaco_loader_spec.js b/spec/javascripts/ide/monaco_loader_spec.js deleted file mode 100644 index 7ab315aa8c8..00000000000 --- a/spec/javascripts/ide/monaco_loader_spec.js +++ /dev/null @@ -1,15 +0,0 @@ -import monacoContext from 'monaco-editor/dev/vs/loader'; -import monacoLoader from '~/ide/monaco_loader'; - -describe('MonacoLoader', () => { - it('calls require.config and exports require', () => { - expect(monacoContext.require.getConfig()).toEqual( - jasmine.objectContaining({ - paths: { - vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase - }, - }), - ); - expect(monacoLoader).toBe(monacoContext.require); - }); -}); -- cgit v1.2.1 From 15993df1d6849bd487855cf4926e2e01e44e54df Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 7 Jun 2018 23:53:45 -0500 Subject: remove need to pass monaco around through DI --- spec/javascripts/ide/lib/common/model_manager_spec.js | 3 +-- spec/javascripts/ide/lib/common/model_spec.js | 5 ++--- spec/javascripts/ide/lib/decorations/controller_spec.js | 3 +-- spec/javascripts/ide/lib/diff/controller_spec.js | 6 +++--- 4 files changed, 7 insertions(+), 10 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/lib/common/model_manager_spec.js b/spec/javascripts/ide/lib/common/model_manager_spec.js index e3886f06f5a..38ffa317e8e 100644 --- a/spec/javascripts/ide/lib/common/model_manager_spec.js +++ b/spec/javascripts/ide/lib/common/model_manager_spec.js @@ -1,4 +1,3 @@ -import * as monaco from 'monaco-editor'; import eventHub from '~/ide/eventhub'; import ModelManager from '~/ide/lib/common/model_manager'; import { file } from '../../helpers'; @@ -7,7 +6,7 @@ describe('Multi-file editor library model manager', () => { let instance; beforeEach(() => { - instance = new ModelManager(monaco); + instance = new ModelManager(); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/common/model_spec.js b/spec/javascripts/ide/lib/common/model_spec.js index ab753a7e5e2..f096e06f43c 100644 --- a/spec/javascripts/ide/lib/common/model_spec.js +++ b/spec/javascripts/ide/lib/common/model_spec.js @@ -1,4 +1,3 @@ -import * as monaco from 'monaco-editor'; import eventHub from '~/ide/eventhub'; import Model from '~/ide/lib/common/model'; import { file } from '../../helpers'; @@ -12,7 +11,7 @@ describe('Multi-file editor library model', () => { const f = file('path'); f.mrChange = { diff: 'ABC' }; f.baseRaw = 'test'; - model = new Model(monaco, f); + model = new Model(f); }); afterEach(() => { @@ -33,7 +32,7 @@ describe('Multi-file editor library model', () => { const f = file('path'); model.dispose(); - model = new Model(monaco, f, { + model = new Model(f, { ...f, content: '123 testing', }); diff --git a/spec/javascripts/ide/lib/decorations/controller_spec.js b/spec/javascripts/ide/lib/decorations/controller_spec.js index 6f50056be73..a112361e0d1 100644 --- a/spec/javascripts/ide/lib/decorations/controller_spec.js +++ b/spec/javascripts/ide/lib/decorations/controller_spec.js @@ -1,4 +1,3 @@ -import * as monaco from 'monaco-editor'; import Editor from '~/ide/lib/editor'; import DecorationsController from '~/ide/lib/decorations/controller'; import Model from '~/ide/lib/common/model'; @@ -14,7 +13,7 @@ describe('Multi-file editor library decorations controller', () => { editorInstance.createInstance(document.createElement('div')); controller = new DecorationsController(editorInstance); - model = new Model(monaco, file('path')); + model = new Model(file('path')); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js index 2438fb22036..96abd1dcd9e 100644 --- a/spec/javascripts/ide/lib/diff/controller_spec.js +++ b/spec/javascripts/ide/lib/diff/controller_spec.js @@ -1,4 +1,4 @@ -import * as monaco from 'monaco-editor'; +import { Range } from 'monaco-editor'; import Editor from '~/ide/lib/editor'; import ModelManager from '~/ide/lib/common/model_manager'; import DecorationsController from '~/ide/lib/decorations/controller'; @@ -17,7 +17,7 @@ describe('Multi-file editor library dirty diff controller', () => { editorInstance = Editor.create(); editorInstance.createInstance(document.createElement('div')); - modelManager = new ModelManager(monaco); + modelManager = new ModelManager(); decorationsController = new DecorationsController(editorInstance); model = modelManager.addModel(file('path')); @@ -165,7 +165,7 @@ describe('Multi-file editor library dirty diff controller', () => { [], [ { - range: new monaco.Range(1, 1, 1, 1), + range: new Range(1, 1, 1, 1), options: { isWholeLine: true, linesDecorationsClassName: 'dirty-diff dirty-diff-modified', -- cgit v1.2.1 From 4653820d37f89c2fabe74800ccce2d1f8420a7da Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 8 Jun 2018 00:09:59 -0500 Subject: refactor monaco-editor import and stop storing as class property within editor --- spec/javascripts/ide/lib/editor_spec.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js index cf867d021e6..c1932284d53 100644 --- a/spec/javascripts/ide/lib/editor_spec.js +++ b/spec/javascripts/ide/lib/editor_spec.js @@ -1,3 +1,4 @@ +import { editor as monacoEditor } from 'monaco-editor'; import Editor from '~/ide/lib/editor'; import { file } from '../helpers'; @@ -32,11 +33,11 @@ describe('Multi-file editor library', () => { describe('createInstance', () => { it('creates editor instance', () => { - spyOn(instance.monaco.editor, 'create').and.callThrough(); + spyOn(monacoEditor, 'create').and.callThrough(); instance.createInstance(holder); - expect(instance.monaco.editor.create).toHaveBeenCalled(); + expect(monacoEditor.create).toHaveBeenCalled(); }); it('creates dirty diff controller', () => { @@ -54,11 +55,11 @@ describe('Multi-file editor library', () => { describe('createDiffInstance', () => { it('creates editor instance', () => { - spyOn(instance.monaco.editor, 'createDiffEditor').and.callThrough(); + spyOn(monacoEditor, 'createDiffEditor').and.callThrough(); instance.createDiffInstance(holder); - expect(instance.monaco.editor.createDiffEditor).toHaveBeenCalledWith(holder, { + expect(monacoEditor.createDiffEditor).toHaveBeenCalledWith(holder, { model: null, contextmenu: true, minimap: { -- cgit v1.2.1 From a904314941d42c41cab7760a154a1fcb3585dc27 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 8 Jun 2018 12:03:21 +0300 Subject: Use nip.io instead of xip.io for jupyter domain suggestion Signed-off-by: Dmitriy Zaporozhets --- spec/javascripts/clusters/stores/clusters_store_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 6854b016852..9e43552f740 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -110,7 +110,7 @@ describe('Clusters Store', () => { expect( store.state.applications.jupyter.hostname, - ).toEqual(`jupyter.${store.state.applications.ingress.externalIp}.xip.io`); + ).toEqual(`jupyter.${store.state.applications.ingress.externalIp}.nip.io`); }); }); }); -- cgit v1.2.1 From f413c4dd267f3a5906bf6c5b3c4eb3b45f8ac60a Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 8 Jun 2018 11:45:36 +0100 Subject: Fix NotificationRecipientService spec for EE EE checks a license, which needs RequestStore enabled to avoid N+1 queries. However, enabling RequestStore causes Gitaly to complain about N+1 invocations, which we really don't care about here. --- spec/services/notification_recipient_service_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb index 340d4585e0c..7f536ce4e68 100644 --- a/spec/services/notification_recipient_service_spec.rb +++ b/spec/services/notification_recipient_service_spec.rb @@ -19,8 +19,8 @@ describe NotificationRecipientService do end end - it 'avoids N+1 queries' do - create_watcher + it 'avoids N+1 queries', :request_store do + Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher } service.build_new_note_recipients(note) @@ -28,7 +28,7 @@ describe NotificationRecipientService do service.build_new_note_recipients(note) end - create_watcher + Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher } expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count) end -- cgit v1.2.1 From c22e51e8c1372e844aa3c4447ebea371d77d0c89 Mon Sep 17 00:00:00 2001 From: Athar Hameed Date: Fri, 8 Jun 2018 12:28:43 +0000 Subject: Resolve "Automatically created MR uses wrong target branch (when branching from Tag)" --- .../services/merge_requests/create_from_issue_service_spec.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb index 38d84cf0ceb..b1882df732d 100644 --- a/spec/services/merge_requests/create_from_issue_service_spec.rb +++ b/spec/services/merge_requests/create_from_issue_service_spec.rb @@ -125,9 +125,14 @@ describe MergeRequests::CreateFromIssueService do end context 'when ref branch does not exist' do - it 'does not create a merge request' do - expect { described_class.new(project, user, issue_iid: issue.iid, ref: 'nobr').execute } - .not_to change { project.merge_requests.count } + subject { described_class.new(project, user, issue_iid: issue.iid, ref: 'no-such-branch').execute } + + it 'creates a merge request' do + expect { subject }.to change(project.merge_requests, :count).by(1) + end + + it 'sets the merge request target branch to the project default branch' do + expect(subject[:merge_request].target_branch).to eq(project.default_branch) end end end -- cgit v1.2.1 From 68cb1c2651fde0ae009beed10f81a74172ceeab1 Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Fri, 8 Jun 2018 13:12:51 +0000 Subject: Revert rename allow collaboration column --- spec/lib/gitlab/import_export/safe_model_attributes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 4354dca25ea..0a1e3eb83d3 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -170,7 +170,7 @@ MergeRequest: - last_edited_by_id - head_pipeline_id - discussion_locked -- allow_collaboration +- allow_maintainer_to_push MergeRequestDiff: - id - state -- cgit v1.2.1 From e1589a5c584acae83d97d41494616be1f3981da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mica=C3=ABl=20Bergeron?= Date: Fri, 8 Jun 2018 10:51:59 -0400 Subject: apply feedback --- spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb index ba01cfe53c5..31d323626c5 100644 --- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb +++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb @@ -129,7 +129,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do describe "limits N+1 queries" do let!(:projects) { create_list(:project, 10, :with_avatar) } - it do + it "to N*#{MIGRATION_QUERIES}" do query_count = ActiveRecord::QueryRecorder.new { perform(uploads) } more_projects = create_list(:project, 100, :with_avatar) @@ -158,7 +158,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do describe "limits N+1 queries" do let!(:projects) { create_list(:project, 10) } - it do + it "to N*#{MIGRATION_QUERIES}" do query_count = ActiveRecord::QueryRecorder.new { perform(uploads) } more_projects = create_list(:project, 100) -- cgit v1.2.1 From 3d713ac114085e091815aa486fb96905347c3002 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Fri, 8 Jun 2018 13:20:44 +0200 Subject: Users can accept terms during registration When a user checks the `accept` checkbox, we will track that acceptance as usual. That way they don't need to accept again after they complete the registration. When an unauthenticated user visits the `/-/users/terms` page, there is no button to accept, decline or continue. The 'current-user menu' is also hidden from the top bar. --- spec/controllers/registrations_controller_spec.rb | 21 ++++ spec/features/admin/admin_settings_spec.rb | 2 +- spec/features/users/signup_spec.rb | 21 +++- spec/features/users/terms_spec.rb | 143 ++++++++++++---------- 4 files changed, 118 insertions(+), 69 deletions(-) (limited to 'spec') diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index 346944fd5b0..898f3863008 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe RegistrationsController do + include TermsHelper + describe '#create' do let(:user_params) { { user: { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } } } @@ -67,6 +69,25 @@ describe RegistrationsController do expect(flash[:notice]).to include 'Welcome! You have signed up successfully.' end end + + context 'when terms are enforced' do + before do + enforce_terms + end + + it 'redirects back with a notice when the checkbox was not checked' do + post :create, user_params + + expect(flash[:alert]).to match /you must accept our terms/i + end + + it 'creates the user with agreement when terms are accepted' do + post :create, user_params.merge(terms_opt_in: '1') + + expect(subject.current_user).to be_present + expect(subject.current_user.terms_accepted?).to be(true) + end + end end describe '#destroy' do diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index dc025d82937..e7aca94db66 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -94,7 +94,7 @@ feature 'Admin updates settings' do accept_terms(admin) page.within('.as-terms') do - check 'Require all users to accept Terms of Service when they access GitLab.' + check 'Require all users to accept Terms of Service and Privacy Policy when they access GitLab.' fill_in 'Terms of Service Agreement', with: 'Be nice!' click_button 'Save changes' end diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index b5bd5c505f2..b51ca5d130b 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -140,7 +140,7 @@ describe 'Signup' do enforce_terms end - it 'asks the user to accept terms before going to the dashboard' do + it 'requires the user to check the checkbox' do visit root_path fill_in 'new_user_name', with: new_user.name @@ -148,11 +148,24 @@ describe 'Signup' do fill_in 'new_user_email', with: new_user.email fill_in 'new_user_email_confirmation', with: new_user.email fill_in 'new_user_password', with: new_user.password - click_button "Register" - expect_to_be_on_terms_page + click_button 'Register' + + expect(current_path).to eq new_user_session_path + expect(page).to have_content(/you must accept our terms of service/i) + end + + it 'asks the user to accept terms before going to the dashboard' do + visit root_path + + fill_in 'new_user_name', with: new_user.name + fill_in 'new_user_username', with: new_user.username + fill_in 'new_user_email', with: new_user.email + fill_in 'new_user_email_confirmation', with: new_user.email + fill_in 'new_user_password', with: new_user.password + check :terms_opt_in - click_button 'Accept terms' + click_button "Register" expect(current_path).to eq dashboard_projects_path end diff --git a/spec/features/users/terms_spec.rb b/spec/features/users/terms_spec.rb index af407c52917..5b2e7605c4d 100644 --- a/spec/features/users/terms_spec.rb +++ b/spec/features/users/terms_spec.rb @@ -3,12 +3,10 @@ require 'spec_helper' describe 'Users > Terms' do include TermsHelper - let(:user) { create(:user) } let!(:term) { create(:term, terms: 'By accepting, you promise to be nice!') } before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') - sign_in(user) end it 'shows the terms' do @@ -17,102 +15,119 @@ describe 'Users > Terms' do expect(page).to have_content('By accepting, you promise to be nice!') end - context 'declining the terms' do - it 'returns the user to the app' do - visit terms_path + it 'does not show buttons to accept, decline or sign out', :aggregate_failures do + visit terms_path + + expect(page).not_to have_css('.footer-block') + expect(page).not_to have_content('Accept terms') + expect(page).not_to have_content('Decline and sign out') + expect(page).not_to have_content('Continue') + end - click_button 'Decline and sign out' + context 'when signed in' do + let(:user) { create(:user) } - expect(page).not_to have_content(term.terms) - expect(user.reload.terms_accepted?).to be(false) + before do + sign_in(user) end - end - context 'accepting the terms' do - it 'returns the user to the app' do - visit terms_path + context 'declining the terms' do + it 'returns the user to the app' do + visit terms_path - click_button 'Accept terms' + click_button 'Decline and sign out' - expect(page).not_to have_content(term.terms) - expect(user.reload.terms_accepted?).to be(true) + expect(page).not_to have_content(term.terms) + expect(user.reload.terms_accepted?).to be(false) + end end - end - context 'when the user has already accepted the terms' do - before do - accept_terms(user) + context 'accepting the terms' do + it 'returns the user to the app' do + visit terms_path + + click_button 'Accept terms' + + expect(page).not_to have_content(term.terms) + expect(user.reload.terms_accepted?).to be(true) + end end - it 'allows the user to continue to the app' do - visit terms_path + context 'when the user has already accepted the terms' do + before do + accept_terms(user) + end + + it 'allows the user to continue to the app' do + visit terms_path - expect(page).to have_content "You have already accepted the Terms of Service as #{user.to_reference}" + expect(page).to have_content "You have already accepted the Terms of Service as #{user.to_reference}" - click_link 'Continue' + click_link 'Continue' - expect(current_path).to eq(root_path) + expect(current_path).to eq(root_path) + end end - end - context 'terms were enforced while session is active', :js do - let(:project) { create(:project) } + context 'terms were enforced while session is active', :js do + let(:project) { create(:project) } - before do - project.add_developer(user) - end + before do + project.add_developer(user) + end - it 'redirects to terms and back to where the user was going' do - visit project_path(project) + it 'redirects to terms and back to where the user was going' do + visit project_path(project) - enforce_terms + enforce_terms - within('.nav-sidebar') do - click_link 'Issues' - end + within('.nav-sidebar') do + click_link 'Issues' + end - expect_to_be_on_terms_page + expect_to_be_on_terms_page - click_button('Accept terms') + click_button('Accept terms') - expect(current_path).to eq(project_issues_path(project)) - end + expect(current_path).to eq(project_issues_path(project)) + end - # Disabled until https://gitlab.com/gitlab-org/gitlab-ce/issues/37162 is solved properly - xit 'redirects back to the page the user was trying to save' do - visit new_project_issue_path(project) + # Disabled until https://gitlab.com/gitlab-org/gitlab-ce/issues/37162 is solved properly + xit 'redirects back to the page the user was trying to save' do + visit new_project_issue_path(project) - fill_in :issue_title, with: 'Hello world, a new issue' - fill_in :issue_description, with: "We don't want to lose what the user typed" + fill_in :issue_title, with: 'Hello world, a new issue' + fill_in :issue_description, with: "We don't want to lose what the user typed" - enforce_terms + enforce_terms - click_button 'Submit issue' + click_button 'Submit issue' - expect(current_path).to eq(terms_path) + expect(current_path).to eq(terms_path) - click_button('Accept terms') + click_button('Accept terms') - expect(current_path).to eq(new_project_issue_path(project)) - expect(find_field('issue_title').value).to eq('Hello world, a new issue') - expect(find_field('issue_description').value).to eq("We don't want to lose what the user typed") + expect(current_path).to eq(new_project_issue_path(project)) + expect(find_field('issue_title').value).to eq('Hello world, a new issue') + expect(find_field('issue_description').value).to eq("We don't want to lose what the user typed") + end end - end - context 'when the terms are enforced' do - before do - enforce_terms - end + context 'when the terms are enforced' do + before do + enforce_terms + end - context 'signing out', :js do - it 'allows the user to sign out without a response' do - visit terms_path + context 'signing out', :js do + it 'allows the user to sign out without a response' do + visit terms_path - find('.header-user-dropdown-toggle').click - click_link('Sign out') + find('.header-user-dropdown-toggle').click + click_link('Sign out') - expect(page).to have_content('Sign in') - expect(page).to have_content('Register') + expect(page).to have_content('Sign in') + expect(page).to have_content('Register') + end end end end -- cgit v1.2.1 From 7fcfe7ccab12e69d7f1d0130614c3dfa374f05d3 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Sat, 9 Jun 2018 22:58:28 +0200 Subject: Fix conversion of integer into string in board spec In rails 5 controller specs, integers are converted to strings unless conte-type is set with `as: :json`: https://github.com/rails/rails/issues/26069 --- spec/controllers/boards/lists_controller_spec.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb index 71d45a22d91..57ccbf1d6b5 100644 --- a/spec/controllers/boards/lists_controller_spec.rb +++ b/spec/controllers/boards/lists_controller_spec.rb @@ -156,12 +156,18 @@ describe Boards::ListsController do def move(user:, board:, list:, position:) sign_in(user) - patch :update, namespace_id: project.namespace.to_param, - project_id: project, - board_id: board.to_param, - id: list.to_param, - list: { position: position }, - format: :json + params = { namespace_id: project.namespace.to_param, + project_id: project, + board_id: board.to_param, + id: list.to_param, + list: { position: position }, + format: :json } + + if Gitlab.rails5? + patch :update, params: params, as: :json + else + patch :update, params + end end end -- cgit v1.2.1 From 88808a1fd4f1b8ebfa90bf5a85cf618065140aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=A4mmerle?= Date: Mon, 11 Jun 2018 09:21:04 +0200 Subject: Replace ... with ellipsis in placeholders --- spec/javascripts/notes/components/comment_form_spec.js | 2 +- spec/javascripts/notes/components/note_app_spec.js | 4 ++-- spec/javascripts/notes/components/note_form_spec.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js index 224debbeff6..a7d1e4331eb 100644 --- a/spec/javascripts/notes/components/comment_form_spec.js +++ b/spec/javascripts/notes/components/comment_form_spec.js @@ -84,7 +84,7 @@ describe('issue_comment_form component', () => { it('should render textarea with placeholder', () => { expect( vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'), - ).toEqual('Write a comment or drag your files here...'); + ).toEqual('Write a comment or drag your files here…'); }); it('should make textarea disabled while requesting', (done) => { diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js index 0e792eee5e9..d494c63ff11 100644 --- a/spec/javascripts/notes/components/note_app_spec.js +++ b/spec/javascripts/notes/components/note_app_spec.js @@ -106,7 +106,7 @@ describe('note_app', () => { expect(vm.$el.querySelector('.js-main-target-form').tagName).toEqual('FORM'); expect( vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'), - ).toEqual('Write a comment or drag your files here...'); + ).toEqual('Write a comment or drag your files here…'); }); it('should render form comment button as disabled', () => { @@ -129,7 +129,7 @@ describe('note_app', () => { expect(vm.$el.querySelector('.js-main-target-form').tagName).toEqual('FORM'); expect( vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'), - ).toEqual('Write a comment or drag your files here...'); + ).toEqual('Write a comment or drag your files here…'); }); }); diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js index f841a408d09..413d4f69434 100644 --- a/spec/javascripts/notes/components/note_form_spec.js +++ b/spec/javascripts/notes/components/note_form_spec.js @@ -49,7 +49,7 @@ describe('issue_note_form component', () => { it('should render text area with placeholder', () => { expect( vm.$el.querySelector('textarea').getAttribute('placeholder'), - ).toEqual('Write a comment or drag your files here...'); + ).toEqual('Write a comment or drag your files here…'); }); it('should link to markdown docs', () => { -- cgit v1.2.1 From 927d2459cd6f36a314f6201db99610044ecd2fad Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Mon, 11 Jun 2018 13:06:29 +0300 Subject: Rename MrWidgetAuthorTime vue component --- .../vue_mr_widget/components/mr_widget_author_time_spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js index 6784b498c29..ef9def03db7 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js @@ -1,12 +1,12 @@ import Vue from 'vue'; -import authorTimeComponent from '~/vue_merge_request_widget/components/mr_widget_author_time.vue'; +import MrWidgetAuthorTime from '~/vue_merge_request_widget/components/mr_widget_author_time.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -describe('MRWidgetAuthorTime', () => { +describe('MrWidgetAuthorTime', () => { let vm; beforeEach(() => { - const Component = Vue.extend(authorTimeComponent); + const Component = Vue.extend(MrWidgetAuthorTime); vm = mountComponent(Component, { actionText: 'Merged by', -- cgit v1.2.1 From ea8dc107729c06bbc15bfba21dd249611c376a19 Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Mon, 11 Jun 2018 10:42:09 +0000 Subject: Don't use Gitlab::Utils.nlbr in Gitlab::Git --- spec/features/tags/master_deletes_tag_spec.rb | 4 ++-- spec/lib/gitlab/git/hook_spec.rb | 25 ++++++++++++++-------- spec/lib/gitlab/git/hooks_service_spec.rb | 8 +++---- spec/lib/gitlab/git/pre_receive_error_spec.rb | 9 ++++++++ .../gitlab/gitaly_client/operation_service_spec.rb | 4 ++-- spec/models/repository_spec.rb | 10 ++++----- .../merge_requests/ff_merge_service_spec.rb | 2 +- spec/services/merge_requests/merge_service_spec.rb | 2 +- spec/services/tags/create_service_spec.rb | 2 +- 9 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 spec/lib/gitlab/git/pre_receive_error_spec.rb (limited to 'spec') diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb index c0b4fa52526..9981bfa4609 100644 --- a/spec/features/tags/master_deletes_tag_spec.rb +++ b/spec/features/tags/master_deletes_tag_spec.rb @@ -38,7 +38,7 @@ feature 'Master deletes tag' do context 'when Gitaly operation_user_delete_tag feature is enabled' do before do allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag) - .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'Do not delete tags') + .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags') end scenario 'shows the error message' do @@ -51,7 +51,7 @@ feature 'Master deletes tag' do context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do before do allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) - .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'Do not delete tags') + .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags') end scenario 'shows the error message' do diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index 2fe1f5603ce..d9b3d0cf419 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -9,24 +9,31 @@ describe Gitlab::Git::Hook do end describe "#trigger" do - let(:project) { create(:project, :repository) } + set(:project) { create(:project, :repository) } let(:repository) { project.repository.raw_repository } let(:repo_path) { repository.path } + let(:hooks_dir) { File.join(repo_path, 'hooks') } let(:user) { create(:user) } let(:gl_id) { Gitlab::GlId.gl_id(user) } let(:gl_username) { user.username } def create_hook(name) - FileUtils.mkdir_p(File.join(repo_path, 'hooks')) - File.open(File.join(repo_path, 'hooks', name), 'w', 0755) do |f| - f.write('exit 0') + FileUtils.mkdir_p(hooks_dir) + hook_path = File.join(hooks_dir, name) + File.open(hook_path, 'w', 0755) do |f| + f.write(<<~HOOK) + #!/bin/sh + exit 0 + HOOK end end def create_failing_hook(name) - FileUtils.mkdir_p(File.join(repo_path, 'hooks')) - File.open(File.join(repo_path, 'hooks', name), 'w', 0755) do |f| - f.write(<<-HOOK) + FileUtils.mkdir_p(hooks_dir) + hook_path = File.join(hooks_dir, name) + File.open(hook_path, 'w', 0755) do |f| + f.write(<<~HOOK) + #!/bin/sh echo 'regular message from the hook' echo 'error message from the hook' 1>&2 echo 'error message from the hook line 2' 1>&2 @@ -38,7 +45,7 @@ describe Gitlab::Git::Hook do ['pre-receive', 'post-receive', 'update'].each do |hook_name| context "when triggering a #{hook_name} hook" do context "when the hook is successful" do - let(:hook_path) { File.join(repo_path, 'hooks', hook_name) } + let(:hook_path) { File.join(hooks_dir, hook_name) } let(:gl_repository) { Gitlab::GlRepository.gl_repository(project, false) } let(:env) do { @@ -76,7 +83,7 @@ describe Gitlab::Git::Hook do status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref) expect(status).to be false - expect(errors).to eq("error message from the hook
error message from the hook line 2
") + expect(errors).to eq("error message from the hook\nerror message from the hook line 2\n") end end end diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb index 3ed3feb4c74..9337aa39e13 100644 --- a/spec/lib/gitlab/git/hooks_service_spec.rb +++ b/spec/lib/gitlab/git/hooks_service_spec.rb @@ -26,24 +26,24 @@ describe Gitlab::Git::HooksService, seed_helper: true do context 'when pre-receive hook failed' do it 'does not call post-receive hook' do - expect(service).to receive(:run_hook).with('pre-receive').and_return([false, '']) + expect(service).to receive(:run_hook).with('pre-receive').and_return([false, 'hello world']) expect(service).not_to receive(:run_hook).with('post-receive') expect do service.execute(user, repository, blankrev, newrev, ref) - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end.to raise_error(Gitlab::Git::PreReceiveError, 'hello world') end end context 'when update hook failed' do it 'does not call post-receive hook' do expect(service).to receive(:run_hook).with('pre-receive').and_return([true, nil]) - expect(service).to receive(:run_hook).with('update').and_return([false, '']) + expect(service).to receive(:run_hook).with('update').and_return([false, 'hello world']) expect(service).not_to receive(:run_hook).with('post-receive') expect do service.execute(user, repository, blankrev, newrev, ref) - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end.to raise_error(Gitlab::Git::PreReceiveError, 'hello world') end end end diff --git a/spec/lib/gitlab/git/pre_receive_error_spec.rb b/spec/lib/gitlab/git/pre_receive_error_spec.rb new file mode 100644 index 00000000000..1b8be62dec6 --- /dev/null +++ b/spec/lib/gitlab/git/pre_receive_error_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe Gitlab::Git::PreReceiveError do + it 'makes its message HTML-friendly' do + ex = described_class.new("hello\nworld\n") + + expect(ex.message).to eq('hello
world
') + end +end diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb index 9fbdd73ee0e..9709f1f5646 100644 --- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb @@ -48,7 +48,7 @@ describe Gitlab::GitalyClient::OperationService do .and_return(response) expect { subject }.to raise_error( - Gitlab::Git::HooksService::PreReceiveError, "something failed") + Gitlab::Git::PreReceiveError, "something failed") end end end @@ -85,7 +85,7 @@ describe Gitlab::GitalyClient::OperationService do .and_return(response) expect { subject }.to raise_error( - Gitlab::Git::HooksService::PreReceiveError, "something failed") + Gitlab::Git::PreReceiveError, "something failed") end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 7c0a1cd967c..b6df048d4ca 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1195,7 +1195,7 @@ describe Repository do Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('feature') do new_rev end - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end.to raise_error(Gitlab::Git::PreReceiveError) end end @@ -1938,13 +1938,13 @@ describe Repository do context 'when pre hooks failed' do before do allow_any_instance_of(Gitlab::GitalyClient::OperationService) - .to receive(:user_delete_branch).and_raise(Gitlab::Git::HooksService::PreReceiveError) + .to receive(:user_delete_branch).and_raise(Gitlab::Git::PreReceiveError) end it 'gets an error and does not delete the branch' do expect do repository.rm_branch(user, 'feature') - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end.to raise_error(Gitlab::Git::PreReceiveError) expect(repository.find_branch('feature')).not_to be_nil end @@ -1980,7 +1980,7 @@ describe Repository do expect do repository.rm_branch(user, 'feature') - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end.to raise_error(Gitlab::Git::PreReceiveError) end it 'does not delete the branch' do @@ -1988,7 +1988,7 @@ describe Repository do expect do repository.rm_branch(user, 'feature') - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end.to raise_error(Gitlab::Git::PreReceiveError) expect(repository.find_branch('feature')).not_to be_nil end end diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb index 5ef6365fcc9..28f56d19657 100644 --- a/spec/services/merge_requests/ff_merge_service_spec.rb +++ b/spec/services/merge_requests/ff_merge_service_spec.rb @@ -72,7 +72,7 @@ describe MergeRequests::FfMergeService do it 'logs and saves error if there is an PreReceiveError exception' do error_message = 'error message' - allow(service).to receive(:repository).and_raise(Gitlab::Git::HooksService::PreReceiveError, error_message) + allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message) allow(service).to receive(:execute_hooks) service.execute(merge_request) diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index dc30a9bccc1..ef2738ef504 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -226,7 +226,7 @@ describe MergeRequests::MergeService do it 'logs and saves error if there is an PreReceiveError exception' do error_message = 'error message' - allow(service).to receive(:repository).and_raise(Gitlab::Git::HooksService::PreReceiveError, error_message) + allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message) allow(service).to receive(:execute_hooks) service.execute(merge_request) diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb index e7e9080b6b0..0cbe57352be 100644 --- a/spec/services/tags/create_service_spec.rb +++ b/spec/services/tags/create_service_spec.rb @@ -41,7 +41,7 @@ describe Tags::CreateService do it 'returns an error' do expect(repository).to receive(:add_tag) .with(user, 'v1.1.0', 'master', 'Foo') - .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'something went wrong') + .and_raise(Gitlab::Git::PreReceiveError, 'something went wrong') response = service.execute('v1.1.0', 'master', 'Foo') -- cgit v1.2.1 From 7469b58eafb2658542211479cd2e615203f94248 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 11 Jun 2018 11:42:47 +0100 Subject: Fix IDE pipelines eTagPoll not stopping Closes #47678 --- spec/javascripts/ide/components/pipelines/list_spec.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/pipelines/list_spec.js b/spec/javascripts/ide/components/pipelines/list_spec.js index 2bb5aa08c3b..68487733cb9 100644 --- a/spec/javascripts/ide/components/pipelines/list_spec.js +++ b/spec/javascripts/ide/components/pipelines/list_spec.js @@ -45,12 +45,15 @@ describe('IDE pipelines list', () => { setTimeout(done); }); - afterEach(() => { - vm.$store.dispatch('pipelines/stopPipelinePolling'); - vm.$store.dispatch('pipelines/clearEtagPoll'); - + afterEach(done => { vm.$destroy(); mock.restore(); + + vm.$store + .dispatch('pipelines/stopPipelinePolling') + .then(() => vm.$store.dispatch('pipelines/clearEtagPoll')) + .then(done) + .catch(done.fail); }); it('renders pipeline data', () => { -- cgit v1.2.1 From d5dae8e59a4097cb5f5be7405c793333683a2317 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 11 Jun 2018 12:08:30 +0100 Subject: fixed specs --- .../stores/modules/merge_requests/actions_spec.js | 47 +++++++++++++--------- 1 file changed, 29 insertions(+), 18 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js index d178a44b76a..03ec08d05c3 100644 --- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js @@ -199,28 +199,39 @@ describe('IDE merge requests actions', () => { }); it('commits reset mutations and actions', done => { - testAction( - openMergeRequest, - { projectPath: 'gitlab-org/gitlab-ce', id: '1' }, - mockedState, - [ - { type: 'CLEAR_PROJECTS' }, - { type: 'SET_CURRENT_MERGE_REQUEST', payload: '1' }, - { type: 'RESET_OPEN_FILES' }, - ], - [ - { type: 'pipelines/stopPipelinePolling' }, - { type: 'pipelines/clearEtagPoll' }, - { type: 'pipelines/resetLatestPipeline' }, - { type: 'setCurrentBranchId', payload: '' }, - ], - done, - ); + const commit = jasmine.createSpy(); + const dispatch = jasmine.createSpy().and.returnValue(Promise.resolve()); + openMergeRequest({ commit, dispatch }, { projectPath: 'gitlab-org/gitlab-ce', id: '1' }); + + setTimeout(() => { + expect(commit.calls.argsFor(0)).toEqual(['CLEAR_PROJECTS', null, { root: true }]); + expect(commit.calls.argsFor(1)).toEqual(['SET_CURRENT_MERGE_REQUEST', '1', { root: true }]); + expect(commit.calls.argsFor(2)).toEqual(['RESET_OPEN_FILES', null, { root: true }]); + + expect(dispatch.calls.argsFor(0)).toEqual([ + 'pipelines/resetLatestPipeline', + null, + { root: true }, + ]); + expect(dispatch.calls.argsFor(1)).toEqual(['setCurrentBranchId', '', { root: true }]); + expect(dispatch.calls.argsFor(2)).toEqual([ + 'pipelines/stopPipelinePolling', + null, + { root: true }, + ]); + expect(dispatch.calls.argsFor(3)).toEqual([ + 'pipelines/clearEtagPoll', + null, + { root: true }, + ]); + + done(); + }); }); it('pushes new route', () => { openMergeRequest( - { commit() {}, dispatch() {} }, + { commit() {}, dispatch: () => Promise.resolve() }, { projectPath: 'gitlab-org/gitlab-ce', id: '1' }, ); -- cgit v1.2.1 From d5e910710760cf8c189b3fc9629ff0874c1ee335 Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Mon, 11 Jun 2018 14:14:51 +0300 Subject: Update MrWidgetAuthorTime and MRWidgetMerged spec --- spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js | 2 +- .../vue_mr_widget/components/states/mr_widget_merged_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js index ef9def03db7..10143402acf 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js @@ -34,7 +34,7 @@ describe('MrWidgetAuthorTime', () => { }); it('renders provided time', () => { - expect(vm.$el.querySelector('time').getAttribute('title')).toEqual('2017-03-23T23:02:00.807Z'); + expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toEqual('2017-03-23T23:02:00.807Z'); expect(vm.$el.querySelector('time').textContent.trim()).toEqual('12 hours ago'); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js index adeea03481f..3e2fd71b5b8 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js @@ -186,7 +186,7 @@ describe('MRWidgetMerged', () => { it('should use mergedEvent mergedAt as tooltip title', () => { expect( - vm.$el.querySelector('time').getAttribute('title'), + vm.$el.querySelector('time').getAttribute('data-original-title'), ).toBe('Jan 24, 2018 1:02pm GMT+0000'); }); }); -- cgit v1.2.1 From 0f90c1fd730663fea30bd4d6368adbfbe752e001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 11 Jun 2018 13:54:25 +0200 Subject: Don't use Rails.root.join for upload fixture paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/controllers/uploads_controller_spec.rb | 2 +- spec/lib/gitlab/favicon_spec.rb | 2 +- spec/models/project_services/jira_service_spec.rb | 2 +- spec/uploaders/favicon_uploader_spec.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 3230d7b438f..1df2c954893 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -562,7 +562,7 @@ describe UploadsController do end context 'original filename or a version filename must match' do - let!(:appearance) { create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } + let!(:appearance) { create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png', 'image/png') } context 'has a valid filename on the original file' do it 'successfully returns the file' do diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index fdc5c0180e4..08c4a474217 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Gitlab::Favicon, :request_store do end it 'uses the custom favicon if a favicon appearance is present' do - create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png') expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png} end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index c3b4eb17a5c..b9f1c7dd5df 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -475,7 +475,7 @@ describe JiraService do end it 'includes returns the custom favicon' do - create :appearance, favicon: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) + create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png') props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title') expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$} diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb index db8a3207f4d..37deea8ab90 100644 --- a/spec/uploaders/favicon_uploader_spec.rb +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -10,7 +10,7 @@ RSpec.describe FaviconUploader do end def upload_fixture(filename) - fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) + fixture_file_upload("spec/fixtures/#{filename}") end context 'versions' do -- cgit v1.2.1 From 1418afc2d6e7699f08a1fc5f33b78ea847ac1451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Mon, 11 Jun 2018 13:29:37 +0000 Subject: Avoid checking the user format in every url validation --- spec/lib/gitlab/url_blocker_spec.rb | 46 +++++++++++++++++++++--------- spec/models/project_spec.rb | 11 ++++++-- spec/models/remote_mirror_spec.rb | 7 +++++ spec/validators/url_validator_spec.rb | 53 +++++++++++++++++++++++++++++++---- 4 files changed, 96 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index 81dbbb962dd..6f5f9938eca 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -58,20 +58,6 @@ describe Gitlab::UrlBlocker do end end - it 'returns true for a non-alphanumeric username' do - stub_resolv - - aggregate_failures do - expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a') - - # The leading character here is a Unicode "soft hyphen" - expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a') - - # Unicode alphanumerics are allowed - expect(described_class).not_to be_blocked_url('ssh://ğitlab@example.com/a') - end - end - it 'returns true for invalid URL' do expect(described_class.blocked_url?('http://:8080')).to be true end @@ -120,6 +106,38 @@ describe Gitlab::UrlBlocker do allow(Addrinfo).to receive(:getaddrinfo).and_call_original end end + + context 'when enforce_user is' do + before do + stub_resolv + end + + context 'false (default)' do + it 'does not block urls with a non-alphanumeric username' do + expect(described_class).not_to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a') + + # The leading character here is a Unicode "soft hyphen" + expect(described_class).not_to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a') + + # Unicode alphanumerics are allowed + expect(described_class).not_to be_blocked_url('ssh://ğitlab@example.com/a') + end + end + + context 'true' do + it 'blocks urls with a non-alphanumeric username' do + aggregate_failures do + expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a', enforce_user: true) + + # The leading character here is a Unicode "soft hyphen" + expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a', enforce_user: true) + + # Unicode alphanumerics are allowed + expect(described_class).not_to be_blocked_url('ssh://ğitlab@example.com/a', enforce_user: true) + end + end + end + end end # Resolv does not support resolving UTF-8 domain names diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 1a6ad3edd78..b9a9c4ebf42 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -238,20 +238,27 @@ describe Project do expect(project2.import_data).to be_nil end - it "does not allow blocked import_url localhost" do + it "does not allow import_url pointing to localhost" do project2 = build(:project, import_url: 'http://localhost:9000/t.git') expect(project2).to be_invalid expect(project2.errors[:import_url].first).to include('Requests to localhost are not allowed') end - it "does not allow blocked import_url port" do + it "does not allow import_url with invalid ports" do project2 = build(:project, import_url: 'http://github.com:25/t.git') expect(project2).to be_invalid expect(project2.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443') end + it "does not allow import_url with invalid user" do + project2 = build(:project, import_url: 'http://$user:password@github.com/t.git') + + expect(project2).to be_invalid + expect(project2.errors[:import_url].first).to include('Username needs to start with an alphanumeric character') + end + describe 'project pending deletion' do let!(:project_pending_deletion) do create(:project, diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index 1d94abe4195..4c086eeadfc 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -15,6 +15,13 @@ describe RemoteMirror do expect(remote_mirror).not_to be_valid end + + it 'does not allow url with an invalid user' do + remote_mirror = build(:remote_mirror, url: 'http://$user:password@invalid.invalid') + + expect(remote_mirror).to be_invalid + expect(remote_mirror.errors[:url].first).to include('Username needs to start with an alphanumeric character') + end end end diff --git a/spec/validators/url_validator_spec.rb b/spec/validators/url_validator_spec.rb index 2d719263fc8..93fe013d11c 100644 --- a/spec/validators/url_validator_spec.rb +++ b/spec/validators/url_validator_spec.rb @@ -50,13 +50,56 @@ describe UrlValidator do end end - context 'when ports is set' do - let(:validator) { described_class.new(attributes: [:link_url], ports: [443]) } + context 'when ports is' do + let(:validator) { described_class.new(attributes: [:link_url], ports: ports) } - it 'blocks urls with a different port' do - subject + context 'empty' do + let(:ports) { [] } - expect(badge.errors.empty?).to be false + it 'does not block any port' do + subject + + expect(badge.errors.empty?).to be true + end + end + + context 'set' do + let(:ports) { [443] } + + it 'blocks urls with a different port' do + subject + + expect(badge.errors.empty?).to be false + end + end + end + + context 'when enforce_user is' do + let(:url) { 'http://$user@example.com'} + let(:validator) { described_class.new(attributes: [:link_url], enforce_user: enforce_user) } + + context 'true' do + let(:enforce_user) { true } + + it 'checks user format' do + badge.link_url = url + + subject + + expect(badge.errors.empty?).to be false + end + end + + context 'false (default)' do + let(:enforce_user) { false } + + it 'does not check user format' do + badge.link_url = url + + subject + + expect(badge.errors.empty?).to be true + end end end end -- cgit v1.2.1 From 6defeb0a7d6928ad32d4d7a2fa35d0d71dbb9dea Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 9 Jun 2018 22:35:06 -0700 Subject: Expire Wiki content cache after importing a repository The cache state for Wikis that were imported via GitHub or Bitbucket does not appear to have been flushed after a successful import. Closes #47546 --- spec/lib/gitlab/github_import/sequential_importer_spec.rb | 1 - spec/models/project_spec.rb | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/github_import/sequential_importer_spec.rb b/spec/lib/gitlab/github_import/sequential_importer_spec.rb index 6089b0b751f..05d3243f806 100644 --- a/spec/lib/gitlab/github_import/sequential_importer_spec.rb +++ b/spec/lib/gitlab/github_import/sequential_importer_spec.rb @@ -30,7 +30,6 @@ describe Gitlab::GithubImport::SequentialImporter do expect(instance).to receive(:execute) end - expect(repository).to receive(:after_import) expect(importer.execute).to eq(true) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 1a6ad3edd78..d385d617731 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3391,10 +3391,11 @@ describe Project do end describe '#after_import' do - let(:project) { build(:project) } + let(:project) { create(:project) } it 'runs the correct hooks' do expect(project.repository).to receive(:after_import) + expect(project.wiki.repository).to receive(:after_import) expect(project).to receive(:import_finish) expect(project).to receive(:update_project_counter_caches) expect(project).to receive(:remove_import_jid) -- cgit v1.2.1 From 698515313fe38fb3f85fdeec1efa15e2c8b54cfd Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Tue, 15 May 2018 12:25:51 +0200 Subject: Fixes rejected pushes from maintainers Before the push git would make a call to `/:namespace/:project/git-receive-pack`. This would perform an access check without a ref. So the `Project#branch_allows_maintainer_push?` would return false. This adjusts `Project#branch_allows_maintainer_push?` to return true when passing no branch name if there are merge requests open that would allow the user to push. The actual check then happens when a call to `/api/v4/internal/allowed` is made from a git hook. --- spec/models/project_spec.rb | 5 +++++ spec/requests/git_http_spec.rb | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'spec') diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b9a9c4ebf42..58928465f95 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3658,6 +3658,11 @@ describe Project do .to be_truthy end + it 'allows access when there are merge requests open but no branch name is given' do + expect(project.branch_allows_collaboration?(user, nil)) + .to be_truthy + end + it 'does not allow guest users access' do guest = create(:user) target_project.add_guest(guest) diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 2514dab1714..92fcfb65269 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -1,6 +1,7 @@ require "spec_helper" describe 'Git HTTP requests' do + include ProjectForksHelper include TermsHelper include GitHttpHelpers include WorkhorseHelpers @@ -305,6 +306,22 @@ describe 'Git HTTP requests' do expect(response.body).to eq(change_access_error(:push_code)) end end + + context 'when merge requests are open that allow maintainer access' do + let(:canonical_project) { create(:project, :public, :repository) } + let(:project) { fork_project(canonical_project, nil, repository: true) } + + before do + canonical_project.add_master(user) + create(:merge_request, + source_project: project, + target_project: canonical_project, + source_branch: 'fixes', + allow_collaboration: true) + end + + it_behaves_like 'pushes are allowed' + end end end -- cgit v1.2.1 From fe8261fdb201ece94f7e89b09d999a37311f4588 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 11 Jun 2018 12:44:08 -0300 Subject: Add Gitlab::SQL:CTE for easily building CTE statements --- spec/lib/gitlab/sql/cte_spec.rb | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 spec/lib/gitlab/sql/cte_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/sql/cte_spec.rb b/spec/lib/gitlab/sql/cte_spec.rb new file mode 100644 index 00000000000..d6763c7b2e1 --- /dev/null +++ b/spec/lib/gitlab/sql/cte_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Gitlab::SQL::CTE, :postgresql do + describe '#to_arel' do + it 'generates an Arel relation for the CTE body' do + relation = User.where(id: 1) + cte = described_class.new(:cte_name, relation) + sql = cte.to_arel.to_sql + name = ActiveRecord::Base.connection.quote_table_name(:cte_name) + + sql1 = ActiveRecord::Base.connection.unprepared_statement do + relation.except(:order).to_sql + end + + expect(sql).to eq("#{name} AS (#{sql1})") + end + end + + describe '#alias_to' do + it 'returns an alias for the CTE' do + cte = described_class.new(:cte_name, nil) + table = Arel::Table.new(:kittens) + + source_name = ActiveRecord::Base.connection.quote_table_name(:cte_name) + alias_name = ActiveRecord::Base.connection.quote_table_name(:kittens) + + expect(cte.alias_to(table).to_sql).to eq("#{source_name} AS #{alias_name}") + end + end + + describe '#apply_to' do + it 'applies a CTE to an ActiveRecord::Relation' do + user = create(:user) + cte = described_class.new(:cte_name, User.where(id: user.id)) + + relation = cte.apply_to(User.all) + + expect(relation.to_sql).to match(/WITH .+cte_name/) + expect(relation.to_a).to eq(User.where(id: user.id).to_a) + end + end +end -- cgit v1.2.1 From 96ce4ed25c7633330f82439f1f9347f6eef8568a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 11 Jun 2018 09:47:51 -0700 Subject: Work around limitations of expect_any_instance_of by stubbing Project.find --- spec/models/project_spec.rb | 6 +++++- spec/workers/repository_fork_worker_spec.rb | 11 ++++++++--- spec/workers/repository_import_worker_spec.rb | 15 ++++++++++----- 3 files changed, 23 insertions(+), 9 deletions(-) (limited to 'spec') diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d385d617731..adb6ea94ac7 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1726,7 +1726,11 @@ describe Project do .with(project.repository_storage, project.disk_path, project.import_url) .and_return(true) - expect_any_instance_of(Repository).to receive(:after_import) + # Works around https://github.com/rspec/rspec-mocks/issues/910 + expect(Project).to receive(:find).with(project.id).twice.and_return(project) + expect(project.repository).to receive(:after_import) + .and_call_original + expect(project.wiki.repository).to receive(:after_import) .and_call_original end diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index 4b3c1736ea0..ae4786389c7 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -55,10 +55,15 @@ describe RepositoryForkWorker do it 'flushes various caches' do expect_fork_repository.and_return(true) - expect_any_instance_of(Repository).to receive(:expire_emptiness_caches) + # Works around https://github.com/rspec/rspec-mocks/issues/910 + expect(Project).to receive(:find).with(fork_project.id).and_return(fork_project) + expect(fork_project.repository).to receive(:expire_emptiness_caches) .and_call_original - - expect_any_instance_of(Repository).to receive(:expire_exists_cache) + expect(fork_project.repository).to receive(:expire_exists_cache) + .and_call_original + expect(fork_project.wiki.repository).to receive(:expire_emptiness_caches) + .and_call_original + expect(fork_project.wiki.repository).to receive(:expire_exists_cache) .and_call_original perform! diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb index 84d1b38ef19..f0884ad0aff 100644 --- a/spec/workers/repository_import_worker_spec.rb +++ b/spec/workers/repository_import_worker_spec.rb @@ -22,8 +22,11 @@ describe RepositoryImportWorker do expect_any_instance_of(Projects::ImportService).to receive(:execute) .and_return({ status: :ok }) - expect_any_instance_of(Repository).to receive(:expire_emptiness_caches) - expect_any_instance_of(Project).to receive(:import_finish) + # Works around https://github.com/rspec/rspec-mocks/issues/910 + expect(Project).to receive(:find).with(project.id).and_return(project) + expect(project.repository).to receive(:expire_emptiness_caches) + expect(project.wiki.repository).to receive(:expire_emptiness_caches) + expect(project).to receive(:import_finish) subject.perform(project.id) end @@ -34,9 +37,11 @@ describe RepositoryImportWorker do expect_any_instance_of(Projects::ImportService).to receive(:execute) .and_return({ status: :ok }) - expect_any_instance_of(Project).to receive(:after_import).and_call_original - expect_any_instance_of(Repository).to receive(:expire_emptiness_caches) - expect_any_instance_of(Project).to receive(:import_finish) + # Works around https://github.com/rspec/rspec-mocks/issues/910 + expect(Project).to receive(:find).with(project.id).and_return(project) + expect(project.repository).to receive(:expire_emptiness_caches) + expect(project.wiki.repository).to receive(:expire_emptiness_caches) + expect(project).to receive(:import_finish) subject.perform(project.id) end -- cgit v1.2.1 From 1756eea5316c9d1d4b6b8a9bb45d1d2425943f5c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 11 Jun 2018 10:05:00 -0700 Subject: Fix Rubocop failure in spec/models/project_spec.rb --- spec/models/project_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index adb6ea94ac7..24c99a7929a 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1727,7 +1727,7 @@ describe Project do .and_return(true) # Works around https://github.com/rspec/rspec-mocks/issues/910 - expect(Project).to receive(:find).with(project.id).twice.and_return(project) + expect(described_class).to receive(:find).with(project.id).twice.and_return(project) expect(project.repository).to receive(:after_import) .and_call_original expect(project.wiki.repository).to receive(:after_import) -- cgit v1.2.1 From 072f9224afded08cb1c93fa0e6c077928f60cca0 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 11 Jun 2018 12:06:25 -0700 Subject: Relax expectation in spec/models/project_spec.rb Multiple Sidekiq workers can run in the spec, causing `Project.find` to be run in different places. Instead of setting a fixed number of calls, just use `allow`. --- spec/models/project_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index a7a6d0bb09c..926365e409a 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1734,7 +1734,7 @@ describe Project do .and_return(true) # Works around https://github.com/rspec/rspec-mocks/issues/910 - expect(described_class).to receive(:find).with(project.id).twice.and_return(project) + allow(described_class).to receive(:find).with(project.id).and_return(project) expect(project.repository).to receive(:after_import) .and_call_original expect(project.wiki.repository).to receive(:after_import) -- cgit v1.2.1 From 79033a5f678c05c640d0b0cb7b53909ae2b5e221 Mon Sep 17 00:00:00 2001 From: "Marko, Peter" Date: Fri, 8 Jun 2018 20:37:19 +0200 Subject: Add id group api sorting option With introduction of subgroups, many groups may have the same name and/or path, thus further sorting options are necessary. Signed-off-by: Marko, Peter --- spec/requests/api/groups_spec.rb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 7d923932309..2e7a5cbeca6 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -138,10 +138,12 @@ describe API::Groups do context "when using sorting" do let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") } + let(:group4) { create(:group, name: "z#{group1.name}", path: "y#{group1.path}") } let(:response_groups) { json_response.map { |group| group['name'] } } before do group3.add_owner(user1) + group4.add_owner(user1) end it "sorts by name ascending by default" do @@ -150,7 +152,7 @@ describe API::Groups do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(response_groups).to eq([group3.name, group1.name]) + expect(response_groups).to eq([group3.name, group1.name, group4.name]) end it "sorts in descending order when passed" do @@ -159,16 +161,25 @@ describe API::Groups do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(response_groups).to eq([group1.name, group3.name]) + expect(response_groups).to eq([group4.name, group1.name, group3.name]) end - it "sorts by the order_by param" do + it "sorts by path in order_by param" do get api("/groups", user1), order_by: "path" expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(response_groups).to eq([group1.name, group3.name]) + expect(response_groups).to eq([group1.name, group4.name, group3.name]) + end + + it "sorts by id in the order_by param" do + get api("/groups", user1), order_by: "id" + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(response_groups).to eq([group1.name, group3.name, group4.name]) end end -- cgit v1.2.1 From c3f499e7c8656fa09b333b88950c70f7377f95f9 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 8 Jun 2018 22:06:08 +0200 Subject: Use upload ID instead of model ID in lease key For FileUploaders it's possible that a model has many uploads and if lease key is created only from model id, it causes that the model's uploads can not be migrated in parallel because the exclusive lease key would be same for all uploads of the model. --- .../uploaders/object_storage_shared_examples.rb | 6 ++---- spec/uploaders/object_storage_spec.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb index 6352f1527cd..12f7d3ed92a 100644 --- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb +++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb @@ -76,10 +76,8 @@ shared_examples "migrates" do |to_store:, from_store: nil| end context 'when migrate! is occupied by another process' do - let(:exclusive_lease_key) { "object_storage_migrate:#{subject.model.class}:#{subject.model.id}" } - before do - @uuid = Gitlab::ExclusiveLease.new(exclusive_lease_key, timeout: 1.hour.to_i).try_obtain + @uuid = Gitlab::ExclusiveLease.new(subject.exclusive_lease_key, timeout: 1.hour.to_i).try_obtain end it 'does not execute migrate!' do @@ -95,7 +93,7 @@ shared_examples "migrates" do |to_store:, from_store: nil| end after do - Gitlab::ExclusiveLease.cancel(exclusive_lease_key, @uuid) + Gitlab::ExclusiveLease.cancel(subject.exclusive_lease_key, @uuid) end end diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 0bc5b6751b3..902fc0bc030 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -332,6 +332,18 @@ describe ObjectStorage do expect { uploader.use_file }.to raise_error('exclusive lease already taken') end end + + it 'can still migrate other files of the same model' do + uploader2 = uploader_class.new(object, :file) + uploader2.upload = create(:upload) + uploader.upload = create(:upload) + + when_file_is_in_use do + expect(uploader2).to receive(:unsafe_migrate!) + + uploader2.migrate!(described_class::Store::REMOTE) + end + end end describe '#fog_credentials' do -- cgit v1.2.1 From 39ed07a816c9f732715b835bd363057a315f6ee7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Jun 2018 09:38:04 +0100 Subject: karma updates --- spec/javascripts/ide/components/commit_sidebar/list_item_spec.js | 1 + spec/javascripts/ide/components/commit_sidebar/list_spec.js | 1 + spec/javascripts/ide/components/repo_commit_section_spec.js | 1 + 3 files changed, 3 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js index cc7e0a3f26d..a732028158e 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js @@ -19,6 +19,7 @@ describe('Multi-file editor commit sidebar list item', () => { vm = createComponentWithStore(Component, store, { file: f, actionComponent: 'stage-button', + activeFileKey: `staged-${f.key}`, }).$mount(); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js index 54625ef90f8..a37cbd5a596 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js @@ -17,6 +17,7 @@ describe('Multi-file editor commit sidebar list', () => { action: 'stageAllChanges', actionBtnText: 'stage all', itemActionComponent: 'stage-button', + activeFileKey: 'staged-testing', }); vm.$store.state.rightPanelCollapsed = false; diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js index 5e3e00a180b..0934c239a8e 100644 --- a/spec/javascripts/ide/components/repo_commit_section_spec.js +++ b/spec/javascripts/ide/components/repo_commit_section_spec.js @@ -98,6 +98,7 @@ describe('RepoCommitSection', () => { store.state.noChangesStateSvgPath = 'nochangessvg'; store.state.committedStateSvgPath = 'svg'; + vm.$destroy(); vm = createComponentWithStore(Component, store).$mount(); expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toContain('No changes'); -- cgit v1.2.1 From 6633fbef4e6c7adaca9b1f1633640f09c3bc90d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Sun, 20 May 2018 14:18:50 +0200 Subject: Add CI_{PIPELINE,JOB}_URL --- spec/models/ci/build_spec.rb | 3 +++ spec/models/ci/pipeline_spec.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 0a0d7d3fea9..51b9b518117 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1548,7 +1548,9 @@ describe Ci::Build do let(:predefined_variables) do [ { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }, + { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true }, { key: 'CI_JOB_ID', value: build.id.to_s, public: true }, + { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true }, { key: 'CI_JOB_TOKEN', value: build.token, public: false }, { key: 'CI_BUILD_ID', value: build.id.to_s, public: true }, { key: 'CI_BUILD_TOKEN', value: build.token, public: false }, @@ -2171,6 +2173,7 @@ describe Ci::Build do it 'does not return prohibited variables' do keys = %w[CI_JOB_ID + CI_JOB_URL CI_JOB_TOKEN CI_BUILD_ID CI_BUILD_TOKEN diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 2bae98dcbb8..a41657b53b7 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -194,7 +194,7 @@ describe Ci::Pipeline, :mailer do it 'does contains persisted variables' do keys = subject.map { |variable| variable[:key] } - expect(keys).to eq %w[CI_PIPELINE_ID] + expect(keys).to eq %w[CI_PIPELINE_ID CI_PIPELINE_URL] end end end -- cgit v1.2.1 From 00ced96c53f320240126f6497e75488eca81e973 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Jun 2018 11:46:00 +0100 Subject: added specs for is-active class added spec for openPendingTab in component --- .../ide/components/commit_sidebar/list_item_spec.js | 16 ++++++++++++++++ .../ide/components/repo_commit_section_spec.js | 9 +++++++++ 2 files changed, 25 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js index a732028158e..8f7cf24c22f 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js @@ -90,4 +90,20 @@ describe('Multi-file editor commit sidebar list item', () => { }); }); }); + + describe('is active', () => { + it('does not add active class when dont keys match', () => { + expect(vm.$el.classList).not.toContain('is-active'); + }); + + it('adds active class when keys match', done => { + vm.keyPrefix = 'staged'; + + vm.$nextTick(() => { + expect(vm.$el.classList).toContain('is-active'); + + done(); + }); + }); + }); }); diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js index 0934c239a8e..de00a372ed4 100644 --- a/spec/javascripts/ide/components/repo_commit_section_spec.js +++ b/spec/javascripts/ide/components/repo_commit_section_spec.js @@ -56,6 +56,8 @@ describe('RepoCommitSection', () => { vm.$store.state.entries[f.path] = f; }); + spyOn(vm, 'openPendingTab').and.callThrough(); + return vm.$mount(); } @@ -177,5 +179,12 @@ describe('RepoCommitSection', () => { expect(store.state.openFiles.length).toBe(1); expect(store.state.openFiles[0].pending).toBe(true); }); + + it('calls openPendingTab', () => { + expect(vm.openPendingTab).toHaveBeenCalledWith({ + file: vm.lastOpenedFile, + keyPrefix: 'unstaged', + }); + }); }); }); -- cgit v1.2.1 From b2cb0c6cbdd7ef46f68e824174df35356636b307 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Jun 2018 12:19:59 +0100 Subject: fixed eslint --- spec/javascripts/ide/components/repo_commit_section_spec.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js index de00a372ed4..531bcd6e540 100644 --- a/spec/javascripts/ide/components/repo_commit_section_spec.js +++ b/spec/javascripts/ide/components/repo_commit_section_spec.js @@ -56,9 +56,7 @@ describe('RepoCommitSection', () => { vm.$store.state.entries[f.path] = f; }); - spyOn(vm, 'openPendingTab').and.callThrough(); - - return vm.$mount(); + return vm; } beforeEach(done => { @@ -66,6 +64,10 @@ describe('RepoCommitSection', () => { vm = createComponent(); + spyOn(vm, 'openPendingTab').and.callThrough(); + + vm.$mount(); + spyOn(service, 'getTreeData').and.returnValue( Promise.resolve({ headers: { -- cgit v1.2.1 From 1292b99b742fd83438b011d3082662d354b3330e Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Tue, 12 Jun 2018 11:30:13 +0000 Subject: Resolve "[Rails5] `ActionView::MissingTemplate` in spec/features/projects/wiki/user_views_wiki_page_spec.rb" --- spec/routing/project_routing_spec.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'spec') diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index e1b4e618092..56d93095a85 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -36,33 +36,36 @@ describe 'project routing' do shared_examples 'RESTful project resources' do let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] } let(:controller_path) { controller } + let(:id) { { id: '1' } } + let(:format) { {} } # response format, e.g. { format: :html } + let(:params) { { namespace_id: 'gitlab', project_id: 'gitlabhq' } } it 'to #index' do - expect(get("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index) + expect(get("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#index", params) if actions.include?(:index) end it 'to #create' do - expect(post("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create) + expect(post("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#create", params) if actions.include?(:create) end it 'to #new' do - expect(get("/gitlab/gitlabhq/#{controller_path}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new) + expect(get("/gitlab/gitlabhq/#{controller_path}/new")).to route_to("projects/#{controller}#new", params) if actions.include?(:new) end it 'to #edit' do - expect(get("/gitlab/gitlabhq/#{controller_path}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit) + expect(get("/gitlab/gitlabhq/#{controller_path}/1/edit")).to route_to("projects/#{controller}#edit", params.merge(**id, **format)) if actions.include?(:edit) end it 'to #show' do - expect(get("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show) + expect(get("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#show", params.merge(**id, **format)) if actions.include?(:show) end it 'to #update' do - expect(put("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update) + expect(put("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#update", params.merge(id)) if actions.include?(:update) end it 'to #destroy' do - expect(delete("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy) + expect(delete("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#destroy", params.merge(**id, **format)) if actions.include?(:destroy) end end @@ -150,12 +153,13 @@ describe 'project routing' do end it 'to #history' do - expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') + expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: :html) end it_behaves_like 'RESTful project resources' do let(:actions) { [:create, :edit, :show, :destroy] } let(:controller) { 'wikis' } + let(:format) { { format: :html } } end end -- cgit v1.2.1 From e4612df0e257a27b47986a9ee6aa77f68a9cd3ba Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 12 Jun 2018 13:29:43 +0200 Subject: Ensure MR diffs always exist in the PR importer In rare cases it could happen that an MR was created, but creating the MR diffs somehow failed (e.g. due to an error). This commit adds an additional check to make sure MR diffs are always present when importing GitHub pull requests. --- .../github_import/importer/pull_request_importer_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb index 6686b7ce0b5..3422a1e82fc 100644 --- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb @@ -276,5 +276,17 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi expect(diff.merge_request_diff_commits.exists?).to eq(true) end + + context 'when the merge request exists' do + it 'creates the merge request diffs if they do not yet exist' do + mr, _ = importer.create_merge_request + + mr.merge_request_diffs.delete_all + + importer.insert_git_data(mr, true) + + expect(mr.merge_request_diffs.exists?).to eq(true) + end + end end end -- cgit v1.2.1 From 9aad5ed0a23a6386bf48f166a6b07b3a41d9c2c1 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 11 Jun 2018 10:28:30 +0200 Subject: Fixes Microsoft Teams notifications for pipeline events Closes #42342 --- spec/lib/microsoft_teams/notifier_spec.rb | 2 +- spec/models/project_services/microsoft_teams_service_spec.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/microsoft_teams/notifier_spec.rb b/spec/lib/microsoft_teams/notifier_spec.rb index 3035693812f..c9756544bd6 100644 --- a/spec/lib/microsoft_teams/notifier_spec.rb +++ b/spec/lib/microsoft_teams/notifier_spec.rb @@ -8,7 +8,7 @@ describe MicrosoftTeams::Notifier do let(:options) do { title: 'JohnDoe4/project2', - pretext: '[[JohnDoe4/project2](http://localhost/namespace2/gitlabhq)] Issue [#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1) opened by user6', + summary: '[[JohnDoe4/project2](http://localhost/namespace2/gitlabhq)] Issue [#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1) opened by user6', activity: { title: 'Issue opened by user6', subtitle: 'in [JohnDoe4/project2](http://localhost/namespace2/gitlabhq)', diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb index 8d9ee96227f..3351c6280b4 100644 --- a/spec/models/project_services/microsoft_teams_service_spec.rb +++ b/spec/models/project_services/microsoft_teams_service_spec.rb @@ -225,10 +225,15 @@ describe MicrosoftTeamsService do it 'calls Microsoft Teams API for pipeline events' do data = Gitlab::DataBuilder::Pipeline.build(pipeline) + data[:markdown] = true chat_service.execute(data) - expect(WebMock).to have_requested(:post, webhook_url).once + message = ChatMessage::PipelineMessage.new(data) + + expect(WebMock).to have_requested(:post, webhook_url) + .with(body: hash_including({ summary: message.summary })) + .once end end -- cgit v1.2.1 From ab93f7cc7912e0f07cf5ac4de95afdabffad5138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Tue, 12 Jun 2018 12:31:10 +0000 Subject: Bumping gitlab-gollum-lib and gitlab-gollum-rugged_adapter --- spec/lib/gitlab/git/wiki_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb index 722d697c28e..35b06b14620 100644 --- a/spec/lib/gitlab/git/wiki_spec.rb +++ b/spec/lib/gitlab/git/wiki_spec.rb @@ -25,6 +25,22 @@ describe Gitlab::Git::Wiki do end end + describe '#delete_page', :skip_gitaly_mock do + after do + destroy_page('page1') + end + + it 'only removes the page with the same path' do + create_page('page1', 'content') + create_page('*', 'content') + + subject.delete_page('*', commit_details('whatever')) + + expect(subject.pages.count).to eq 1 + expect(subject.pages.first.title).to eq 'page1' + end + end + def create_page(name, content) subject.write_page(name, :markdown, content, commit_details(name)) end -- cgit v1.2.1 From f376347f24f24efc157d80de63381dd22d060630 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 5 Jun 2018 17:58:28 +0200 Subject: Find and mark more Git disk access locations, part 2 --- spec/factories/projects.rb | 12 --- spec/lib/gitlab/diff/file_spec.rb | 4 +- spec/lib/gitlab/git/blob_spec.rb | 14 ++- spec/lib/gitlab/git/branch_spec.rb | 4 +- spec/lib/gitlab/git/commit_spec.rb | 17 +++- spec/lib/gitlab/git/diff_spec.rb | 6 +- spec/lib/gitlab/git/gitlab_projects_spec.rb | 112 ++++++++++----------- spec/lib/gitlab/git/hook_spec.rb | 7 ++ spec/lib/gitlab/git/index_spec.rb | 7 ++ .../import_export/merge_request_parser_spec.rb | 4 +- spec/lib/gitlab/shell_spec.rb | 72 ++++++++++--- spec/services/git_tag_push_service_spec.rb | 4 +- .../services/merge_requests/squash_service_spec.rb | 4 +- .../services/projects/after_import_service_spec.rb | 8 +- spec/services/projects/destroy_service_spec.rb | 6 +- .../migrate_repository_service_spec.rb | 6 +- spec/spec_helper.rb | 13 --- spec/support/helpers/test_env.rb | 10 ++ spec/tasks/gitlab/gitaly_rake_spec.rb | 4 +- spec/tasks/gitlab/shell_rake_spec.rb | 8 +- spec/workers/git_garbage_collect_worker_spec.rb | 8 +- spec/workers/project_destroy_worker_spec.rb | 6 +- spec/workers/repository_fork_worker_spec.rb | 3 + 23 files changed, 214 insertions(+), 125 deletions(-) (limited to 'spec') diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 16e025618a6..f6b05bac0e8 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -151,11 +151,6 @@ FactoryBot.define do trait :empty_repo do after(:create) do |project| raise "Failed to create repository!" unless project.create_repository - - # We delete hooks so that gitlab-shell will not try to authenticate with - # an API that isn't running - project.gitlab_shell.rm_directory(project.repository_storage, - File.join("#{project.disk_path}.git", 'hooks')) end end @@ -180,13 +175,6 @@ FactoryBot.define do trait :wiki_repo do after(:create) do |project| raise 'Failed to create wiki repository!' unless project.create_wiki - - # We delete hooks so that gitlab-shell will not try to authenticate with - # an API that isn't running - project.gitlab_shell.rm_directory( - project.repository_storage, - File.join("#{project.wiki.repository.disk_path}.git", "hooks") - ) end end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index f0e83ccfc7a..5dfbb8e71f8 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -79,7 +79,9 @@ describe Gitlab::Diff::File do let(:diffs) { commit.diffs } before do - info_dir_path = File.join(project.repository.path_to_repo, 'info') + info_dir_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + File.join(project.repository.path_to_repo, 'info') + end FileUtils.mkdir(info_dir_path) unless File.exist?(info_dir_path) File.write(File.join(info_dir_path, 'attributes'), "*.md -diff\n") diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 94eaf86ef80..6015086f002 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -149,7 +149,9 @@ describe Gitlab::Git::Blob, seed_helper: true do it 'limits the size of a large file' do blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1 buffer = Array.new(blob_size, 0) - rugged_blob = Rugged::Blob.from_buffer(repository.rugged, buffer.join('')) + rugged_blob = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Rugged::Blob.from_buffer(repository.rugged, buffer.join('')) + end blob = Gitlab::Git::Blob.raw(repository, rugged_blob) expect(blob.size).to eq(blob_size) @@ -164,7 +166,9 @@ describe Gitlab::Git::Blob, seed_helper: true do context 'when sha references a tree' do it 'returns nil' do - tree = repository.rugged.rev_parse('master^{tree}') + tree = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged.rev_parse('master^{tree}') + end blob = Gitlab::Git::Blob.raw(repository, tree.oid) @@ -278,7 +282,11 @@ describe Gitlab::Git::Blob, seed_helper: true do end describe '.batch_lfs_pointers' do - let(:tree_object) { repository.rugged.rev_parse('master^{tree}') } + let(:tree_object) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged.rev_parse('master^{tree}') + end + end let(:non_lfs_blob) do Gitlab::Git::Blob.find( diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb index a19155ed5b0..ec1a684cfbc 100644 --- a/spec/lib/gitlab/git/branch_spec.rb +++ b/spec/lib/gitlab/git/branch_spec.rb @@ -69,7 +69,9 @@ describe Gitlab::Git::Branch, seed_helper: true do Gitlab::Git.committer_hash(email: user.email, name: user.name) end let(:params) do - parents = [repository.rugged.head.target] + parents = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + [repository.rugged.head.target] + end tree = parents.first.tree { diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 89be8a1b7f2..5af982c7a54 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -4,12 +4,15 @@ describe Gitlab::Git::Commit, seed_helper: true do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } let(:rugged_commit) do - repository.rugged.lookup(SeedRepo::Commit::ID) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged.lookup(SeedRepo::Commit::ID) + end end - describe "Commit info" do before do - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged + repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged + end @committer = { email: 'mike@smith.com', @@ -58,7 +61,9 @@ describe Gitlab::Git::Commit, seed_helper: true do after do # Erase the new commit so other tests get the original repo - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged + repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged + end repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) end end @@ -115,7 +120,9 @@ describe Gitlab::Git::Commit, seed_helper: true do describe '.find' do it "should return first head commit if without params" do expect(described_class.last(repository).id).to eq( - repository.rugged.head.target.oid + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged.head.target.oid + end ) end diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index 4a7b06003fc..3bb0b5be15b 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -27,8 +27,10 @@ EOT too_large: false } - @rugged_diff = repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths: - [".gitmodules"]).patches.first + @rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths: + [".gitmodules"]).patches.first + end end describe '.new' do diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb index 8b715d717c1..f5d8503c30c 100644 --- a/spec/lib/gitlab/git/gitlab_projects_spec.rb +++ b/spec/lib/gitlab/git/gitlab_projects_spec.rb @@ -5,6 +5,13 @@ describe Gitlab::Git::GitlabProjects do TestEnv.clean_test_path end + around do |example| + # TODO move this spec to gitaly-ruby. GitlabProjects is not used in gitlab-ce + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + let(:project) { create(:project, :repository) } if $VERBOSE @@ -190,36 +197,30 @@ describe Gitlab::Git::GitlabProjects do end end - context 'when Gitaly import_repository feature is enabled' do - it_behaves_like 'importing repository' - end + describe 'logging' do + it 'imports a repo' do + message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>." + expect(logger).to receive(:info).with(message) - context 'when Gitaly import_repository feature is disabled', :disable_gitaly do - describe 'logging' do - it 'imports a repo' do - message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>." - expect(logger).to receive(:info).with(message) - - subject - end + subject end + end - context 'timeout' do - it 'does not import a repo' do - stub_spawn_timeout(cmd, timeout, nil) + context 'timeout' do + it 'does not import a repo' do + stub_spawn_timeout(cmd, timeout, nil) - message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed." - expect(logger).to receive(:error).with(message) + message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed." + expect(logger).to receive(:error).with(message) - is_expected.to be_falsy + is_expected.to be_falsy - expect(gl_projects.output).to eq("Timed out\n") - expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy - end + expect(gl_projects.output).to eq("Timed out\n") + expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy end - - it_behaves_like 'importing repository' end + + it_behaves_like 'importing repository' end describe '#fork_repository' do @@ -232,9 +233,6 @@ describe Gitlab::Git::GitlabProjects do before do FileUtils.mkdir_p(dest_repos_path) - - # Undo spec_helper stub that deletes hooks - allow_any_instance_of(described_class).to receive(:fork_repository).and_call_original end after do @@ -258,51 +256,45 @@ describe Gitlab::Git::GitlabProjects do end end - context 'when Gitaly fork_repository feature is enabled' do - it_behaves_like 'forking a repository' - end - - context 'when Gitaly fork_repository feature is disabled', :disable_gitaly do - it_behaves_like 'forking a repository' + it_behaves_like 'forking a repository' - # We seem to be stuck to having only one working Gitaly storage in tests, changing - # that is not very straight-forward so I'm leaving this test here for now till - # https://gitlab.com/gitlab-org/gitlab-ce/issues/41393 is fixed. - context 'different storages' do - let(:dest_repos) { 'alternative' } - let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), dest_repos) } + # We seem to be stuck to having only one working Gitaly storage in tests, changing + # that is not very straight-forward so I'm leaving this test here for now till + # https://gitlab.com/gitlab-org/gitlab-ce/issues/41393 is fixed. + context 'different storages' do + let(:dest_repos) { 'alternative' } + let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), dest_repos) } - before do - stub_storage_settings(dest_repos => { 'path' => dest_repos_path }) - end + before do + stub_storage_settings(dest_repos => { 'path' => dest_repos_path }) + end - it 'forks the repo' do - is_expected.to be_truthy + it 'forks the repo' do + is_expected.to be_truthy - expect(File.exist?(dest_repo)).to be_truthy - expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy - expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy - end + expect(File.exist?(dest_repo)).to be_truthy + expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy + expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy end + end - describe 'log messages' do - describe 'successful fork' do - it do - message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>." - expect(logger).to receive(:info).with(message) + describe 'log messages' do + describe 'successful fork' do + it do + message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>." + expect(logger).to receive(:info).with(message) - subject - end + subject end + end - describe 'failed fork due existing destination' do - it do - FileUtils.mkdir_p(dest_repo) - message = "fork-repository failed: destination repository <#{dest_repo}> already exists." - expect(logger).to receive(:error).with(message) + describe 'failed fork due existing destination' do + it do + FileUtils.mkdir_p(dest_repo) + message = "fork-repository failed: destination repository <#{dest_repo}> already exists." + expect(logger).to receive(:error).with(message) - subject - end + subject end end end diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index 2fe1f5603ce..c8a5052200c 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -8,6 +8,13 @@ describe Gitlab::Git::Hook do allow_any_instance_of(described_class).to receive(:trigger).and_call_original end + around do |example| + # TODO move hook tests to gitaly-ruby. Hook will disappear from gitlab-ce + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + describe "#trigger" do let(:project) { create(:project, :repository) } let(:repository) { project.repository.raw_repository } diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb index 73fbc6a6afa..16e6bd35449 100644 --- a/spec/lib/gitlab/git/index_spec.rb +++ b/spec/lib/gitlab/git/index_spec.rb @@ -8,6 +8,13 @@ describe Gitlab::Git::Index, seed_helper: true do index.read_tree(repository.lookup('master').tree) end + around do |example| + # TODO move these specs to gitaly-ruby. The Index class will disappear from gitlab-ce + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + describe '#create' do let(:options) do { diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb index b793636c4d6..68eaa70e6b6 100644 --- a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb +++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb @@ -19,7 +19,9 @@ describe Gitlab::ImportExport::MergeRequestParser do end after do - FileUtils.rm_rf(project.repository.path_to_repo) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + FileUtils.rm_rf(project.repository.path_to_repo) + end end it 'has a source branch' do diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 14eae22a2ec..155e1663298 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -498,16 +498,34 @@ describe Gitlab::Shell do ) end - it 'returns true when the command succeeds' do - expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { true } + context 'with gitaly' do + it 'returns true when the command succeeds' do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository) + .with(repository.raw_repository) { :gitaly_response_object } + + is_expected.to be_truthy + end + + it 'return false when the command fails' do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository) + .with(repository.raw_repository) { raise GRPC::BadStatus, 'bla' } - is_expected.to be_truthy + is_expected.to be_falsy + end end - it 'return false when the command fails' do - expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { false } + context 'without gitaly', :disable_gitaly do + it 'returns true when the command succeeds' do + expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { true } - is_expected.to be_falsy + is_expected.to be_truthy + end + + it 'return false when the command fails' do + expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { false } + + is_expected.to be_falsy + end end end @@ -662,21 +680,43 @@ describe Gitlab::Shell do describe '#import_repository' do let(:import_url) { 'https://gitlab.com/gitlab-org/gitlab-ce.git' } - it 'returns true when the command succeeds' do - expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true } + context 'with gitaly' do + it 'returns true when the command succeeds' do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:import_repository).with(import_url) - result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url) + result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url) - expect(result).to be_truthy + expect(result).to be_truthy + end + + it 'raises an exception when the command fails' do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:import_repository) + .with(import_url) { raise GRPC::BadStatus, 'bla' } + expect_any_instance_of(Gitlab::Shell::GitalyGitlabProjects).to receive(:output) { 'error'} + + expect do + gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url) + end.to raise_error(Gitlab::Shell::Error, "error") + end end - it 'raises an exception when the command fails' do - allow(gitlab_projects).to receive(:output) { 'error' } - expect(gitlab_projects).to receive(:import_project) { false } + context 'without gitaly', :disable_gitaly do + it 'returns true when the command succeeds' do + expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true } - expect do - gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url) - end.to raise_error(Gitlab::Shell::Error, "error") + result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url) + + expect(result).to be_truthy + end + + it 'raises an exception when the command fails' do + allow(gitlab_projects).to receive(:output) { 'error' } + expect(gitlab_projects).to receive(:import_project) { false } + + expect do + gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url) + end.to raise_error(Gitlab::Shell::Error, "error") + end end end end diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index 33405d7a7ec..92159e1e372 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -118,7 +118,9 @@ describe GitTagPushService do before do # Create the lightweight tag - project.repository.raw_repository.rugged.tags.create(tag_name, newrev) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.raw_repository.rugged.tags.create(tag_name, newrev) + end # Clear tag list cache project.repository.expire_tags_cache diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb index bd884787425..ded17fa92a4 100644 --- a/spec/services/merge_requests/squash_service_spec.rb +++ b/spec/services/merge_requests/squash_service_spec.rb @@ -63,7 +63,9 @@ describe MergeRequests::SquashService do end it 'has the same diff as the merge request, but a different SHA' do - rugged = project.repository.rugged + rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.rugged + end mr_diff = rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha) squash_diff = rugged.diff(merge_request.diff_start_sha, squash_sha) diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb index c6678fc1f5c..cd52bc88f4c 100644 --- a/spec/services/projects/after_import_service_spec.rb +++ b/spec/services/projects/after_import_service_spec.rb @@ -32,7 +32,7 @@ describe Projects::AfterImportService do end it 'removes refs/pull/**/*' do - expect(repository.rugged.references.map(&:name)) + expect(rugged.references.map(&:name)) .not_to include(%r{\Arefs/pull/}) end end @@ -46,10 +46,14 @@ describe Projects::AfterImportService do end it "does not remove refs/#{name}/tmp" do - expect(repository.rugged.references.map(&:name)) + expect(rugged.references.map(&:name)) .to include("refs/#{name}/tmp") end end end + + def rugged + Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } + end end end diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index b63f409579e..38660ad7a01 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -5,7 +5,11 @@ describe Projects::DestroyService do let!(:user) { create(:user) } let!(:project) { create(:project, :repository, namespace: user.namespace) } - let!(:path) { project.repository.path_to_repo } + let!(:path) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.path_to_repo + end + end let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") } let!(:async) { false } # execute or async_execute diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb index 7dca81eb59e..ed4930313c5 100644 --- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb +++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb @@ -37,7 +37,11 @@ describe Projects::HashedStorage::MigrateRepositoryService do it 'writes project full path to .git/config' do service.execute - expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path + rugged_config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.rugged.config['gitlab.fullpath'] + end + + expect(rugged_config).to eq project.full_path end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e093444121a..8417b340de5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -107,19 +107,6 @@ RSpec.configure do |config| end config.before(:example) do - # Skip pre-receive hook check so we can use the web editor and merge. - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) - - allow_any_instance_of(Gitlab::Git::GitlabProjects).to receive(:fork_repository).and_wrap_original do |m, *args| - m.call(*args) - - shard_name, repository_relative_path = args - # We can't leave the hooks in place after a fork, as those would fail in tests - # The "internal" API is not available - Gitlab::Shell.new.rm_directory(shard_name, - File.join(repository_relative_path, 'hooks')) - end - # Enable all features by default for testing allow(Feature).to receive(:enabled?) { true } end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 1fef50a52ec..05a8e6206ae 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -135,6 +135,16 @@ module TestEnv install_dir: Gitlab.config.gitlab_shell.path, version: Gitlab::Shell.version_required, task: 'gitlab:shell:install') + + create_fake_git_hooks + end + + def create_fake_git_hooks + # gitlab-shell hooks don't work in our test environment because they try to make internal API calls + hooks_dir = File.join(Gitlab.config.gitlab_shell.path, 'hooks') + %w[pre-receive post-receive update].each do |hook| + File.open(File.join(hooks_dir, hook), 'w', 0755) { |f| f.puts '#!/bin/sh' } + end end def setup_gitaly diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index 1e507c0236e..4545226d78c 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -134,7 +134,9 @@ describe 'gitlab:gitaly namespace rake task' do parsed_output = TomlRB.parse(expected_output) config.each do |name, params| - expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params.legacy_disk_path }) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params.legacy_disk_path }) + end end end end diff --git a/spec/tasks/gitlab/shell_rake_spec.rb b/spec/tasks/gitlab/shell_rake_spec.rb index 4a756c5742d..0ed5d3e27b9 100644 --- a/spec/tasks/gitlab/shell_rake_spec.rb +++ b/spec/tasks/gitlab/shell_rake_spec.rb @@ -7,11 +7,17 @@ describe 'gitlab:shell rake tasks' do stub_warn_user_is_not_gitlab end + after do + TestEnv.create_fake_git_hooks + end + describe 'install task' do it 'invokes create_hooks task' do expect(Rake::Task['gitlab:shell:create_hooks']).to receive(:invoke) - storages = Gitlab.config.repositories.storages.values.map(&:legacy_disk_path) + storages = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab.config.repositories.storages.values.map(&:legacy_disk_path) + end expect(Kernel).to receive(:system).with('bin/install', *storages).and_call_original expect(Kernel).to receive(:system).with('bin/compile').and_call_original diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index f44b4edc305..807d1b8c084 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -218,7 +218,9 @@ describe GitGarbageCollectWorker do # Create a new commit on a random new branch def create_objects(project) - rugged = project.repository.rugged + rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.rugged + end old_commit = rugged.branches.first.target new_commit_sha = Rugged::Commit.create( rugged, @@ -237,7 +239,9 @@ describe GitGarbageCollectWorker do end def packs(project) - Dir["#{project.repository.path_to_repo}/objects/pack/*.pack"] + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Dir["#{project.repository.path_to_repo}/objects/pack/*.pack"] + end end def packed_refs(project) diff --git a/spec/workers/project_destroy_worker_spec.rb b/spec/workers/project_destroy_worker_spec.rb index f19c9dff941..42e1d86e3bb 100644 --- a/spec/workers/project_destroy_worker_spec.rb +++ b/spec/workers/project_destroy_worker_spec.rb @@ -2,7 +2,11 @@ require 'spec_helper' describe ProjectDestroyWorker do let(:project) { create(:project, :repository, pending_delete: true) } - let(:path) { project.repository.path_to_repo } + let(:path) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.path_to_repo + end + end subject { described_class.new } diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index 4b3c1736ea0..f5f1b60ace8 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -89,6 +89,9 @@ describe RepositoryForkWorker do it_behaves_like 'RepositoryForkWorker performing' it 'logs a message about forking with old-style arguments' do + allow(subject).to receive(:gitlab_shell).and_return(shell) + expect(shell).to receive(:fork_repository) { true } + allow(Rails.logger).to receive(:info).with(anything) # To compensate for other logs expect(Rails.logger).to receive(:info).with("Project #{fork_project.id} is being forked using old-style arguments.") -- cgit v1.2.1 From 1801b7fd20b6732a8381878147943f32f564dc18 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Jun 2018 13:53:13 +0100 Subject: Fixed IDE jobs empty state showing when loading Closes #47766 --- spec/javascripts/ide/components/jobs/detail_spec.js | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js index 641ba06f653..8f8d4b9709e 100644 --- a/spec/javascripts/ide/components/jobs/detail_spec.js +++ b/spec/javascripts/ide/components/jobs/detail_spec.js @@ -62,6 +62,11 @@ describe('IDE jobs detail view', () => { expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe(''); }); + it('hides output when loading', () => { + expect(vm.$el.querySelector('.bash')).not.toBe(null); + expect(vm.$el.querySelector('.bash').style.display).toBe('none'); + }); + it('hide loading icon when isLoading is false', done => { vm.$store.state.pipelines.detailJob.isLoading = false; -- cgit v1.2.1 From 423fcb476e64c96d7945af8d9fca65aa6db20314 Mon Sep 17 00:00:00 2001 From: Murat Dogan Date: Tue, 12 Jun 2018 13:04:01 +0000 Subject: mergeError message has been binded using v-html directive --- .../states/mr_widget_failed_to_merge_spec.js | 23 ++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js index a0a74648328..8de99fd3c96 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js @@ -6,6 +6,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; describe('MRWidgetFailedToMerge', () => { const dummyIntervalId = 1337; let Component; + let mr; let vm; beforeEach(() => { @@ -13,10 +14,11 @@ describe('MRWidgetFailedToMerge', () => { spyOn(eventHub, '$emit'); spyOn(window, 'setInterval').and.returnValue(dummyIntervalId); spyOn(window, 'clearInterval').and.stub(); + mr = { + mergeError: 'Merge error happened', + }; vm = mountComponent(Component, { - mr: { - mergeError: 'Merge error happened.', - }, + mr, }); }); @@ -44,6 +46,19 @@ describe('MRWidgetFailedToMerge', () => { expect(vm.timerText).toEqual('Refreshing in a second to show the updated status...'); }); }); + + describe('mergeError', () => { + it('removes forced line breaks', done => { + mr.mergeError = 'contains
line breaks
'; + + Vue.nextTick() + .then(() => { + expect(vm.mergeError).toBe('contains line breaks'); + }) + .then(done) + .catch(done.fail); + }); + }); }); describe('created', () => { @@ -103,7 +118,7 @@ describe('MRWidgetFailedToMerge', () => { it('renders given error', () => { expect(vm.$el.querySelector('.has-error-message').textContent.trim()).toEqual( - 'Merge error happened..', + 'Merge error happened.', ); }); -- cgit v1.2.1 From c1cc4777caad078e3eff9ba6170beb7ee7254917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 12 Jun 2018 10:02:06 -0500 Subject: Hide events from internal projects in public feed for anonymous users This change fixes a bug where an anonymous user was able to see the activity related to internal projects when visiting the public profile of a user of the GitLab instance. --- spec/finders/user_recent_events_finder_spec.rb | 45 ++++++++++++++++++-------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'spec') diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb index 3ca0f7c3c89..da043f94021 100644 --- a/spec/finders/user_recent_events_finder_spec.rb +++ b/spec/finders/user_recent_events_finder_spec.rb @@ -1,31 +1,50 @@ require 'spec_helper' describe UserRecentEventsFinder do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:project_owner) { project.creator } - let!(:event) { create(:event, project: project, author: project_owner) } + let(:current_user) { create(:user) } + let(:project_owner) { create(:user) } + let(:private_project) { create(:project, :private, creator: project_owner) } + let(:internal_project) { create(:project, :internal, creator: project_owner) } + let(:public_project) { create(:project, :public, creator: project_owner) } + let!(:private_event) { create(:event, project: private_project, author: project_owner) } + let!(:internal_event) { create(:event, project: internal_project, author: project_owner) } + let!(:public_event) { create(:event, project: public_project, author: project_owner) } - subject(:finder) { described_class.new(user, project_owner) } + subject(:finder) { described_class.new(current_user, project_owner) } describe '#execute' do - it 'does not include the event when a user does not have access to the project' do - expect(finder.execute).to be_empty + context 'current user does not have access to projects' do + it 'returns public and internal events' do + records = finder.execute + + expect(records).to include(public_event, internal_event) + expect(records).not_to include(private_event) + end end - context 'when the user has access to a project' do + context 'when current user has access to the projects' do before do - project.add_developer(user) + private_project.add_developer(current_user) + internal_project.add_developer(current_user) + public_project.add_developer(current_user) end - it 'includes the event' do - expect(finder.execute).to include(event) + it 'returns all the events' do + expect(finder.execute).to include(private_event, internal_event, public_event) end - it 'does not include the event if the user cannot read cross project' do - expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false } + it 'does not include the events if the user cannot read cross project' do + expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false } expect(finder.execute).to be_empty end end + + context 'when current user is anonymous' do + let(:current_user) { nil } + + it 'returns public events only' do + expect(finder.execute).to eq([public_event]) + end + end end end -- cgit v1.2.1 From 404ef05002e067d057dabafb4945d79a49d028bc Mon Sep 17 00:00:00 2001 From: Leonid Batizhevskii Date: Wed, 6 Jun 2018 15:15:35 +0300 Subject: Added with_statsoption for GET /projects/:id/repository/commits --- .../schemas/public_api/v4/commit/with_stats.json | 14 ++++++++++++++ .../schemas/public_api/v4/commits_with_stats.json | 4 ++++ spec/requests/api/commits_spec.rb | 21 +++++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json create mode 100644 spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json (limited to 'spec') diff --git a/spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json b/spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json new file mode 100644 index 00000000000..3b5dd547e69 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json @@ -0,0 +1,14 @@ +{ + "type": "object", + "allOf": [ + { "$ref": "basic.json" }, + { + "required" : [ + "stats" + ], + "properties": { + "stats": { "$ref": "../commit_stats.json" } + } + } + ] +} diff --git a/spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json b/spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json new file mode 100644 index 00000000000..23511123ce4 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json @@ -0,0 +1,4 @@ +{ + "type": "array", + "items": { "$ref": "commit/with_stats.json" } +} diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 8ad19e3f0f5..fc0c8fdceb8 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -18,14 +18,14 @@ describe API::Commits do describe 'GET /projects/:id/repository/commits' do let(:route) { "/projects/#{project_id}/repository/commits" } - shared_examples_for 'project commits' do + shared_examples_for 'project commits' do |schema: 'public_api/v4/commits'| it "returns project commits" do commit = project.repository.commit get api(route, current_user) expect(response).to have_gitlab_http_status(200) - expect(response).to match_response_schema('public_api/v4/commits') + expect(response).to match_response_schema(schema) expect(json_response.first['id']).to eq(commit.id) expect(json_response.first['committer_name']).to eq(commit.committer_name) expect(json_response.first['committer_email']).to eq(commit.committer_email) @@ -161,6 +161,23 @@ describe API::Commits do end end + context 'with_stats optional parameter' do + let(:project) { create(:project, :public, :repository) } + + it_behaves_like 'project commits', schema: 'public_api/v4/commits_with_stats' do + let(:route) { "/projects/#{project_id}/repository/commits?with_stats=true" } + + it 'include commits details' do + commit = project.repository.commit + get api(route, current_user) + + expect(json_response.first['stats']['additions']).to eq(commit.stats.additions) + expect(json_response.first['stats']['deletions']).to eq(commit.stats.deletions) + expect(json_response.first['stats']['total']).to eq(commit.stats.total) + end + end + end + context 'with pagination params' do let(:page) { 1 } let(:per_page) { 5 } -- cgit v1.2.1 From 3d42bab71ad293c99d029dfb4f0c9aa0378643d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mica=C3=ABl=20Bergeron?= Date: Tue, 12 Jun 2018 11:22:35 -0400 Subject: apply feedback --- .../object_storage/migrate_uploads_worker_spec.rb | 34 +++++++++------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'spec') diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb index 31d323626c5..da490cb02af 100644 --- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb +++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb @@ -1,7 +1,5 @@ require 'spec_helper' -MIGRATION_QUERIES = 5 - describe ObjectStorage::MigrateUploadsWorker, :sidekiq do shared_context 'sanity_check! fails' do before do @@ -127,13 +125,12 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do it_behaves_like "uploads migration worker" describe "limits N+1 queries" do - let!(:projects) { create_list(:project, 10, :with_avatar) } - - it "to N*#{MIGRATION_QUERIES}" do + it "to N*5" do query_count = ActiveRecord::QueryRecorder.new { perform(uploads) } - more_projects = create_list(:project, 100, :with_avatar) - expected_queries_per_migration = MIGRATION_QUERIES * more_projects.count + more_projects = create_list(:project, 3, :with_avatar) + + expected_queries_per_migration = 5 * more_projects.count expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration) end end @@ -144,30 +141,27 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do let(:secret) { SecureRandom.hex } let(:mounted_as) { nil } + def upload_file(project) + uploader = FileUploader.new(project) + uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt')) + end + before do stub_uploads_object_storage(FileUploader) - projects.map do |project| - uploader = FileUploader.new(project) - uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt')) - end + projects.map(&method(:upload_file)) end it_behaves_like "uploads migration worker" describe "limits N+1 queries" do - let!(:projects) { create_list(:project, 10) } - - it "to N*#{MIGRATION_QUERIES}" do + it "to N*5" do query_count = ActiveRecord::QueryRecorder.new { perform(uploads) } - more_projects = create_list(:project, 100) - more_projects.map do |project| - uploader = FileUploader.new(project) - uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt')) - end - expected_queries_per_migration = MIGRATION_QUERIES * more_projects.count + more_projects = create_list(:project, 3) + more_projects.map(&method(:upload_file)) + expected_queries_per_migration = 5 * more_projects.count expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration) end end -- cgit v1.2.1 From 7530e4ec86a686dede8eb8535fd231c1a75d3659 Mon Sep 17 00:00:00 2001 From: Mario de la Ossa Date: Tue, 12 Jun 2018 14:35:45 -0600 Subject: Fix Banzai reference for milestones belonging to parent groups --- spec/lib/banzai/filter/milestone_reference_filter_spec.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index f8fa9b2d13d..91d4a60ba95 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -3,7 +3,8 @@ require 'spec_helper' describe Banzai::Filter::MilestoneReferenceFilter do include FilterSpecHelper - let(:group) { create(:group, :public) } + let(:parent_group) { create(:group, :public) } + let(:group) { create(:group, :public, parent: parent_group) } let(:project) { create(:project, :public, group: group) } it 'requires project context' do @@ -340,6 +341,13 @@ describe Banzai::Filter::MilestoneReferenceFilter do expect(doc.css('a')).to be_empty end + + it 'supports parent group references', :nested_groups do + milestone.update!(group: parent_group) + + doc = reference_filter("See #{reference}") + expect(doc.css('a').first.text).to eq(milestone.name) + end end context 'group context' do -- cgit v1.2.1 From 379a6a709a55f952133354febddf97854c2d5d4d Mon Sep 17 00:00:00 2001 From: Mario de la Ossa Date: Tue, 12 Jun 2018 16:40:16 -0600 Subject: Sidebar Milestone - Fix wrong URL when selecting a parent group milestone --- .../api/schemas/public_api/v4/milestones.json | 3 ++- spec/lib/gitlab/url_builder_spec.rb | 25 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/fixtures/api/schemas/public_api/v4/milestones.json b/spec/fixtures/api/schemas/public_api/v4/milestones.json index c3c42b6ee60..448e97d6c85 100644 --- a/spec/fixtures/api/schemas/public_api/v4/milestones.json +++ b/spec/fixtures/api/schemas/public_api/v4/milestones.json @@ -13,7 +13,8 @@ "created_at": { "type": "date" }, "updated_at": { "type": "date" }, "start_date": { "type": "date" }, - "due_date": { "type": "date" } + "due_date": { "type": "date" }, + "web_url": { "type": "string" } }, "required": [ "id", "iid", "title", "description", "state", diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index b81749cf428..9f495a5d50b 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -22,6 +22,31 @@ describe Gitlab::UrlBuilder do end end + context 'when passing a Milestone' do + let(:group) { create(:group) } + let(:project) { create(:project, :public, namespace: group) } + + context 'belonging to a project' do + it 'returns a proper URL' do + milestone = create(:milestone, project: project) + + url = described_class.build(milestone) + + expect(url).to eq "#{Settings.gitlab['url']}/#{milestone.project.full_path}/milestones/#{milestone.iid}" + end + end + + context 'belonging to a group' do + it 'returns a proper URL' do + milestone = create(:milestone, group: group) + + url = described_class.build(milestone) + + expect(url).to eq "#{Settings.gitlab['url']}/groups/#{milestone.group.full_path}/-/milestones/#{milestone.iid}" + end + end + end + context 'when passing a MergeRequest' do it 'returns a proper URL' do merge_request = build_stubbed(:merge_request, iid: 42) -- cgit v1.2.1 From df45623b3455193395d523bc88deee87b5f6d56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Wed, 13 Jun 2018 08:25:01 +0000 Subject: Restoring user v3 endpoint --- spec/requests/api/users_spec.rb | 73 ++++++++++++---------- .../api/scopes/read_user_shared_examples.rb | 10 +-- 2 files changed, 45 insertions(+), 38 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 3377d67b644..a97c3f3461a 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -1123,58 +1123,63 @@ describe API::Users do describe "GET /user" do let(:personal_access_token) { create(:personal_access_token, user: user).token } - context 'with regular user' do - context 'with personal access token' do - it 'returns 403 without private token when sudo is defined' do - get api("/user?private_token=#{personal_access_token}&sudo=123") + shared_examples 'get user info' do |version| + context 'with regular user' do + context 'with personal access token' do + it 'returns 403 without private token when sudo is defined' do + get api("/user?private_token=#{personal_access_token}&sudo=123", version: version) - expect(response).to have_gitlab_http_status(403) + expect(response).to have_gitlab_http_status(403) + end end - end - it 'returns current user without private token when sudo not defined' do - get api("/user", user) + it 'returns current user without private token when sudo not defined' do + get api("/user", user, version: version) - expect(response).to have_gitlab_http_status(200) - expect(response).to match_response_schema('public_api/v4/user/public') - expect(json_response['id']).to eq(user.id) - end + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/user/public') + expect(json_response['id']).to eq(user.id) + end - context "scopes" do - let(:path) { "/user" } - let(:api_call) { method(:api) } + context "scopes" do + let(:path) { "/user" } + let(:api_call) { method(:api) } - include_examples 'allows the "read_user" scope' + include_examples 'allows the "read_user" scope', version + end end - end - context 'with admin' do - let(:admin_personal_access_token) { create(:personal_access_token, user: admin).token } + context 'with admin' do + let(:admin_personal_access_token) { create(:personal_access_token, user: admin).token } - context 'with personal access token' do - it 'returns 403 without private token when sudo defined' do - get api("/user?private_token=#{admin_personal_access_token}&sudo=#{user.id}") + context 'with personal access token' do + it 'returns 403 without private token when sudo defined' do + get api("/user?private_token=#{admin_personal_access_token}&sudo=#{user.id}", version: version) - expect(response).to have_gitlab_http_status(403) - end + expect(response).to have_gitlab_http_status(403) + end - it 'returns initial current user without private token but with is_admin when sudo not defined' do - get api("/user?private_token=#{admin_personal_access_token}") + it 'returns initial current user without private token but with is_admin when sudo not defined' do + get api("/user?private_token=#{admin_personal_access_token}", version: version) - expect(response).to have_gitlab_http_status(200) - expect(response).to match_response_schema('public_api/v4/user/admin') - expect(json_response['id']).to eq(admin.id) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/user/admin') + expect(json_response['id']).to eq(admin.id) + end end end - end - context 'with unauthenticated user' do - it "returns 401 error if user is unauthenticated" do - get api("/user") + context 'with unauthenticated user' do + it "returns 401 error if user is unauthenticated" do + get api("/user", version: version) - expect(response).to have_gitlab_http_status(401) + expect(response).to have_gitlab_http_status(401) + end end end + + it_behaves_like 'get user info', 'v3' + it_behaves_like 'get user info', 'v4' end describe "GET /user/keys" do diff --git a/spec/support/api/scopes/read_user_shared_examples.rb b/spec/support/api/scopes/read_user_shared_examples.rb index 06ae8792c61..d7cef137989 100644 --- a/spec/support/api/scopes/read_user_shared_examples.rb +++ b/spec/support/api/scopes/read_user_shared_examples.rb @@ -1,10 +1,12 @@ -shared_examples_for 'allows the "read_user" scope' do +shared_examples_for 'allows the "read_user" scope' do |api_version| + let(:version) { api_version || 'v4' } + context 'for personal access tokens' do context 'when the requesting token has the "api" scope' do let(:token) { create(:personal_access_token, scopes: ['api'], user: user) } it 'returns a "200" response' do - get api_call.call(path, user, personal_access_token: token) + get api_call.call(path, user, personal_access_token: token, version: version) expect(response).to have_gitlab_http_status(200) end @@ -14,7 +16,7 @@ shared_examples_for 'allows the "read_user" scope' do let(:token) { create(:personal_access_token, scopes: ['read_user'], user: user) } it 'returns a "200" response' do - get api_call.call(path, user, personal_access_token: token) + get api_call.call(path, user, personal_access_token: token, version: version) expect(response).to have_gitlab_http_status(200) end @@ -28,7 +30,7 @@ shared_examples_for 'allows the "read_user" scope' do end it 'returns a "403" response' do - get api_call.call(path, user, personal_access_token: token) + get api_call.call(path, user, personal_access_token: token, version: version) expect(response).to have_gitlab_http_status(403) end -- cgit v1.2.1 From 9a37ef438e52fd33bb592f1b5cf637ecc2bb962d Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Wed, 13 Jun 2018 19:39:47 +1100 Subject: [Rails5] Fix `storage_counter` helper Since rails 5 beta 2 support of petabytes and exabytes were added to the `number_to_human*` methods. See https://github.com/rails/rails/pull/22759 So for rails5 the comma separator gets shown for big numbers (EBs). --- spec/helpers/storage_helper_spec.rb | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'spec') diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb index 4627a1e1872..c580b78c908 100644 --- a/spec/helpers/storage_helper_spec.rb +++ b/spec/helpers/storage_helper_spec.rb @@ -1,21 +1,25 @@ -require 'spec_helper' +require "spec_helper" describe StorageHelper do - describe '#storage_counter' do - it 'formats bytes to one decimal place' do - expect(helper.storage_counter(1.23.megabytes)).to eq '1.2 MB' + describe "#storage_counter" do + it "formats bytes to one decimal place" do + expect(helper.storage_counter(1.23.megabytes)).to eq("1.2 MB") end - it 'does not add decimals for sizes < 1 MB' do - expect(helper.storage_counter(23.5.kilobytes)).to eq '24 KB' + it "does not add decimals for sizes < 1 MB" do + expect(helper.storage_counter(23.5.kilobytes)).to eq("24 KB") end - it 'does not add decimals for zeroes' do - expect(helper.storage_counter(2.megabytes)).to eq '2 MB' + it "does not add decimals for zeroes" do + expect(helper.storage_counter(2.megabytes)).to eq("2 MB") end - it 'uses commas as thousands separator' do - expect(helper.storage_counter(100_000_000_000_000_000)).to eq '90,949.5 TB' + it "uses commas as thousands separator" do + if Gitlab.rails5? + expect(helper.storage_counter(100_000_000_000_000_000_000_000)).to eq("86,736.2 EB") + else + expect(helper.storage_counter(100_000_000_000_000_000)).to eq("90,949.5 TB") + end end end end -- cgit v1.2.1 From 9d9d952c1172e2b68bd4537a0ce05f96eae7b340 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 13 Jun 2018 10:10:24 +0100 Subject: moved strings into constants file --- spec/javascripts/ide/components/commit_sidebar/list_spec.js | 1 + 1 file changed, 1 insertion(+) (limited to 'spec') diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js index a37cbd5a596..6fb52378386 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js @@ -18,6 +18,7 @@ describe('Multi-file editor commit sidebar list', () => { actionBtnText: 'stage all', itemActionComponent: 'stage-button', activeFileKey: 'staged-testing', + keyPrefix: 'staged', }); vm.$store.state.rightPanelCollapsed = false; -- cgit v1.2.1 From cf22366d79e2a3a332de3d7729c14dcffe45cd98 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Jun 2018 14:12:39 +0100 Subject: Resets state correctly when switching to merge request in IDE Closes #47763 --- spec/javascripts/ide/components/repo_editor_spec.js | 11 +++++++++++ .../ide/stores/modules/merge_requests/actions_spec.js | 13 +++++++------ .../ide/stores/modules/pipelines/actions_spec.js | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index d318521d0a0..2256deb7dac 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -315,6 +315,17 @@ describe('RepoEditor', () => { done(); }); }); + + it('calls updateDimensions when rightPane is updated', done => { + vm.$store.state.rightPane = 'testing'; + + vm.$nextTick(() => { + expect(vm.editor.updateDimensions).toHaveBeenCalled(); + expect(vm.editor.updateDiffView).toHaveBeenCalled(); + + done(); + }); + }); }); describe('show tabs', () => { diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js index 03ec08d05c3..fa4c18931e5 100644 --- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js @@ -208,18 +208,19 @@ describe('IDE merge requests actions', () => { expect(commit.calls.argsFor(1)).toEqual(['SET_CURRENT_MERGE_REQUEST', '1', { root: true }]); expect(commit.calls.argsFor(2)).toEqual(['RESET_OPEN_FILES', null, { root: true }]); - expect(dispatch.calls.argsFor(0)).toEqual([ - 'pipelines/resetLatestPipeline', + expect(dispatch.calls.argsFor(0)).toEqual(['setCurrentBranchId', '', { root: true }]); + expect(dispatch.calls.argsFor(1)).toEqual([ + 'pipelines/stopPipelinePolling', null, { root: true }, ]); - expect(dispatch.calls.argsFor(1)).toEqual(['setCurrentBranchId', '', { root: true }]); - expect(dispatch.calls.argsFor(2)).toEqual([ - 'pipelines/stopPipelinePolling', + expect(dispatch.calls.argsFor(2)).toEqual(['setRightPane', null, { root: true }]); + expect(dispatch.calls.argsFor(3)).toEqual([ + 'pipelines/resetLatestPipeline', null, { root: true }, ]); - expect(dispatch.calls.argsFor(3)).toEqual([ + expect(dispatch.calls.argsFor(4)).toEqual([ 'pipelines/clearEtagPoll', null, { root: true }, diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js index f2f8e780cd1..f47e69d6e5b 100644 --- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js @@ -18,6 +18,7 @@ import actions, { receiveJobTraceError, receiveJobTraceSuccess, fetchJobTrace, + resetLatestPipeline, } from '~/ide/stores/modules/pipelines/actions'; import state from '~/ide/stores/modules/pipelines/state'; import * as types from '~/ide/stores/modules/pipelines/mutation_types'; @@ -416,4 +417,20 @@ describe('IDE pipelines actions', () => { }); }); }); + + describe('resetLatestPipeline', () => { + it('commits reset mutations', done => { + testAction( + resetLatestPipeline, + null, + mockedState, + [ + { type: types.RECEIVE_LASTEST_PIPELINE_SUCCESS, payload: null }, + { type: types.SET_DETAIL_JOB, payload: null }, + ], + [], + done, + ); + }); + }); }); -- cgit v1.2.1 From 8a23bcc9bc0f7ab453ee09d41a9407d40d57ba4c Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Wed, 13 Jun 2018 09:35:52 +0000 Subject: Image Diff Viewing + Download Diff Viewing --- spec/javascripts/fixtures/images/green_box.png | Bin 0 -> 1306 bytes spec/javascripts/fixtures/images/red_box.png | Bin 0 -> 1305 bytes spec/javascripts/ide/stores/mutations/file_spec.js | 47 ++++++ spec/javascripts/test_constants.js | 3 + .../content_viewer/content_viewer_spec.js | 10 +- .../components/diff_viewer/diff_viewer_spec.js | 70 ++++++++ .../diff_viewer/viewers/image_diff_viewer_spec.js | 185 +++++++++++++++++++++ .../components/lib/utils/dom_utils_spec.js | 13 ++ 8 files changed, 323 insertions(+), 5 deletions(-) create mode 100644 spec/javascripts/fixtures/images/green_box.png create mode 100644 spec/javascripts/fixtures/images/red_box.png create mode 100644 spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js create mode 100644 spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js create mode 100644 spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js (limited to 'spec') diff --git a/spec/javascripts/fixtures/images/green_box.png b/spec/javascripts/fixtures/images/green_box.png new file mode 100644 index 00000000000..cd1ff9f9ade Binary files /dev/null and b/spec/javascripts/fixtures/images/green_box.png differ diff --git a/spec/javascripts/fixtures/images/red_box.png b/spec/javascripts/fixtures/images/red_box.png new file mode 100644 index 00000000000..73b2927da0f Binary files /dev/null and b/spec/javascripts/fixtures/images/red_box.png differ diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js index e83961fcedc..52f83be8e8c 100644 --- a/spec/javascripts/ide/stores/mutations/file_spec.js +++ b/spec/javascripts/ide/stores/mutations/file_spec.js @@ -152,6 +152,53 @@ describe('IDE store file mutations', () => { expect(localFile.mrChange.diff).toBe('ABC'); }); + + it('has diffMode replaced by default', () => { + mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { + file: localFile, + mrChange: { + diff: 'ABC', + }, + }); + + expect(localFile.mrChange.diffMode).toBe('replaced'); + }); + + it('has diffMode new', () => { + mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { + file: localFile, + mrChange: { + diff: 'ABC', + new_file: true, + }, + }); + + expect(localFile.mrChange.diffMode).toBe('new'); + }); + + it('has diffMode deleted', () => { + mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { + file: localFile, + mrChange: { + diff: 'ABC', + deleted_file: true, + }, + }); + + expect(localFile.mrChange.diffMode).toBe('deleted'); + }); + + it('has diffMode renamed', () => { + mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, { + file: localFile, + mrChange: { + diff: 'ABC', + renamed_file: true, + }, + }); + + expect(localFile.mrChange.diffMode).toBe('renamed'); + }); }); describe('DISCARD_FILE_CHANGES', () => { diff --git a/spec/javascripts/test_constants.js b/spec/javascripts/test_constants.js index df59195e9f6..a820dd2d09c 100644 --- a/spec/javascripts/test_constants.js +++ b/spec/javascripts/test_constants.js @@ -2,3 +2,6 @@ export const FIXTURES_PATH = '/base/spec/javascripts/fixtures'; export const TEST_HOST = 'http://test.host'; export const DUMMY_IMAGE_URL = `${FIXTURES_PATH}/one_white_pixel.png`; + +export const GREEN_BOX_IMAGE_URL = `${FIXTURES_PATH}/images/green_box.png`; +export const RED_BOX_IMAGE_URL = `${FIXTURES_PATH}/images/red_box.png`; diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js index 383f0cd29ea..e2c34508b0d 100644 --- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js @@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; describe('ContentViewer', () => { let vm; @@ -41,12 +42,12 @@ describe('ContentViewer', () => { it('renders image preview', done => { createComponent({ - path: 'test.jpg', + path: GREEN_BOX_IMAGE_URL, fileSize: 1024, }); setTimeout(() => { - expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe('test.jpg'); + expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL); done(); }); @@ -59,9 +60,8 @@ describe('ContentViewer', () => { }); setTimeout(() => { - expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain( - 'test.abc (1.00 KiB)', - ); + expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain('test.abc'); + expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain('(1.00 KiB)'); expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toContain('Download'); done(); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js new file mode 100644 index 00000000000..71d9145bf22 --- /dev/null +++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js @@ -0,0 +1,70 @@ +import Vue from 'vue'; +import diffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; + +describe('DiffViewer', () => { + let vm; + + function createComponent(props) { + const DiffViewer = Vue.extend(diffViewer); + vm = mountComponent(DiffViewer, props); + } + + afterEach(() => { + vm.$destroy(); + }); + + it('renders image diff', done => { + window.gon = { + relative_url_root: '', + }; + + createComponent({ + diffMode: 'replaced', + newPath: GREEN_BOX_IMAGE_URL, + newSha: 'ABC', + oldPath: RED_BOX_IMAGE_URL, + oldSha: 'DEF', + projectPath: '', + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe( + `//raw/DEF/${RED_BOX_IMAGE_URL}`, + ); + + expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe( + `//raw/ABC/${GREEN_BOX_IMAGE_URL}`, + ); + + done(); + }); + }); + + it('renders fallback download diff display', done => { + createComponent({ + diffMode: 'replaced', + newPath: 'test.abc', + newSha: 'ABC', + oldPath: 'testold.abc', + oldSha: 'DEF', + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain( + 'testold.abc', + ); + expect(vm.$el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain( + 'Download', + ); + + expect(vm.$el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc'); + expect(vm.$el.querySelector('.added .btn.btn-default').textContent.trim()).toContain( + 'Download', + ); + + done(); + }); + }); +}); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js new file mode 100644 index 00000000000..b878286ae3f --- /dev/null +++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js @@ -0,0 +1,185 @@ +import Vue from 'vue'; +import imageDiffViewer from '~/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; + +describe('ImageDiffViewer', () => { + let vm; + + function createComponent(props) { + const ImageDiffViewer = Vue.extend(imageDiffViewer); + vm = mountComponent(ImageDiffViewer, props); + } + + const triggerEvent = (eventName, el = vm.$el, clientX = 0) => { + const event = document.createEvent('MouseEvents'); + event.initMouseEvent( + eventName, + true, + true, + window, + 1, + clientX, + 0, + clientX, + 0, + false, + false, + false, + false, + 0, + null, + ); + + el.dispatchEvent(event); + }; + + const dragSlider = (sliderElement, dragPixel = 20) => { + triggerEvent('mousedown', sliderElement); + triggerEvent('mousemove', document.body, dragPixel); + triggerEvent('mouseup', document.body); + }; + + afterEach(() => { + vm.$destroy(); + }); + + it('renders image diff for replaced', done => { + createComponent({ + diffMode: 'replaced', + newPath: GREEN_BOX_IMAGE_URL, + oldPath: RED_BOX_IMAGE_URL, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe( + GREEN_BOX_IMAGE_URL, + ); + expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe( + RED_BOX_IMAGE_URL, + ); + + expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe('2-up'); + expect(vm.$el.querySelector('.view-modes-menu li:nth-child(2)').textContent.trim()).toBe( + 'Swipe', + ); + expect(vm.$el.querySelector('.view-modes-menu li:nth-child(3)').textContent.trim()).toBe( + 'Onion skin', + ); + + done(); + }); + }); + + it('renders image diff for new', done => { + createComponent({ + diffMode: 'new', + newPath: GREEN_BOX_IMAGE_URL, + oldPath: '', + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe( + GREEN_BOX_IMAGE_URL, + ); + + done(); + }); + }); + + it('renders image diff for deleted', done => { + createComponent({ + diffMode: 'deleted', + newPath: '', + oldPath: RED_BOX_IMAGE_URL, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe( + RED_BOX_IMAGE_URL, + ); + + done(); + }); + }); + + describe('swipeMode', () => { + beforeEach(done => { + createComponent({ + diffMode: 'replaced', + newPath: GREEN_BOX_IMAGE_URL, + oldPath: RED_BOX_IMAGE_URL, + }); + + setTimeout(() => { + done(); + }); + }); + + it('switches to Swipe Mode', done => { + vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click(); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe('Swipe'); + done(); + }); + }); + + it('drag handler is working', done => { + vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click(); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('1px'); + expect(vm.$el.querySelector('.top-handle')).not.toBeNull(); + + dragSlider(vm.$el.querySelector('.swipe-bar'), 40); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('-20px'); + done(); + }); + }); + }); + }); + + describe('onionSkin', () => { + beforeEach(done => { + createComponent({ + diffMode: 'replaced', + newPath: GREEN_BOX_IMAGE_URL, + oldPath: RED_BOX_IMAGE_URL, + }); + + setTimeout(() => { + done(); + }); + }); + + it('switches to Onion Skin Mode', done => { + vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click(); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe( + 'Onion skin', + ); + done(); + }); + }); + + it('has working drag handler', done => { + vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click(); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.dragger').style.left).toBe('100px'); + + dragSlider(vm.$el.querySelector('.dragger')); + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.dragger').style.left).toBe('20px'); + expect(vm.$el.querySelector('.added.frame').style.opacity).toBe('0.2'); + done(); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js b/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js new file mode 100644 index 00000000000..2388660b0c2 --- /dev/null +++ b/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js @@ -0,0 +1,13 @@ +import * as domUtils from '~/vue_shared/components/lib/utils/dom_utils'; + +describe('domUtils', () => { + describe('pixeliseValue', () => { + it('should add px to a given Number', () => { + expect(domUtils.pixeliseValue(12)).toEqual('12px'); + }); + + it('should not add px to 0', () => { + expect(domUtils.pixeliseValue(0)).toEqual(''); + }); + }); +}); -- cgit v1.2.1 From 1721bbcb013eed3c98c81d615087dd79dddb65be Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 13 Jun 2018 10:23:57 +0000 Subject: Resolve "Quick actions are case sensitive" --- spec/lib/gitlab/quick_actions/extractor_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/quick_actions/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb index f7c288f2393..0166f6c2ee0 100644 --- a/spec/lib/gitlab/quick_actions/extractor_spec.rb +++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb @@ -182,6 +182,14 @@ describe Gitlab::QuickActions::Extractor do expect(msg).to eq "hello\nworld" end + it 'extracts command case insensitive' do + msg = %(hello\n/PoWer @user.name %9.10 ~"bar baz.2"\nworld) + msg, commands = extractor.extract_commands(msg) + + expect(commands).to eq [['power', '@user.name %9.10 ~"bar baz.2"']] + expect(msg).to eq "hello\nworld" + end + it 'does not extract noop commands' do msg = %(hello\nworld\n/reopen\n/noop_command) msg, commands = extractor.extract_commands(msg) @@ -206,6 +214,14 @@ describe Gitlab::QuickActions::Extractor do expect(msg).to eq "hello\nworld\nthis is great? SHRUG" end + it 'extracts and performs substitution commands case insensitive' do + msg = %(hello\nworld\n/reOpen\n/sHRuG this is great?) + msg, commands = extractor.extract_commands(msg) + + expect(commands).to eq [['reopen'], ['shrug', 'this is great?']] + expect(msg).to eq "hello\nworld\nthis is great? SHRUG" + end + it 'extracts and performs substitution commands with comments' do msg = %(hello\nworld\n/reopen\n/substitution wow this is a thing.) msg, commands = extractor.extract_commands(msg) -- cgit v1.2.1 From 4077d4f735ed40b7a62baa35464677813c3b6784 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 13 Jun 2018 10:28:27 +0000 Subject: Resolve "Provide ability to retrieve `visibility` level via Snippets API" --- spec/fixtures/api/schemas/public_api/v4/snippets.json | 1 + spec/requests/api/snippets_spec.rb | 3 +++ 2 files changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/fixtures/api/schemas/public_api/v4/snippets.json b/spec/fixtures/api/schemas/public_api/v4/snippets.json index e37e9704649..d13d703e063 100644 --- a/spec/fixtures/api/schemas/public_api/v4/snippets.json +++ b/spec/fixtures/api/schemas/public_api/v4/snippets.json @@ -8,6 +8,7 @@ "title": { "type": "string" }, "file_name": { "type": ["string", "null"] }, "description": { "type": ["string", "null"] }, + "visibility": { "type": "string" }, "web_url": { "type": "string" }, "created_at": { "type": "date" }, "updated_at": { "type": "date" }, diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb index b3e253befc6..c5456977b60 100644 --- a/spec/requests/api/snippets_spec.rb +++ b/spec/requests/api/snippets_spec.rb @@ -20,6 +20,7 @@ describe API::Snippets do private_snippet.id) expect(json_response.last).to have_key('web_url') expect(json_response.last).to have_key('raw_url') + expect(json_response.last).to have_key('visibility') end it 'hides private snippets from regular user' do @@ -112,6 +113,7 @@ describe API::Snippets do expect(json_response['title']).to eq(snippet.title) expect(json_response['description']).to eq(snippet.description) expect(json_response['file_name']).to eq(snippet.file_name) + expect(json_response['visibility']).to eq(snippet.visibility) end it 'returns 404 for invalid snippet id' do @@ -142,6 +144,7 @@ describe API::Snippets do expect(json_response['title']).to eq(params[:title]) expect(json_response['description']).to eq(params[:description]) expect(json_response['file_name']).to eq(params[:file_name]) + expect(json_response['visibility']).to eq(params[:visibility]) end it 'returns 400 for missing parameters' do -- cgit v1.2.1 From 6d902e07d908769a909db0bf93d84f5be28720af Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 13 Jun 2018 13:21:12 +0200 Subject: Fix shoulda-matchers in Rails 5 Backports a fix for https://github.com/thoughtbot/shoulda-matchers/issues/913. This can be removed once new shoulda-matchers version is released. --- spec/support/shoulda/matchers/rails_shim.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 spec/support/shoulda/matchers/rails_shim.rb (limited to 'spec') diff --git a/spec/support/shoulda/matchers/rails_shim.rb b/spec/support/shoulda/matchers/rails_shim.rb new file mode 100644 index 00000000000..8d70598beb5 --- /dev/null +++ b/spec/support/shoulda/matchers/rails_shim.rb @@ -0,0 +1,27 @@ +# monkey patch which fixes serialization matcher in Rails 5 +# https://github.com/thoughtbot/shoulda-matchers/issues/913 +# This can be removed when a new version of shoulda-matchers +# is released +module Shoulda + module Matchers + class RailsShim + def self.serialized_attributes_for(model) + if defined?(::ActiveRecord::Type::Serialized) + # Rails 5+ + serialized_columns = model.columns.select do |column| + model.type_for_attribute(column.name).is_a?( + ::ActiveRecord::Type::Serialized + ) + end + + serialized_columns.inject({}) do |hash, column| # rubocop:disable Style/EachWithObject + hash[column.name.to_s] = model.type_for_attribute(column.name).coder + hash + end + else + model.serialized_attributes + end + end + end + end +end -- cgit v1.2.1 From 5ba9279c5039615fd797beab273f3c1aeb642dd0 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 13 Jun 2018 12:42:36 +0000 Subject: Resolve "Add `/confidential` quick action for issues" --- .../issues/user_uses_slash_commands_spec.rb | 36 ++++++++++++++++++++++ .../quick_actions/interpret_service_spec.rb | 18 +++++++++++ 2 files changed, 54 insertions(+) (limited to 'spec') diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index fd0aa6cf3a3..dacca494755 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -153,6 +153,42 @@ feature 'Issues > User uses quick actions', :js do end end + describe 'make issue confidential' do + let(:issue) { create(:issue, project: project) } + let(:original_issue) { create(:issue, project: project) } + + context 'when the current user can update issues' do + it 'does not create a note, and marks the issue as confidential' do + add_note("/confidential") + + expect(page).not_to have_content "/confidential" + expect(page).to have_content 'Commands applied' + expect(page).to have_content "made the issue confidential" + + expect(issue.reload).to be_confidential + end + end + + context 'when the current user cannot update the issue' do + let(:guest) { create(:user) } + before do + project.add_guest(guest) + gitlab_sign_out + sign_in(guest) + visit project_issue_path(project, issue) + end + + it 'does not create a note, and does not mark the issue as confidential' do + add_note("/confidential") + + expect(page).not_to have_content 'Commands applied' + expect(page).not_to have_content "made the issue confidential" + + expect(issue.reload).not_to be_confidential + end + end + end + describe 'move the issue to another project' do let(:issue) { create(:issue, project: project) } diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index bd835a1fca6..743e281183e 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -323,6 +323,14 @@ describe QuickActions::InterpretService do end end + shared_examples 'confidential command' do + it 'marks issue as confidential if content contains /confidential' do + _, updates = service.execute(content, issuable) + + expect(updates).to eq(confidential: true) + end + end + shared_examples 'shrug command' do it 'appends ¯\_(ツ)_/¯ to the comment' do new_content, _ = service.execute(content, issuable) @@ -774,6 +782,11 @@ describe QuickActions::InterpretService do let(:issuable) { issue } end + it_behaves_like 'confidential command' do + let(:content) { '/confidential' } + let(:issuable) { issue } + end + context '/copy_metadata command' do let(:todo_label) { create(:label, project: project, title: 'To Do') } let(:inreview_label) { create(:label, project: project, title: 'In Review') } @@ -918,6 +931,11 @@ describe QuickActions::InterpretService do let(:issuable) { issue } end + it_behaves_like 'empty command' do + let(:content) { '/confidential' } + let(:issuable) { issue } + end + it_behaves_like 'empty command' do let(:content) { '/duplicate #{issue.to_reference}' } let(:issuable) { issue } -- cgit v1.2.1 From e4a9b0771abdcd4e478cfc763ca7fcecc99ae505 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 13 Jun 2018 15:23:34 +0200 Subject: ListCommitByOid isn't called with an empty batch Batching commits for performance improvements, might lead to empty batches being used. This isn't the case yet, but to guard against this in future cases, a guard clause is added. --- spec/lib/gitlab/git/commit_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 5af982c7a54..ae69a362dda 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -421,6 +421,16 @@ describe Gitlab::Git::Commit, seed_helper: true do end end + describe '#batch_by_oid' do + context 'when oids is empty' do + it 'makes no Gitaly request' do + expect(Gitlab::GitalyClient).not_to receive(:call) + + described_class.batch_by_oid(repository, []) + end + end + end + shared_examples 'extracting commit signature' do context 'when the commit is signed' do let(:commit_id) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } -- cgit v1.2.1 From 0e2577229d8e12fdb02a156fcc6672ebc6bb3f5d Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 13 Jun 2018 16:36:43 +0200 Subject: Move GC RPCs to mandatory Closes https://gitlab.com/gitlab-org/gitaly/issues/354 --- spec/workers/git_garbage_collect_worker_spec.rb | 117 +++++++++--------------- 1 file changed, 45 insertions(+), 72 deletions(-) (limited to 'spec') diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 807d1b8c084..e39dec556fc 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -11,36 +11,63 @@ describe GitGarbageCollectWorker do subject { described_class.new } describe "#perform" do - shared_examples 'flushing ref caches' do |gitaly| - context 'with active lease_uuid' do + context 'with active lease_uuid' do + before do + allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid) + end + + it "flushes ref caches when the task if 'gc'" do + expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect) + .and_return(nil) + expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original + expect_any_instance_of(Repository).to receive(:branch_names).and_call_original + expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original + expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original + + subject.perform(project.id, :gc, lease_key, lease_uuid) + end + end + + context 'with different lease than the active one' do + before do + allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid) + end + + it 'returns silently' do + expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original + expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original + expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original + + subject.perform(project.id, :gc, lease_key, lease_uuid) + end + end + + context 'with no active lease' do + before do + allow(subject).to receive(:get_lease_uuid).and_return(false) + end + + context 'when is able to get the lease' do before do - allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid) + allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid) end it "flushes ref caches when the task if 'gc'" do - expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original - expect(subject).to receive(:command).with(:gc).and_return([:the, :command]) - - if gitaly - expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect) - .and_return(nil) - else - expect(Gitlab::Popen).to receive(:popen) - .with([:the, :command], project.repository.path_to_repo).and_return(["", 0]) - end - + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect) + .and_return(nil) expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original expect_any_instance_of(Repository).to receive(:branch_names).and_call_original expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original - subject.perform(project.id, :gc, lease_key, lease_uuid) + subject.perform(project.id) end end - context 'with different lease than the active one' do + context 'when no lease can be obtained' do before do - allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid) + expect(subject).to receive(:try_obtain_lease).and_return(false) end it 'returns silently' do @@ -49,63 +76,9 @@ describe GitGarbageCollectWorker do expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original - subject.perform(project.id, :gc, lease_key, lease_uuid) + subject.perform(project.id) end end - - context 'with no active lease' do - before do - allow(subject).to receive(:get_lease_uuid).and_return(false) - end - - context 'when is able to get the lease' do - before do - allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid) - end - - it "flushes ref caches when the task if 'gc'" do - expect(subject).to receive(:command).with(:gc).and_return([:the, :command]) - - if gitaly - expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect) - .and_return(nil) - else - expect(Gitlab::Popen).to receive(:popen) - .with([:the, :command], project.repository.path_to_repo).and_return(["", 0]) - end - - expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original - expect_any_instance_of(Repository).to receive(:branch_names).and_call_original - expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original - expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original - - subject.perform(project.id) - end - end - - context 'when no lease can be obtained' do - before do - expect(subject).to receive(:try_obtain_lease).and_return(false) - end - - it 'returns silently' do - expect(subject).not_to receive(:command) - expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original - expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original - expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original - - subject.perform(project.id) - end - end - end - end - - context "with Gitaly turned on" do - it_should_behave_like 'flushing ref caches', true - end - - context "with Gitaly turned off", :disable_gitaly do - it_should_behave_like 'flushing ref caches', false end context "repack_full" do -- cgit v1.2.1 From f195a7436d8848cccc8888f2de047547fa9eb94f Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 13 Jun 2018 16:44:59 +0200 Subject: RawBlame only called through Gitaly Closes https://gitlab.com/gitlab-org/gitaly/issues/376 --- spec/lib/gitlab/git/blame_spec.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/blame_spec.rb b/spec/lib/gitlab/git/blame_spec.rb index 793228701cf..ba790b717ae 100644 --- a/spec/lib/gitlab/git/blame_spec.rb +++ b/spec/lib/gitlab/git/blame_spec.rb @@ -7,7 +7,7 @@ describe Gitlab::Git::Blame, seed_helper: true do Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md") end - shared_examples 'blaming a file' do + describe 'blaming a file' do context "each count" do it do data = [] @@ -68,12 +68,4 @@ describe Gitlab::Git::Blame, seed_helper: true do end end end - - context 'when Gitaly blame feature is enabled' do - it_behaves_like 'blaming a file' - end - - context 'when Gitaly blame feature is disabled', :skip_gitaly_mock do - it_behaves_like 'blaming a file' - end end -- cgit v1.2.1 From 7fe92d998125d3dc8be3544346de8dbd5c64b240 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Wed, 13 Jun 2018 16:05:55 +0200 Subject: Render access denied without message The `errors/access_denied` page should not fail to render when no message is provided. When accessing something as a sessionless user, we should also display the terms message if possible. --- spec/controllers/application_controller_spec.rb | 10 ++++++++++ spec/views/errors/access_denied.html.haml_spec.rb | 7 +++++++ 2 files changed, 17 insertions(+) create mode 100644 spec/views/errors/access_denied.html.haml_spec.rb (limited to 'spec') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 773bf25ed44..fbafb4a4de8 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -458,6 +458,8 @@ describe ApplicationController do end context 'for sessionless users' do + render_views + before do sign_out user end @@ -468,6 +470,14 @@ describe ApplicationController do expect(response).to have_gitlab_http_status(403) end + it 'renders the error message when the format was html' do + get :index, + private_token: create(:personal_access_token, user: user).token, + format: :html + + expect(response.body).to have_content /accept the terms of service/i + end + it 'renders a 200 when the sessionless user accepted the terms' do accept_terms(user) diff --git a/spec/views/errors/access_denied.html.haml_spec.rb b/spec/views/errors/access_denied.html.haml_spec.rb new file mode 100644 index 00000000000..bde2f6f0169 --- /dev/null +++ b/spec/views/errors/access_denied.html.haml_spec.rb @@ -0,0 +1,7 @@ +require 'spec_helper' + +describe 'errors/access_denied' do + it 'does not fail to render when there is no message provided' do + expect { render }.not_to raise_error + end +end -- cgit v1.2.1 From b6996837ea992a98018caa0210f10ae6f3bcc9c0 Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 13 Jun 2018 17:35:39 +0200 Subject: Rails5 fix stack level too deep --- spec/controllers/application_controller_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 773bf25ed44..b35b220b440 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -502,7 +502,7 @@ describe ApplicationController do context '422 errors' do it 'logs a response with a string' do - response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json') + response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json', cookies: {}) allow(controller).to receive(:response).and_return(response) get :index @@ -511,7 +511,7 @@ describe ApplicationController do it 'logs a response with an array' do body = ['I want', 'my hat back'] - response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json') + response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json', cookies: {}) allow(controller).to receive(:response).and_return(response) get :index @@ -519,7 +519,7 @@ describe ApplicationController do end it 'does not log a string with an empty body' do - response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json') + response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json', cookies: {}) allow(controller).to receive(:response).and_return(response) get :index @@ -527,7 +527,7 @@ describe ApplicationController do end it 'does not log an HTML body' do - response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html') + response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html', cookies: {}) allow(controller).to receive(:response).and_return(response) get :index -- cgit v1.2.1 From 71e0d33e74fb5c571032be641130105fd3f6a4b6 Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 13 Jun 2018 17:48:27 +0200 Subject: Rails5 ActionController::ParameterMissing: param is missing or the value is empty: application_setting --- spec/controllers/admin/application_settings_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index b4fc2aa326f..9d10d725ff3 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -73,7 +73,7 @@ describe Admin::ApplicationSettingsController do end it 'updates the restricted_visibility_levels when empty array is passed' do - put :update, application_setting: { restricted_visibility_levels: [] } + put :update, application_setting: { restricted_visibility_levels: [""] } expect(response).to redirect_to(admin_application_settings_path) expect(ApplicationSetting.current.restricted_visibility_levels).to be_empty -- cgit v1.2.1 From 6a0178d8324964489654eb26fbefcb90bbcd1147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 13 Jun 2018 11:57:18 -0500 Subject: Invalidate cache with project details when repository is updated The partial using this cache key was showing stale information due to the cache not being invalidated. --- spec/helpers/projects_helper_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index d372e58f63d..815b4035114 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -90,6 +90,10 @@ describe ProjectsHelper do expect(helper.project_list_cache_key(project)).to include(project.cache_key) end + it "includes the last activity date" do + expect(helper.project_list_cache_key(project)).to include(project.last_activity_date) + end + it "includes the controller name" do expect(helper.controller).to receive(:controller_name).and_return("testcontroller") -- cgit v1.2.1 From 2f40fb456bbc99fa4d3816696dd9ac612a21482c Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Wed, 13 Jun 2018 17:11:43 +0000 Subject: Add support for verifying remote uploads, artifacts, and LFS objects in check rake tasks --- spec/lib/gitlab/verify/job_artifacts_spec.rb | 29 +++++++++++++++++++++++++--- spec/lib/gitlab/verify/lfs_objects_spec.rb | 25 +++++++++++++++--------- spec/lib/gitlab/verify/uploads_spec.rb | 27 ++++++++++++++++---------- 3 files changed, 59 insertions(+), 22 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/verify/job_artifacts_spec.rb b/spec/lib/gitlab/verify/job_artifacts_spec.rb index ec490bdfde2..6e916a56564 100644 --- a/spec/lib/gitlab/verify/job_artifacts_spec.rb +++ b/spec/lib/gitlab/verify/job_artifacts_spec.rb @@ -21,15 +21,38 @@ describe Gitlab::Verify::JobArtifacts do FileUtils.rm_f(artifact.file.path) expect(failures.keys).to contain_exactly(artifact) - expect(failure).to be_a(Errno::ENOENT) - expect(failure.to_s).to include(artifact.file.path) + expect(failure).to include('No such file or directory') + expect(failure).to include(artifact.file.path) end it 'fails artifacts with a mismatched checksum' do File.truncate(artifact.file.path, 0) expect(failures.keys).to contain_exactly(artifact) - expect(failure.to_s).to include('Checksum mismatch') + expect(failure).to include('Checksum mismatch') + end + + context 'with remote files' do + let(:file) { double(:file) } + + before do + stub_artifacts_object_storage + artifact.update!(file_store: ObjectStorage::Store::REMOTE) + expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file) + end + + it 'passes artifacts in object storage that exist' do + expect(file).to receive(:exists?).and_return(true) + + expect(failures).to eq({}) + end + + it 'fails artifacts in object storage that do not exist' do + expect(file).to receive(:exists?).and_return(false) + + expect(failures.keys).to contain_exactly(artifact) + expect(failure).to include('Remote object does not exist') + end end end end diff --git a/spec/lib/gitlab/verify/lfs_objects_spec.rb b/spec/lib/gitlab/verify/lfs_objects_spec.rb index 0f890e2c7ce..2feaedd6f14 100644 --- a/spec/lib/gitlab/verify/lfs_objects_spec.rb +++ b/spec/lib/gitlab/verify/lfs_objects_spec.rb @@ -21,30 +21,37 @@ describe Gitlab::Verify::LfsObjects do FileUtils.rm_f(lfs_object.file.path) expect(failures.keys).to contain_exactly(lfs_object) - expect(failure).to be_a(Errno::ENOENT) - expect(failure.to_s).to include(lfs_object.file.path) + expect(failure).to include('No such file or directory') + expect(failure).to include(lfs_object.file.path) end it 'fails LFS objects with a mismatched oid' do File.truncate(lfs_object.file.path, 0) expect(failures.keys).to contain_exactly(lfs_object) - expect(failure.to_s).to include('Checksum mismatch') + expect(failure).to include('Checksum mismatch') end context 'with remote files' do + let(:file) { double(:file) } + before do stub_lfs_object_storage + lfs_object.update!(file_store: ObjectStorage::Store::REMOTE) + expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file) end - it 'skips LFS objects in object storage' do - local_failure = create(:lfs_object) - create(:lfs_object, :object_storage) + it 'passes LFS objects in object storage that exist' do + expect(file).to receive(:exists?).and_return(true) + + expect(failures).to eq({}) + end - failures = {} - described_class.new(batch_size: 10).run_batches { |_, failed| failures.merge!(failed) } + it 'fails LFS objects in object storage that do not exist' do + expect(file).to receive(:exists?).and_return(false) - expect(failures.keys).to contain_exactly(local_failure) + expect(failures.keys).to contain_exactly(lfs_object) + expect(failure).to include('Remote object does not exist') end end end diff --git a/spec/lib/gitlab/verify/uploads_spec.rb b/spec/lib/gitlab/verify/uploads_spec.rb index 85768308edc..296866d3319 100644 --- a/spec/lib/gitlab/verify/uploads_spec.rb +++ b/spec/lib/gitlab/verify/uploads_spec.rb @@ -23,37 +23,44 @@ describe Gitlab::Verify::Uploads do FileUtils.rm_f(upload.absolute_path) expect(failures.keys).to contain_exactly(upload) - expect(failure).to be_a(Errno::ENOENT) - expect(failure.to_s).to include(upload.absolute_path) + expect(failure).to include('No such file or directory') + expect(failure).to include(upload.absolute_path) end it 'fails uploads with a mismatched checksum' do upload.update!(checksum: 'something incorrect') expect(failures.keys).to contain_exactly(upload) - expect(failure.to_s).to include('Checksum mismatch') + expect(failure).to include('Checksum mismatch') end it 'fails uploads with a missing precalculated checksum' do upload.update!(checksum: '') expect(failures.keys).to contain_exactly(upload) - expect(failure.to_s).to include('Checksum missing') + expect(failure).to include('Checksum missing') end context 'with remote files' do + let(:file) { double(:file) } + before do stub_uploads_object_storage(AvatarUploader) + upload.update!(store: ObjectStorage::Store::REMOTE) + expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file) end - it 'skips uploads in object storage' do - local_failure = create(:upload) - create(:upload, :object_storage) + it 'passes uploads in object storage that exist' do + expect(file).to receive(:exists?).and_return(true) + + expect(failures).to eq({}) + end - failures = {} - described_class.new(batch_size: 10).run_batches { |_, failed| failures.merge!(failed) } + it 'fails uploads in object storage that do not exist' do + expect(file).to receive(:exists?).and_return(false) - expect(failures.keys).to contain_exactly(local_failure) + expect(failures.keys).to contain_exactly(upload) + expect(failure).to include('Remote object does not exist') end end end -- cgit v1.2.1 From fa29bc2899193c5c5d8f92b54d8ade1a4230c84e Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Wed, 13 Jun 2018 22:48:59 +0000 Subject: Fix branch name encoding for dropdown on issue page --- .../create_merge_request_dropdown_spec.js | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 spec/javascripts/create_merge_request_dropdown_spec.js (limited to 'spec') diff --git a/spec/javascripts/create_merge_request_dropdown_spec.js b/spec/javascripts/create_merge_request_dropdown_spec.js new file mode 100644 index 00000000000..b229765a8c5 --- /dev/null +++ b/spec/javascripts/create_merge_request_dropdown_spec.js @@ -0,0 +1,67 @@ +import axios from '~/lib/utils/axios_utils'; +import MockAdapter from 'axios-mock-adapter'; +import CreateMergeRequestDropdown from '~/create_merge_request_dropdown'; +import { TEST_HOST } from 'spec/test_constants'; + +describe('CreateMergeRequestDropdown', () => { + let axiosMock; + let dropdown; + + beforeEach(() => { + axiosMock = new MockAdapter(axios); + + setFixtures(` +
+
+
+
+
+
+
+
+
+
+
+ `); + + const dummyElement = document.getElementById('dummy-wrapper-element'); + dropdown = new CreateMergeRequestDropdown(dummyElement); + dropdown.refsPath = `${TEST_HOST}/dummy/refs?search=`; + }); + + afterEach(() => { + axiosMock.restore(); + }); + + describe('getRef', () => { + it('escapes branch names correctly', done => { + const endpoint = `${dropdown.refsPath}contains%23hash`; + spyOn(axios, 'get').and.callThrough(); + axiosMock.onGet(endpoint).replyOnce({}); + + dropdown + .getRef('contains#hash') + .then(() => { + expect(axios.get).toHaveBeenCalledWith(endpoint); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('updateCreatePaths', () => { + it('escapes branch names correctly', () => { + dropdown.createBranchPath = `${TEST_HOST}/branches?branch_name=some-branch&issue=42`; + dropdown.createMrPath = `${TEST_HOST}/create_merge_request?branch_name=some-branch&ref=master`; + + dropdown.updateCreatePaths('branch', 'contains#hash'); + + expect(dropdown.createBranchPath).toBe( + `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`, + ); + expect(dropdown.createMrPath).toBe( + `${TEST_HOST}/create_merge_request?branch_name=contains%23hash&ref=master`, + ); + }); + }); +}); -- cgit v1.2.1 From 202bd2da16942ca8c9b667b10c75025a0c95980d Mon Sep 17 00:00:00 2001 From: James Edwards-Jones Date: Thu, 14 Jun 2018 09:02:32 +1000 Subject: Backport InternalRedirect#sanitize_redirect --- .../controllers/concerns/internal_redirect_spec.rb | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'spec') diff --git a/spec/controllers/concerns/internal_redirect_spec.rb b/spec/controllers/concerns/internal_redirect_spec.rb index a0ee13b2352..7e23b56356e 100644 --- a/spec/controllers/concerns/internal_redirect_spec.rb +++ b/spec/controllers/concerns/internal_redirect_spec.rb @@ -54,6 +54,31 @@ describe InternalRedirect do end end + describe '#sanitize_redirect' do + let(:valid_path) { '/hello/world?hello=world' } + let(:valid_url) { "http://test.host#{valid_path}" } + + it 'returns `nil` for invalid paths' do + invalid_path = '//not/valid' + + expect(controller.sanitize_redirect(invalid_path)).to eq nil + end + + it 'returns `nil` for invalid urls' do + input = 'http://test.host:3000/invalid' + + expect(controller.sanitize_redirect(input)).to eq nil + end + + it 'returns input for valid paths' do + expect(controller.sanitize_redirect(valid_path)).to eq valid_path + end + + it 'returns path for valid urls' do + expect(controller.sanitize_redirect(valid_url)).to eq valid_path + end + end + describe '#host_allowed?' do it 'allows uris with the same host and port' do expect(controller.host_allowed?(URI('http://test.host/test'))).to be(true) -- cgit v1.2.1 From fa36101a7fc8679d98198942f15dd6285673594d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarka=20Kadlecov=C3=A1?= Date: Tue, 5 Jun 2018 11:29:33 +0200 Subject: Use data_source_exists? instead of table_exists? Use data_source_exists? where possible instead of table_exists? in order to be Rails5 compatible --- .../background_migration/populate_untracked_uploads_spec.rb | 2 +- spec/lib/gitlab/database_spec.rb | 9 +++++++-- spec/support/helpers/migrations_helpers.rb | 4 ---- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb index 0d2074eed22..0dee683350f 100644 --- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb @@ -114,7 +114,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra it 'does not drop the temporary tracking table after processing the batch, if there are still untracked rows' do subject.perform(1, untracked_files_for_uploads.last.id - 1) - expect(ActiveRecord::Base.connection.table_exists?(:untracked_files_for_uploads)).to be_truthy + expect(ActiveRecord::Base.connection.data_source_exists?(:untracked_files_for_uploads)).to be_truthy end it 'drops the temporary tracking table after processing the batch, if there are no untracked rows left' do diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index 8ac36ae8bab..8bb246aa4bd 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -314,8 +314,13 @@ describe Gitlab::Database do describe '.cached_table_exists?' do it 'only retrieves data once per table' do - expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original - expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original + if Gitlab.rails5? + expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:projects).once.and_call_original + expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:bogus_table_name).once.and_call_original + else + expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original + expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original + end 2.times do expect(described_class.cached_table_exists?(:projects)).to be_truthy diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb index 84abec75c26..0bc235701eb 100644 --- a/spec/support/helpers/migrations_helpers.rb +++ b/spec/support/helpers/migrations_helpers.rb @@ -10,10 +10,6 @@ module MigrationsHelpers ActiveRecord::Migrator.migrations_paths end - def table_exists?(name) - ActiveRecord::Base.connection.table_exists?(name) - end - def migrations ActiveRecord::Migrator.migrations(migrations_paths) end -- cgit v1.2.1 From 4fc46007479784997a90e3b648fd5d4b3ff897ec Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 14 Jun 2018 09:56:16 +0200 Subject: Default branch detection happens through Gitaly Migration: https://gitlab.com/gitlab-org/gitaly/issues/220 --- spec/lib/gitlab/git/repository_spec.rb | 51 ---------------------------------- 1 file changed, 51 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 1744db1b17e..9cc7a0d79dc 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -77,17 +77,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#root_ref' do - context 'with gitaly disabled' do - before do - allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) - end - - it 'calls #discover_default_branch' do - expect(repository).to receive(:discover_default_branch) - repository.root_ref - end - end - it 'returns UTF-8' do expect(repository.root_ref).to be_utf8 end @@ -153,46 +142,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe "#discover_default_branch" do - let(:master) { 'master' } - let(:feature) { 'feature' } - let(:feature2) { 'feature2' } - - around do |example| - # discover_default_branch will be moved to gitaly-ruby - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - example.run - end - end - - it "returns 'master' when master exists" do - expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master]) - expect(repository.discover_default_branch).to eq('master') - end - - it "returns non-master when master exists but default branch is set to something else" do - File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/feature') - expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master]) - expect(repository.discover_default_branch).to eq('feature') - File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/master') - end - - it "returns a non-master branch when only one exists" do - expect(repository).to receive(:branch_names).at_least(:once).and_return([feature]) - expect(repository.discover_default_branch).to eq('feature') - end - - it "returns a non-master branch when more than one exists and master does not" do - expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, feature2]) - expect(repository.discover_default_branch).to eq('feature') - end - - it "returns nil when no branch exists" do - expect(repository).to receive(:branch_names).at_least(:once).and_return([]) - expect(repository.discover_default_branch).to be_nil - end - end - describe '#branch_names' do subject { repository.branch_names } -- cgit v1.2.1 From 89ab32c6b20b9a0e8cde9d7f28759db8f53c7ffb Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 14 Jun 2018 10:25:28 +0200 Subject: Branches are fully migrated to Gitaly Closes: https://gitlab.com/gitlab-org/gitaly/issues/389 --- spec/lib/gitlab/git/repository_spec.rb | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 9cc7a0d79dc..9de7724e34e 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1344,24 +1344,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - # With Gitaly enabled, Gitaly just doesn't return deleted branches. - context 'with deleted branch with Gitaly disabled' do - before do - allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) - end - - it 'returns no results' do - ref = double() - allow(ref).to receive(:name) { 'bad-branch' } - allow(ref).to receive(:target) { raise Rugged::ReferenceError } - branches = double() - allow(branches).to receive(:each) { [ref].each } - allow(repository_rugged).to receive(:branches) { branches } - - expect(subject).to be_empty - end - end - it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branches end -- cgit v1.2.1 From f79410fe1772798d7fa1b288880e14e806483c3c Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Thu, 14 Jun 2018 08:30:16 +0000 Subject: enable CommonMark as the default --- spec/features/markdown/copy_as_gfm_spec.rb | 9 +++- spec/features/markdown/markdown_spec.rb | 55 +++++++++++++++++------ spec/features/task_lists_spec.rb | 45 ++++++++++++++++--- spec/fixtures/markdown.md.erb | 10 ++++- spec/helpers/markup_helper_spec.rb | 2 +- spec/javascripts/shortcuts_issuable_spec.js | 2 +- spec/lib/banzai/filter/markdown_filter_spec.rb | 4 +- spec/models/concerns/cache_markdown_field_spec.rb | 10 ++--- 8 files changed, 106 insertions(+), 31 deletions(-) (limited to 'spec') diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index 4d897f09b57..05228e27963 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -502,6 +502,13 @@ describe 'Copy as GFM', :js do 1. Numbered lists GFM + # list item followed by an HR + <<-GFM.strip_heredoc, + - list item + + ----- + GFM + '# Heading', '## Heading', '### Heading', @@ -515,8 +522,6 @@ describe 'Copy as GFM', :js do '~~Strikethrough~~', - '2^2', - '-----', # table diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index c86ba8c50a5..cac8a5068ec 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -44,7 +44,7 @@ describe 'GitLab Markdown', :aggregate_failures do # Shared behavior that all pipelines should exhibit shared_examples 'all pipelines' do - it 'includes Redcarpet extensions' do + it 'includes extensions' do aggregate_failures 'does not parse emphasis inside of words' do expect(doc.to_html).not_to match('foobarbaz') end @@ -72,10 +72,6 @@ describe 'GitLab Markdown', :aggregate_failures do aggregate_failures 'parses strikethroughs' do expect(doc).to have_selector(%{del:contains("and this text doesn't")}) end - - aggregate_failures 'parses superscript' do - expect(doc).to have_selector('sup', count: 2) - end end it 'includes SanitizationFilter' do @@ -123,16 +119,24 @@ describe 'GitLab Markdown', :aggregate_failures do expect(doc).to have_selector('details summary:contains("collapsible")') end - aggregate_failures 'permits style attribute in th elements' do - expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' - expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' - expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' + aggregate_failures 'permits align attribute in th elements' do + expect(doc.at_css('th:contains("Header")')['align']).to eq 'center' + expect(doc.at_css('th:contains("Row")')['align']).to eq 'right' + expect(doc.at_css('th:contains("Example")')['align']).to eq 'left' end - aggregate_failures 'permits style attribute in td elements' do - expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' - expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' - expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' + aggregate_failures 'permits align attribute in td elements' do + expect(doc.at_css('td:contains("Foo")')['align']).to eq 'center' + expect(doc.at_css('td:contains("Bar")')['align']).to eq 'right' + expect(doc.at_css('td:contains("Baz")')['align']).to eq 'left' + end + + aggregate_failures 'permits superscript elements' do + expect(doc).to have_selector('sup', count: 2) + end + + aggregate_failures 'permits subscript elements' do + expect(doc).to have_selector('sub', count: 3) end aggregate_failures 'removes `rel` attribute from links' do @@ -320,6 +324,31 @@ describe 'GitLab Markdown', :aggregate_failures do end end + context 'Redcarpet documents' do + before do + allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet') + @html = markdown(@feat.raw_markdown) + end + + it 'processes certain elements differently' do + aggregate_failures 'parses superscript' do + expect(doc).to have_selector('sup', count: 3) + end + + aggregate_failures 'permits style attribute in th elements' do + expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' + expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' + expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' + end + + aggregate_failures 'permits style attribute in td elements' do + expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' + expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' + expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' + end + end + end + # Fake a `current_user` helper def current_user @feat.user diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index 2dc3c5e3927..f37d8998045 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -36,7 +36,7 @@ feature 'Task Lists' do MARKDOWN end - let(:nested_tasks_markdown) do + let(:nested_tasks_markdown_redcarpet) do <<-EOT.strip_heredoc - [ ] Task a - [x] Task a.1 @@ -49,6 +49,19 @@ feature 'Task Lists' do EOT end + let(:nested_tasks_markdown) do + <<-EOT.strip_heredoc + - [ ] Task a + - [x] Task a.1 + - [ ] Task a.2 + - [ ] Task b + + 1. [ ] Task 1 + 1. [ ] Task 1.1 + 1. [x] Task 1.2 + EOT + end + before do Warden.test_mode! @@ -141,13 +154,11 @@ feature 'Task Lists' do end end - describe 'nested tasks', :js do - let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) } - + shared_examples 'shared nested tasks' do before do + allow(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet') visit_issue(project, issue) end - it 'renders' do expect(page).to have_selector('ul.task-list', count: 2) expect(page).to have_selector('li.task-list-item', count: 7) @@ -171,6 +182,30 @@ feature 'Task Lists' do expect(page).to have_content('marked the task Task 1.1 as complete') end end + + describe 'nested tasks', :js do + context 'with Redcarpet' do + let(:issue) { create(:issue, description: nested_tasks_markdown_redcarpet, author: user, project: project) } + + before do + allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet') + visit_issue(project, issue) + end + + it_behaves_like 'shared nested tasks' + end + + context 'with CommonMark' do + let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) } + + before do + allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('CommonMark') + visit_issue(project, issue) + end + + it_behaves_like 'shared nested tasks' + end + end end describe 'for Notes' do diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index da32a46675f..e5d01c3bd03 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -43,8 +43,14 @@ This text says this, ~~and this text doesn't~~. ### Superscript -This is my 1^(st) time using superscript in Markdown. Now this is my -2^(nd). +This is my 1(st) time using superscript in Markdown. Now this is my +2(nd). + +Redcarpet supports this superscript syntax ( x^2 ). + +### Subscript + +This (C6H12O6) is an example of subscripts in Markdown. ### Next step diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index c0dc9293397..1a720aae55c 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -298,7 +298,7 @@ describe MarkupHelper do it 'preserves code color scheme' do object = create_object("```ruby\ndef test\n 'hello world'\nend\n```") - expected = "\n
" \
+        expected = "
" \
           "def test...\n" \
           "
" diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js index d73608ed0ed..b10d8be6781 100644 --- a/spec/javascripts/shortcuts_issuable_spec.js +++ b/spec/javascripts/shortcuts_issuable_spec.js @@ -66,7 +66,7 @@ describe('ShortcutsIssuable', function () { }); describe('with a multi-line selection', () => { it('quotes the selected lines as a group', () => { - stubSelection('

Selected line one.

\n\n

Selected line two.

\n\n

Selected line three.

'); + stubSelection('

Selected line one.

\n

Selected line two.

\n

Selected line three.

'); this.shortcut.replyWithSelectedText(true); expect($(this.selector).val()).toBe('> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n'); }); diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb index 00c407d1b69..ab14d77d552 100644 --- a/spec/lib/banzai/filter/markdown_filter_spec.rb +++ b/spec/lib/banzai/filter/markdown_filter_spec.rb @@ -7,13 +7,13 @@ describe Banzai::Filter::MarkdownFilter do it 'adds language to lang attribute when specified' do result = filter("```html\nsome code\n```") - expect(result).to start_with("\n
")
+      expect(result).to start_with("
")
     end
 
     it 'does not add language to lang attribute when not specified' do
       result = filter("```\nsome code\n```")
 
-      expect(result).to start_with("\n
")
+      expect(result).to start_with("
")
     end
   end
 end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index b3797c1fb46..2d75422ee68 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -156,7 +156,7 @@ describe CacheMarkdownField do
     end
 
     it { expect(thing.foo_html).to eq(updated_html) }
-    it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION) }
+    it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
   end
 
   describe '#cached_html_up_to_date?' do
@@ -234,7 +234,7 @@ describe CacheMarkdownField do
 
     it 'returns default version when version is nil' do
       thing.cached_markdown_version = nil
-      is_expected.to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+      is_expected.to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
     end
   end
 
@@ -261,7 +261,7 @@ describe CacheMarkdownField do
       thing.cached_markdown_version = nil
       thing.refresh_markdown_cache
 
-      expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+      expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
     end
   end
 
@@ -346,7 +346,7 @@ describe CacheMarkdownField do
 
         expect(thing.foo_html).to eq(updated_html)
         expect(thing.baz_html).to eq(updated_html)
-        expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+        expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
       end
     end
 
@@ -366,7 +366,7 @@ describe CacheMarkdownField do
 
         expect(thing.foo_html).to eq(updated_html)
         expect(thing.baz_html).to eq(updated_html)
-        expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+        expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
       end
     end
   end
-- 
cgit v1.2.1


From 3ed4a1b3aadd50b47181312b49911d2a770c4c82 Mon Sep 17 00:00:00 2001
From: Zeger-Jan van de Weg 
Date: Thu, 14 Jun 2018 10:31:24 +0200
Subject: HasLocalBranches check is done by Gitaly only

Closes https://gitlab.com/gitlab-org/gitaly/issues/217
---
 spec/lib/gitlab/git/repository_spec.rb | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

(limited to 'spec')

diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 9de7724e34e..5bae99101e6 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -425,7 +425,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
   end
 
   describe '#has_local_branches?' do
-    shared_examples 'check for local branches' do
+    context 'check for local branches' do
       it { expect(repository.has_local_branches?).to eq(true) }
 
       context 'mutable' do
@@ -459,14 +459,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
         end
       end
     end
-
-    context 'with gitaly' do
-      it_behaves_like 'check for local branches'
-    end
-
-    context 'without gitaly', :skip_gitaly_mock do
-      it_behaves_like 'check for local branches'
-    end
   end
 
   describe "#delete_branch" do
-- 
cgit v1.2.1


From 946a982748f93bc09f2f3c8092f9e8c757e8115f Mon Sep 17 00:00:00 2001
From: Eric Eastwood 
Date: Wed, 13 Jun 2018 14:18:08 -0500
Subject: Fix flakey user comment on issue test race condition

---
 spec/features/projects/issues/user_comments_on_issue_spec.rb | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'spec')

diff --git a/spec/features/projects/issues/user_comments_on_issue_spec.rb b/spec/features/projects/issues/user_comments_on_issue_spec.rb
index c45fdc7642f..353f487485d 100644
--- a/spec/features/projects/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/projects/issues/user_comments_on_issue_spec.rb
@@ -31,11 +31,14 @@ describe "User comments on issue", :js do
     end
 
     it "adds comment with code block" do
-      comment = "```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```"
+      code_block_content = "Command [1]: /usr/local/bin/git , see [text](doc/text)"
+      comment = "```\n#{code_block_content}\n```"
 
       add_note(comment)
 
-      expect(page).to have_content(comment)
+      wait_for_requests
+
+      expect(page.find('pre code').text).to eq code_block_content
     end
   end
 
-- 
cgit v1.2.1


From aef29d00fea11938587dceeb7a9abb2cb6afa74f Mon Sep 17 00:00:00 2001
From: Eric Eastwood 
Date: Wed, 13 Jun 2018 17:09:50 -0500
Subject: Fix flakey move slash command with unauthorized project

---
 spec/features/issues/user_uses_slash_commands_spec.rb | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index dacca494755..17818beb947 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -226,7 +226,9 @@ feature 'Issues > User uses quick actions', :js do
         it 'does not move the issue' do
           add_note("/move #{project_unauthorized.full_path}")
 
-          expect(page).not_to have_content 'Commands applied'
+          wait_for_requests
+
+          expect(page).to have_content 'Commands applied'
           expect(issue.reload).to be_open
         end
       end
-- 
cgit v1.2.1


From 9c3e9fe2c099a0fc251a8c8e15e4846cd1d946e5 Mon Sep 17 00:00:00 2001
From: Eric Eastwood 
Date: Thu, 14 Jun 2018 00:05:05 -0500
Subject: Fix flakey time-sensitive remove deploy key spec

---
 spec/features/projects/deploy_keys_spec.rb | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 43a23c42f83..1552a3512dd 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -22,7 +22,8 @@ describe 'Project deploy keys', :js do
 
         accept_confirm { find('.ic-remove').click() }
 
-        expect(page).not_to have_selector('.fa-spinner', count: 0)
+        wait_for_requests
+
         expect(page).to have_selector('.deploy-key', count: 0)
       end
     end
-- 
cgit v1.2.1


From 56b64e8b2fa5ca7024fade404f0d73cdd5a9aec2 Mon Sep 17 00:00:00 2001
From: Lin Jen-Shin 
Date: Thu, 14 Jun 2018 19:03:24 +0800
Subject: We don't need to pass lib: true as in EE

---
 spec/lib/banzai/filter/image_lazy_load_filter_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index c19de7b784a..41f957c4e00 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Banzai::Filter::ImageLazyLoadFilter, lib: true do
+describe Banzai::Filter::ImageLazyLoadFilter do
   include FilterSpecHelper
 
   def image(path)
-- 
cgit v1.2.1


From 5cf5680f9c8ce251570efce7dd4c348fb68efccf Mon Sep 17 00:00:00 2001
From: "Jacob Vosmaer (GitLab)" 
Date: Thu, 14 Jun 2018 11:18:25 +0000
Subject: Deny repository disk access in development and test

---
 spec/controllers/projects_controller_spec.rb               | 12 +++++++++---
 spec/helpers/projects_helper_spec.rb                       |  6 +++++-
 spec/lib/gitlab/git_access_wiki_spec.rb                    |  4 +++-
 spec/migrations/migrate_process_commit_worker_jobs_spec.rb |  6 +++++-
 ...urn_nested_groups_into_regular_groups_for_mysql_spec.rb |  8 ++++++--
 spec/models/project_spec.rb                                | 14 ++++++++++----
 spec/models/project_wiki_spec.rb                           |  6 +++++-
 spec/models/remote_mirror_spec.rb                          |  4 +++-
 spec/requests/api/internal_spec.rb                         |  1 -
 spec/services/projects/create_service_spec.rb              |  5 ++++-
 spec/support/gitaly.rb                                     |  2 +-
 spec/workers/repository_remove_remote_worker_spec.rb       |  4 +++-
 12 files changed, 54 insertions(+), 18 deletions(-)

(limited to 'spec')

diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 5bd22ea803c..705b30f0130 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -296,16 +296,22 @@ describe ProjectsController do
     shared_examples_for 'updating a project' do
       context 'when only renaming a project path' do
         it "sets the repository to the right path after a rename" do
-          original_repository_path = project.repository.path
+          original_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+            project.repository.path
+          end
 
           expect { update_project path: 'renamed_path' }
             .to change { project.reload.path }
           expect(project.path).to include 'renamed_path'
 
+          assign_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+            assigns(:repository).path
+          end
+
           if project.hashed_storage?(:repository)
-            expect(assigns(:repository).path).to eq(original_repository_path)
+            expect(assign_repository_path).to eq(original_repository_path)
           else
-            expect(assigns(:repository).path).to include(project.path)
+            expect(assign_repository_path).to include(project.path)
           end
 
           expect(response).to have_gitlab_http_status(302)
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 815b4035114..5cf9e9e8f12 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -280,7 +280,11 @@ describe ProjectsHelper do
 
   describe '#sanitizerepo_repo_path' do
     let(:project) { create(:project, :repository) }
-    let(:storage_path) { Gitlab.config.repositories.storages.default.legacy_disk_path }
+    let(:storage_path) do
+      Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+        Gitlab.config.repositories.storages.default.legacy_disk_path
+      end
+    end
 
     before do
       allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 730ede99fc9..9c6c9fe13bf 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -52,7 +52,9 @@ describe Gitlab::GitAccessWiki do
       context 'when the wiki repository does not exist' do
         it 'returns not found' do
           wiki_repo = project.wiki.repository
-          FileUtils.rm_rf(wiki_repo.path)
+          Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+            FileUtils.rm_rf(wiki_repo.path)
+          end
 
           # Sanity check for rm_rf
           expect(wiki_repo.exists?).to eq(false)
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index 4ee1d255fbd..ac34efa4f9d 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -6,7 +6,11 @@ require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_
 describe MigrateProcessCommitWorkerJobs do
   let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
   let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
-  let(:commit) { project.commit.raw.rugged_commit }
+  let(:commit) do
+    Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+      project.commit.raw.rugged_commit
+    end
+  end
 
   describe 'Project' do
     describe 'find_including_path' do
diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
index 560409f08de..5f5ba426d69 100644
--- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
+++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
@@ -49,10 +49,14 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
     end
 
     it 'renames the repository of any projects' do
-      expect(updated_project.repository.path)
+      repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+        updated_project.repository.path
+      end
+
+      expect(repo_path)
         .to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
 
-      expect(File.directory?(updated_project.repository.path)).to eq(true)
+      expect(File.directory?(repo_path)).to eq(true)
     end
 
     it 'creates a redirect route for renamed projects' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 585cf7aab44..bc9cce6b0c3 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2943,7 +2943,7 @@ describe Project do
 
         project.rename_repo
 
-        expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+        expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
       end
     end
 
@@ -3104,7 +3104,7 @@ describe Project do
       it 'updates project full path in .git/config' do
         project.rename_repo
 
-        expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+        expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
       end
     end
 
@@ -3525,13 +3525,13 @@ describe Project do
     it 'writes full path in .git/config when key is missing' do
       project.write_repository_config
 
-      expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+      expect(rugged_config['gitlab.fullpath']).to eq project.full_path
     end
 
     it 'updates full path in .git/config when key is present' do
       project.write_repository_config(gl_full_path: 'old/path')
 
-      expect { project.write_repository_config }.to change { project.repository.rugged.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
+      expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
     end
 
     it 'does not raise an error with an empty repository' do
@@ -3817,4 +3817,10 @@ describe Project do
       let(:uploader_class) { AttachmentUploader }
     end
   end
+
+  def rugged_config
+    Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+      project.repository.rugged.config
+    end
+  end
 end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index f1142832f1a..a3c20b3b3c1 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -188,7 +188,11 @@ describe ProjectWiki do
       before do
         subject.wiki # Make sure the wiki repo exists
 
-        BareRepoOperations.new(subject.repository.path_to_repo).commit_file(image, 'image.png')
+        repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+          subject.repository.path_to_repo
+        end
+
+        BareRepoOperations.new(repo_path).commit_file(image, 'image.png')
       end
 
       it 'returns the latest version of the file if it exists' do
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 4c086eeadfc..3597b080021 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -74,7 +74,9 @@ describe RemoteMirror do
 
         mirror.update_attribute(:url, 'http://foo:baz@test.com')
 
-        config = repo.raw_repository.rugged.config
+        config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+          repo.raw_repository.rugged.config
+        end
         expect(config["remote.#{mirror.remote_name}.url"]).to eq('http://foo:baz@test.com')
       end
 
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index bc32372d3a9..a56b913198c 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -522,7 +522,6 @@ describe API::Internal do
 
     context 'the project path was changed' do
       let(:project) { create(:project, :repository, :legacy_storage) }
-      let!(:old_path_to_repo) { project.repository.path_to_repo }
       let!(:repository) { project.repository }
 
       before do
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index a8f003b1073..e8cbf84e3be 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -272,8 +272,11 @@ describe Projects::CreateService, '#execute' do
 
   it 'writes project full path to .git/config' do
     project = create_project(user, opts)
+    rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+      project.repository.rugged
+    end
 
-    expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+    expect(rugged.config['gitlab.fullpath']).to eq project.full_path
   end
 
   def create_project(user, opts)
diff --git a/spec/support/gitaly.rb b/spec/support/gitaly.rb
index 5a1dd44bc9d..614aaa73693 100644
--- a/spec/support/gitaly.rb
+++ b/spec/support/gitaly.rb
@@ -9,7 +9,7 @@ RSpec.configure do |config|
       # Use 'and_wrap_original' to make sure the arguments are valid
       allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_wrap_original do |m, *args|
         m.call(*args)
-        !Gitlab::GitalyClient::EXPLICIT_OPT_IN_REQUIRED.include?(args.first)
+        !Gitlab::GitalyClient.explicit_opt_in_required.include?(args.first)
       end
     end
   end
diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb
index f22d7c1d073..5968c5da3c9 100644
--- a/spec/workers/repository_remove_remote_worker_spec.rb
+++ b/spec/workers/repository_remove_remote_worker_spec.rb
@@ -44,7 +44,9 @@ describe RepositoryRemoveRemoteWorker do
   end
 
   def create_remote_branch(remote_name, branch_name, target)
-    rugged = project.repository.rugged
+    rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+      project.repository.rugged
+    end
     rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
   end
 end
-- 
cgit v1.2.1


From cc4541e1b2bc4a946d34c689464ec88ac7320963 Mon Sep 17 00:00:00 2001
From: Lin Jen-Shin 
Date: Thu, 14 Jun 2018 21:33:35 +0800
Subject: Port route helper from EE to CE

---
 spec/spec_helper.rb | 1 +
 1 file changed, 1 insertion(+)

(limited to 'spec')

diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8417b340de5..dac609e2545 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -87,6 +87,7 @@ RSpec.configure do |config|
   config.include LiveDebugger, :js
   config.include MigrationsHelpers, :migration
   config.include RedisHelpers
+  config.include Rails.application.routes.url_helpers, type: :routing
 
   if ENV['CI']
     # This includes the first try, i.e. tests will be run 4 times before failing.
-- 
cgit v1.2.1


From 30ef25452182cb9a956f895595cf0cd4afc51c57 Mon Sep 17 00:00:00 2001
From: Phil Hughes 
Date: Wed, 13 Jun 2018 09:44:14 +0100
Subject: fixed hover styling caused by dropdown fixed karma specs added
 CHANGELOG item

---
 .../ide/components/commit_sidebar/list_spec.js       |  1 +
 .../components/commit_sidebar/stage_button_spec.js   |  2 +-
 .../ide/components/repo_commit_section_spec.js       | 20 ++++++++++++--------
 3 files changed, 14 insertions(+), 9 deletions(-)

(limited to 'spec')

diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
index 6fb52378386..b994488af87 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
@@ -16,6 +16,7 @@ describe('Multi-file editor commit sidebar list', () => {
       iconName: 'staged',
       action: 'stageAllChanges',
       actionBtnText: 'stage all',
+      actionBtnIcon: 'history',
       itemActionComponent: 'stage-button',
       activeFileKey: 'staged-testing',
       keyPrefix: 'staged',
diff --git a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
index 6bf8710bda7..a5b906da8a1 100644
--- a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
@@ -39,7 +39,7 @@ describe('IDE stage file button', () => {
   });
 
   it('calls store with discard button', () => {
-    vm.$el.querySelectorAll('.btn')[1].click();
+    vm.$el.querySelector('.dropdown-menu button').click();
 
     expect(vm.discardFileChanges).toHaveBeenCalledWith(f.path);
   });
diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js
index 531bcd6e540..6bf309fb4bf 100644
--- a/spec/javascripts/ide/components/repo_commit_section_spec.js
+++ b/spec/javascripts/ide/components/repo_commit_section_spec.js
@@ -111,7 +111,7 @@ describe('RepoCommitSection', () => {
   });
 
   it('renders a commit section', () => {
-    const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
+    const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list > li')];
     const allFiles = vm.$store.state.changedFiles.concat(vm.$store.state.stagedFiles);
 
     expect(changedFileElements.length).toEqual(4);
@@ -140,22 +140,26 @@ describe('RepoCommitSection', () => {
     vm.$el.querySelector('.multi-file-discard-btn .btn').click();
 
     Vue.nextTick(() => {
-      expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
-        1,
-      );
+      expect(
+        vm.$el
+          .querySelector('.ide-commit-list-container')
+          .querySelectorAll('.multi-file-commit-list > li').length,
+      ).toBe(1);
 
       done();
     });
   });
 
   it('discards a single file', done => {
-    vm.$el.querySelectorAll('.multi-file-discard-btn .btn')[1].click();
+    vm.$el.querySelector('.multi-file-discard-btn .dropdown-menu button').click();
 
     Vue.nextTick(() => {
       expect(vm.$el.querySelector('.ide-commit-list-container').textContent).not.toContain('file1');
-      expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
-        1,
-      );
+      expect(
+        vm.$el
+          .querySelector('.ide-commit-list-container')
+          .querySelectorAll('.multi-file-commit-list > li').length,
+      ).toBe(1);
 
       done();
     });
-- 
cgit v1.2.1


From a2bfb820021f05a51ffaa96a3ef7cc87dc1cf6b2 Mon Sep 17 00:00:00 2001
From: Phil Hughes 
Date: Wed, 13 Jun 2018 10:59:51 +0100
Subject: fixed sidebar list spec

---
 spec/javascripts/ide/components/commit_sidebar/list_spec.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
index b994488af87..b786be55019 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
@@ -43,7 +43,7 @@ describe('Multi-file editor commit sidebar list', () => {
     });
 
     it('renders list', () => {
-      expect(vm.$el.querySelectorAll('li').length).toBe(1);
+      expect(vm.$el.querySelectorAll('.multi-file-commit-list > li').length).toBe(1);
     });
   });
 
-- 
cgit v1.2.1


From f454f1ab5ab69de51ad4ae8cb620782e48fc0541 Mon Sep 17 00:00:00 2001
From: Phil Hughes 
Date: Wed, 13 Jun 2018 12:24:48 +0100
Subject: fixed karma spec

---
 spec/javascripts/ide/components/commit_sidebar/list_item_spec.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'spec')

diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
index 8f7cf24c22f..bf96170f703 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
@@ -93,14 +93,14 @@ describe('Multi-file editor commit sidebar list item', () => {
 
   describe('is active', () => {
     it('does not add active class when dont keys match', () => {
-      expect(vm.$el.classList).not.toContain('is-active');
+      expect(vm.$el.querySelector('.is-active')).toBe(null);
     });
 
     it('adds active class when keys match', done => {
       vm.keyPrefix = 'staged';
 
       vm.$nextTick(() => {
-        expect(vm.$el.classList).toContain('is-active');
+        expect(vm.$el.querySelector('.is-active')).not.toBe(null);
 
         done();
       });
-- 
cgit v1.2.1


From 4d098451c3830a13e7ae3a9615d87dbc1f73ceec Mon Sep 17 00:00:00 2001
From: Annabel Gray 
Date: Thu, 14 Jun 2018 16:34:58 +0000
Subject: Resolve "Performance bar Gitaly modal is hard to read"

---
 spec/javascripts/vue_shared/components/gl_modal_spec.js | 8 ++++++++
 1 file changed, 8 insertions(+)

(limited to 'spec')

diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js
index 23be8d93b81..e4737714312 100644
--- a/spec/javascripts/vue_shared/components/gl_modal_spec.js
+++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js
@@ -208,6 +208,14 @@ describe('GlModal', () => {
       expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(true);
     });
 
+    it('should render modal-xl', () => {
+      vm = mountComponent(modalComponent, {
+        modalSize: 'xl',
+      });
+
+      expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-xl')).toEqual(true);
+    });
+
     it('should not add modal size classes when md size is passed', () => {
       vm = mountComponent(modalComponent, {
         modalSize: 'md',
-- 
cgit v1.2.1


From 2126f8f38e6df1da4afec169343303facdde339a Mon Sep 17 00:00:00 2001
From: Eric Eastwood 
Date: Wed, 13 Jun 2018 23:43:21 -0500
Subject: Fix flakey user/assignee autocomplete dropdown

Related to https://gitlab.com/gitlab-org/gitlab-ce/issues/38664

This previously worked because, it would assert the correct names
in the full list and then wait for the list to go blank for the NOT assertion

Example failure, https://gitlab.com/gitlab-org/gitlab-ee/-/jobs/74649299
---
 .../issues/filtered_search/dropdown_assignee_spec.rb   | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

(limited to 'spec')

diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index cbd0949c192..4fc55acc6bf 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -79,23 +79,21 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'filters by name' do
-      filtered_search.send_keys('j')
+      input_filtered_search('jac', submit: false, extra_space: false)
 
-      expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name)
     end
 
     it 'filters by case insensitive name' do
-      filtered_search.send_keys('J')
+      input_filtered_search('JAC', submit: false, extra_space: false)
 
-      expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name)
     end
 
     it 'filters by username with symbol' do
-      filtered_search.send_keys('@ot')
+      input_filtered_search('@ott', submit: false, extra_space: false)
 
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -103,7 +101,7 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'filters by case insensitive username with symbol' do
-      filtered_search.send_keys('@OT')
+      input_filtered_search('@OTT', submit: false, extra_space: false)
 
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -111,7 +109,9 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'filters by username without symbol' do
-      filtered_search.send_keys('ot')
+      filtered_search.send_keys('ott')
+
+      wait_for_requests
 
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -119,7 +119,9 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'filters by case insensitive username without symbol' do
-      filtered_search.send_keys('OT')
+      filtered_search.send_keys('OTT')
+
+      wait_for_requests
 
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
-- 
cgit v1.2.1


From 9201e42a224dcb97dcca18ca7d8ea79c746220c5 Mon Sep 17 00:00:00 2001
From: Eric Eastwood 
Date: Thu, 14 Jun 2018 12:15:19 -0500
Subject: Update dropdown_assignee_spec with filtered_search_helpers

---
 .../filtered_search/dropdown_assignee_spec.rb      | 37 +++++++++++-----------
 1 file changed, 18 insertions(+), 19 deletions(-)

(limited to 'spec')

diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index 4fc55acc6bf..c8115db9212 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -31,7 +31,7 @@ describe 'Dropdown assignee', :js do
 
   describe 'behavior' do
     it 'opens when the search bar has assignee:' do
-      filtered_search.set('assignee:')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
 
       expect(page).to have_css(js_dropdown_assignee, visible: true)
     end
@@ -44,6 +44,7 @@ describe 'Dropdown assignee', :js do
 
     it 'should show loading indicator when opened' do
       slow_requests do
+        # We aren't using `input_filtered_search` because we want to see the loading indicator
         filtered_search.set('assignee:')
 
         expect(page).to have_css('#js-dropdown-assignee .filter-dropdown-loading', visible: true)
@@ -51,19 +52,19 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'should hide loading indicator when loaded' do
-      filtered_search.set('assignee:')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
 
       expect(find(js_dropdown_assignee)).not_to have_css('.filter-dropdown-loading')
     end
 
     it 'should load all the assignees when opened' do
-      filtered_search.set('assignee:')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
 
       expect(dropdown_assignee_size).to eq(4)
     end
 
     it 'shows current user at top of dropdown' do
-      filtered_search.set('assignee:')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
 
       expect(filter_dropdown.first('.filter-dropdown-item')).to have_content(user.name)
     end
@@ -71,7 +72,7 @@ describe 'Dropdown assignee', :js do
 
   describe 'filtering' do
     before do
-      filtered_search.set('assignee:')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
 
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
       expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
@@ -109,7 +110,7 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'filters by username without symbol' do
-      filtered_search.send_keys('ott')
+      input_filtered_search('ott', submit: false, extra_space: false)
 
       wait_for_requests
 
@@ -119,7 +120,7 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'filters by case insensitive username without symbol' do
-      filtered_search.send_keys('OTT')
+      input_filtered_search('OTT', submit: false, extra_space: false)
 
       wait_for_requests
 
@@ -131,7 +132,7 @@ describe 'Dropdown assignee', :js do
 
   describe 'selecting from dropdown' do
     before do
-      filtered_search.set('assignee:')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
     end
 
     it 'fills in the assignee username when the assignee has not been filtered' do
@@ -145,7 +146,7 @@ describe 'Dropdown assignee', :js do
     end
 
     it 'fills in the assignee username when the assignee has been filtered' do
-      filtered_search.send_keys('roo')
+      input_filtered_search('roo', submit: false, extra_space: false)
       click_assignee(user.name)
 
       wait_for_requests
@@ -167,7 +168,7 @@ describe 'Dropdown assignee', :js do
   describe 'selecting from dropdown without Ajax call' do
     before do
       Gitlab::Testing::RequestBlockerMiddleware.block_requests!
-      filtered_search.set('assignee:')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
     end
 
     after do
@@ -185,31 +186,31 @@ describe 'Dropdown assignee', :js do
 
   describe 'input has existing content' do
     it 'opens assignee dropdown with existing search term' do
-      filtered_search.set('searchTerm assignee:')
+      input_filtered_search('searchTerm assignee:', submit: false, extra_space: false)
 
       expect(page).to have_css(js_dropdown_assignee, visible: true)
     end
 
     it 'opens assignee dropdown with existing author' do
-      filtered_search.set('author:@user assignee:')
+      input_filtered_search('author:@user assignee:', submit: false, extra_space: false)
 
       expect(page).to have_css(js_dropdown_assignee, visible: true)
     end
 
     it 'opens assignee dropdown with existing label' do
-      filtered_search.set('label:~bug assignee:')
+      input_filtered_search('label:~bug assignee:', submit: false, extra_space: false)
 
       expect(page).to have_css(js_dropdown_assignee, visible: true)
     end
 
     it 'opens assignee dropdown with existing milestone' do
-      filtered_search.set('milestone:%v1.0 assignee:')
+      input_filtered_search('milestone:%v1.0 assignee:', submit: false, extra_space: false)
 
       expect(page).to have_css(js_dropdown_assignee, visible: true)
     end
 
     it 'opens assignee dropdown with existing my-reaction' do
-      filtered_search.set('my-reaction:star assignee:')
+      input_filtered_search('my-reaction:star assignee:', submit: false, extra_space: false)
 
       expect(page).to have_css(js_dropdown_assignee, visible: true)
     end
@@ -217,8 +218,7 @@ describe 'Dropdown assignee', :js do
 
   describe 'caching requests' do
     it 'caches requests after the first load' do
-      filtered_search.set('assignee')
-      filtered_search.send_keys(':')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
       initial_size = dropdown_assignee_size
 
       expect(initial_size).to be > 0
@@ -226,8 +226,7 @@ describe 'Dropdown assignee', :js do
       new_user = create(:user)
       project.add_master(new_user)
       find('.filtered-search-box .clear-search').click
-      filtered_search.set('assignee')
-      filtered_search.send_keys(':')
+      input_filtered_search('assignee:', submit: false, extra_space: false)
 
       expect(dropdown_assignee_size).to eq(initial_size)
     end
-- 
cgit v1.2.1


From 26fcf83308ba1388423fe6afd536bf433d327a76 Mon Sep 17 00:00:00 2001
From: Clement Ho 
Date: Thu, 14 Jun 2018 17:35:15 +0000
Subject: Resolve "Placeholder note renders the user name in a new line"

---
 spec/javascripts/notes_spec.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'spec')

diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 648fb3e9bd3..acbf23e2007 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -974,7 +974,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
         ).toBeFalsy();
         expect(
           $tempNoteHeader
-            .find('.d-none.d-sm-block')
+            .find('.d-none.d-sm-inline-block')
             .text()
             .trim(),
         ).toEqual(currentUserFullname);
@@ -1020,7 +1020,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
         const $tempNoteHeader = $tempNote.find('.note-header');
         expect(
           $tempNoteHeader
-            .find('.d-none.d-sm-block')
+            .find('.d-none.d-sm-inline-block')
             .text()
             .trim(),
         ).toEqual('Foo <script>alert("XSS")</script>');
-- 
cgit v1.2.1


From 3b2a909ac22b7ac559062973dbd1c951fae3ed43 Mon Sep 17 00:00:00 2001
From: Tim Zallmann 
Date: Thu, 14 Jun 2018 19:37:16 +0200
Subject: Makes the html attribute in the json output optional with the option
 html_render=false

---
 spec/controllers/projects/blob_controller_spec.rb | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

(limited to 'spec')

diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 00a7df6ccc8..fff363bca7a 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -55,6 +55,24 @@ describe Projects::BlobController do
           expect(json_response).to have_key 'raw_path'
         end
       end
+
+      context "html_render=false" do
+        let(:id) { 'master/README.md' }
+
+        before do
+          get(:show,
+              namespace_id: project.namespace,
+              project_id: project,
+              id: id,
+              format: :json,
+              html_render: false)
+        end
+
+        it do
+          expect(response).to be_ok
+          expect(json_response).note_to have_key 'html'
+        end
+      end
     end
 
     context 'with tree path' do
-- 
cgit v1.2.1


From 2fec4183368aebf848e3014598081948ddce49ca Mon Sep 17 00:00:00 2001
From: Mario de la Ossa 
Date: Thu, 14 Jun 2018 11:40:35 -0600
Subject: ChatNotificationService - fix sending tag notifications when "only
 default branch" enabled

---
 ...ack_mattermost_notifications_shared_examples.rb | 81 +++++++++++++++++-----
 1 file changed, 64 insertions(+), 17 deletions(-)

(limited to 'spec')

diff --git a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
index 2228e872926..7c34c7b4977 100644
--- a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
@@ -245,6 +245,70 @@ RSpec.shared_examples 'slack or mattermost notifications' do
     end
   end
 
+  describe 'Push events' do
+    let(:user) { create(:user) }
+    let(:project) { create(:project, :repository, creator: user) }
+
+    before do
+      allow(chat_service).to receive_messages(
+        project: project,
+        service_hook: true,
+        webhook: webhook_url
+      )
+
+      WebMock.stub_request(:post, webhook_url)
+    end
+
+    context 'only notify for the default branch' do
+      context 'when enabled' do
+        before do
+          chat_service.notify_only_default_branch = true
+        end
+
+        it 'does not notify push events if they are not for the default branch' do
+          ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}test"
+          push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
+
+          chat_service.execute(push_sample_data)
+
+          expect(WebMock).not_to have_requested(:post, webhook_url)
+        end
+
+        it 'notifies about push events for the default branch' do
+          push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
+
+          chat_service.execute(push_sample_data)
+
+          expect(WebMock).to have_requested(:post, webhook_url).once
+        end
+
+        it 'still notifies about pushed tags' do
+          ref = "#{Gitlab::Git::TAG_REF_PREFIX}test"
+          push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
+
+          chat_service.execute(push_sample_data)
+
+          expect(WebMock).to have_requested(:post, webhook_url).once
+        end
+      end
+
+      context 'when disabled' do
+        before do
+          chat_service.notify_only_default_branch = false
+        end
+
+        it 'notifies about all push events' do
+          ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}test"
+          push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
+
+          chat_service.execute(push_sample_data)
+
+          expect(WebMock).to have_requested(:post, webhook_url).once
+        end
+      end
+    end
+  end
+
   describe "Note events" do
     let(:user) { create(:user) }
     let(:project) { create(:project, :repository, creator: user) }
@@ -394,23 +458,6 @@ RSpec.shared_examples 'slack or mattermost notifications' do
 
           expect(result).to be_falsy
         end
-
-        it 'does not notify push events if they are not for the default branch' do
-          ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}test"
-          push_sample_data = Gitlab::DataBuilder::Push.build(project, user, nil, nil, ref, [])
-
-          chat_service.execute(push_sample_data)
-
-          expect(WebMock).not_to have_requested(:post, webhook_url)
-        end
-
-        it 'notifies about push events for the default branch' do
-          push_sample_data = Gitlab::DataBuilder::Push.build_sample(project, user)
-
-          chat_service.execute(push_sample_data)
-
-          expect(WebMock).to have_requested(:post, webhook_url).once
-        end
       end
 
       context 'when disabled' do
-- 
cgit v1.2.1


From 24068596ee0ab7d5a02e5aef49dc19c58ac84e40 Mon Sep 17 00:00:00 2001
From: Tim Zallmann 
Date: Thu, 14 Jun 2018 20:46:53 +0200
Subject: Fixed linting error with trailing space in rb

---
 spec/controllers/projects/blob_controller_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index fff363bca7a..a543f626bb0 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -70,7 +70,7 @@ describe Projects::BlobController do
 
         it do
           expect(response).to be_ok
-          expect(json_response).note_to have_key 'html'
+          expect(json_response).not_to have_key 'html'
         end
       end
     end
-- 
cgit v1.2.1


From cf01719d6e5a511adae61cd10ef0f82228747c24 Mon Sep 17 00:00:00 2001
From: Annabel Gray 
Date: Thu, 14 Jun 2018 20:22:56 +0000
Subject: Resolve ""Click to expand" link in collapsed diffs should be blue"

---
 spec/features/projects/diffs/diff_show_spec.rb | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'spec')

diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index c1307ab640f..9bfcb1e816a 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -166,8 +166,7 @@ feature 'Diff file viewer', :js do
 
     context 'expanding the diff' do
       before do
-        # We can't use `click_link` because the "link" doesn't have an `href`.
-        find('a.click-to-expand').click
+        click_button 'Click to expand it.'
 
         wait_for_requests
       end
-- 
cgit v1.2.1


From c84a470bab08abce2ac9ad268e57c1c90fb7755e Mon Sep 17 00:00:00 2001
From: Jan Provaznik 
Date: Thu, 14 Jun 2018 23:21:12 +0200
Subject: Fix queue_name in Rails 5

In Rails 5 DeliveryJob.queue_name may return Proc (probably
if block is used for queue name definition). Instance method
takes handles this already and returns string as expected.
---
 spec/workers/every_sidekiq_worker_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 9e3b99b3502..2106959e23c 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -13,7 +13,7 @@ describe 'Every Sidekiq worker' do
     file_worker_queues = Gitlab::SidekiqConfig.worker_queues.to_set
 
     worker_queues = Gitlab::SidekiqConfig.workers.map(&:queue).to_set
-    worker_queues << ActionMailer::DeliveryJob.queue_name
+    worker_queues << ActionMailer::DeliveryJob.new.queue_name
     worker_queues << 'default'
 
     missing_from_file = worker_queues - file_worker_queues
-- 
cgit v1.2.1


From a1d0a16bfbef8654684bca828505632bcfaaaeba Mon Sep 17 00:00:00 2001
From: Eric Eastwood 
Date: Thu, 14 Jun 2018 21:31:54 -0500
Subject: Fix flakey time-senstive group filter specs

This passed previously because the filtered group search is debounced by 0.5s. The test cleared the input, entered `group1`, cleared the input, entered nothing, and the all of groups are still listed because of the 0.5s debounce hasn't triggered and the test passes before anything is actually filtered.

Even if we assert that the list is filtered before clearing the input, the test still fails because the nature of the `fill_in 'filter', with: ""` method is that ["if you're setting the value to "", no keys are ever actually sent, hence no [`input`] event"](https://github.com/teamcapybara/capybara/issues/203#issuecomment-557281) and we never filter back to everything in the list.

So the solution is two-fold, add in the assertions that the list is actually filtered after each step. Then use a method that fills the input with proper events fired.
---
 spec/features/dashboard/groups_list_spec.rb | 4 ++++
 spec/features/explore/groups_list_spec.rb   | 4 ++++
 2 files changed, 8 insertions(+)

(limited to 'spec')

diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index ed47f7ed390..29280bd6e06 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -65,7 +65,11 @@ feature 'Dashboard Groups page', :js do
       fill_in 'filter', with: group.name
       wait_for_requests
 
+      expect(page).to have_content(group.name)
+      expect(page).not_to have_content(nested_group.parent.name)
+
       fill_in 'filter', with: ''
+      page.find('[name="filter"]').send_keys(:enter)
       wait_for_requests
 
       expect(page).to have_content(group.name)
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index 801a33979ff..ad02b454aee 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -35,7 +35,11 @@ describe 'Explore Groups page', :js do
     fill_in 'filter', with: group.name
     wait_for_requests
 
+    expect(page).to have_content(group.full_name)
+    expect(page).not_to have_content(public_group.full_name)
+
     fill_in 'filter', with: ""
+    page.find('[name="filter"]').send_keys(:enter)
     wait_for_requests
 
     expect(page).to have_content(group.full_name)
-- 
cgit v1.2.1


From f1955962d91eb25a847553b2900068cd9487d0be Mon Sep 17 00:00:00 2001
From: Jasper Maes 
Date: Fri, 15 Jun 2018 07:57:35 +0200
Subject: Rails5 fix update_attribute usage not causing a save

---
 spec/controllers/projects/imports_controller_spec.rb | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'spec')

diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 011843baffc..812833cc86b 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -29,7 +29,7 @@ describe Projects::ImportsController do
 
       context 'when import is in progress' do
         before do
-          project.update_attribute(:import_status, :started)
+          project.update_attributes(import_status: :started)
         end
 
         it 'renders template' do
@@ -47,7 +47,7 @@ describe Projects::ImportsController do
 
       context 'when import failed' do
         before do
-          project.update_attribute(:import_status, :failed)
+          project.update_attributes(import_status: :failed)
         end
 
         it 'redirects to new_namespace_project_import_path' do
@@ -59,7 +59,7 @@ describe Projects::ImportsController do
 
       context 'when import finished' do
         before do
-          project.update_attribute(:import_status, :finished)
+          project.update_attributes(import_status: :finished)
         end
 
         context 'when project is a fork' do
@@ -108,7 +108,7 @@ describe Projects::ImportsController do
 
       context 'when import never happened' do
         before do
-          project.update_attribute(:import_status, :none)
+          project.update_attributes(import_status: :none)
         end
 
         it 'redirects to namespace_project_path' do
-- 
cgit v1.2.1


From 4ac381a565eb1b3e7ebaec443889c6a7138fdfd0 Mon Sep 17 00:00:00 2001
From: Tim Zallmann 
Date: Fri, 15 Jun 2018 09:08:21 +0200
Subject: Changed the query string parameter to a string

---
 spec/controllers/projects/blob_controller_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index a543f626bb0..f47593aad72 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -65,7 +65,7 @@ describe Projects::BlobController do
               project_id: project,
               id: id,
               format: :json,
-              html_render: false)
+              html_render: 'false')
         end
 
         it do
-- 
cgit v1.2.1


From 242039e4e12b90495fcddfbb250d9540f3a6f95a Mon Sep 17 00:00:00 2001
From: Shinya Maeda 
Date: Fri, 15 Jun 2018 17:20:25 +0900
Subject: Fix "[Rails5] '-1' is not a valid data_store"

---
 spec/models/ci/build_trace_chunk_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index cbcf1e55979..6213a203d6a 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -57,7 +57,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
 
     context 'when data_store is others' do
       before do
-        build_trace_chunk.send(:write_attribute, :data_store, -1)
+        build_trace_chunk.send(:write_attribute, :data_store, 999)
       end
 
       it { expect { subject }.to raise_error('Unsupported data store') }
-- 
cgit v1.2.1


From d4540c6131ba036df45463604925217756dac28f Mon Sep 17 00:00:00 2001
From: Shinya Maeda 
Date: Fri, 15 Jun 2018 18:20:46 +0900
Subject: Remove unexpected data store test suite

---
 spec/models/ci/build_trace_chunk_spec.rb | 8 --------
 1 file changed, 8 deletions(-)

(limited to 'spec')

diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 6213a203d6a..b5a6d959ccb 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -54,14 +54,6 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
 
       it { is_expected.to eq('Sample data in db') }
     end
-
-    context 'when data_store is others' do
-      before do
-        build_trace_chunk.send(:write_attribute, :data_store, 999)
-      end
-
-      it { expect { subject }.to raise_error('Unsupported data store') }
-    end
   end
 
   describe '#set_data' do
-- 
cgit v1.2.1


From fbc749a96e07dd1b1b5760cfe376b5b7838181f0 Mon Sep 17 00:00:00 2001
From: Douwe Maan 
Date: Fri, 15 Jun 2018 09:42:56 +0000
Subject: Reuse viewer param and move logic to blob controller

---
 spec/controllers/projects/blob_controller_spec.rb | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'spec')

diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index f47593aad72..9e696e9cb29 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -56,7 +56,7 @@ describe Projects::BlobController do
         end
       end
 
-      context "html_render=false" do
+      context "with viewer=none" do
         let(:id) { 'master/README.md' }
 
         before do
@@ -65,12 +65,13 @@ describe Projects::BlobController do
               project_id: project,
               id: id,
               format: :json,
-              html_render: 'false')
+              viewer: 'none')
         end
 
         it do
           expect(response).to be_ok
           expect(json_response).not_to have_key 'html'
+          expect(json_response).to have_key 'raw_path'
         end
       end
     end
-- 
cgit v1.2.1


From 9d735dc27c09f0d9ce50711dbf99ddd8514f75b9 Mon Sep 17 00:00:00 2001
From: "Marko, Peter" 
Date: Thu, 14 Jun 2018 23:45:17 +0200
Subject: Ensure ordering for group sorting tests

Instead of manually creating list to compare the test result,
use system sort to assure no errors are introduced.

Signed-off-by: Marko, Peter 
---
 spec/requests/api/groups_spec.rb | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'spec')

diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 2e7a5cbeca6..f5218ebfac2 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -152,7 +152,7 @@ describe API::Groups do
         expect(response).to have_gitlab_http_status(200)
         expect(response).to include_pagination_headers
         expect(json_response).to be_an Array
-        expect(response_groups).to eq([group3.name, group1.name, group4.name])
+        expect(response_groups).to eq(Group.visible_to_user(user1).order(:name).pluck(:name))
       end
 
       it "sorts in descending order when passed" do
@@ -161,7 +161,7 @@ describe API::Groups do
         expect(response).to have_gitlab_http_status(200)
         expect(response).to include_pagination_headers
         expect(json_response).to be_an Array
-        expect(response_groups).to eq([group4.name, group1.name, group3.name])
+        expect(response_groups).to eq(Group.visible_to_user(user1).order(name: :desc).pluck(:name))
       end
 
       it "sorts by path in order_by param" do
@@ -170,7 +170,7 @@ describe API::Groups do
         expect(response).to have_gitlab_http_status(200)
         expect(response).to include_pagination_headers
         expect(json_response).to be_an Array
-        expect(response_groups).to eq([group1.name, group4.name, group3.name])
+        expect(response_groups).to eq(Group.visible_to_user(user1).order(:path).pluck(:name))
       end
 
       it "sorts by id in the order_by param" do
@@ -179,7 +179,7 @@ describe API::Groups do
         expect(response).to have_gitlab_http_status(200)
         expect(response).to include_pagination_headers
         expect(json_response).to be_an Array
-        expect(response_groups).to eq([group1.name, group3.name, group4.name])
+        expect(response_groups).to eq(Group.visible_to_user(user1).order(:id).pluck(:name))
       end
     end
 
-- 
cgit v1.2.1


From e906be2f683f6170a5ee1d5b5f104ab0e08062d1 Mon Sep 17 00:00:00 2001
From: Phil Hughes 
Date: Thu, 7 Jun 2018 17:19:35 +0100
Subject: IDE sends last commit ID when committing changes

Closes #46192
---
 .../ide/stores/modules/commit/actions_spec.js      | 101 ++++++---------------
 spec/javascripts/ide/stores/utils_spec.js          |  60 ++++++++++++
 2 files changed, 90 insertions(+), 71 deletions(-)

(limited to 'spec')

diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index a2869ff378b..45101304a4c 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -108,77 +108,6 @@ describe('IDE commit module actions', () => {
     });
   });
 
-  describe('checkCommitStatus', () => {
-    beforeEach(() => {
-      store.state.currentProjectId = 'abcproject';
-      store.state.currentBranchId = 'master';
-      store.state.projects.abcproject = {
-        branches: {
-          master: {
-            workingReference: '1',
-          },
-        },
-      };
-    });
-
-    it('calls service', done => {
-      spyOn(service, 'getBranchData').and.returnValue(
-        Promise.resolve({
-          data: {
-            commit: { id: '123' },
-          },
-        }),
-      );
-
-      store
-        .dispatch('commit/checkCommitStatus')
-        .then(() => {
-          expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
-
-          done();
-        })
-        .catch(done.fail);
-    });
-
-    it('returns true if current ref does not equal returned ID', done => {
-      spyOn(service, 'getBranchData').and.returnValue(
-        Promise.resolve({
-          data: {
-            commit: { id: '123' },
-          },
-        }),
-      );
-
-      store
-        .dispatch('commit/checkCommitStatus')
-        .then(val => {
-          expect(val).toBeTruthy();
-
-          done();
-        })
-        .catch(done.fail);
-    });
-
-    it('returns false if current ref equals returned ID', done => {
-      spyOn(service, 'getBranchData').and.returnValue(
-        Promise.resolve({
-          data: {
-            commit: { id: '1' },
-          },
-        }),
-      );
-
-      store
-        .dispatch('commit/checkCommitStatus')
-        .then(val => {
-          expect(val).toBeFalsy();
-
-          done();
-        })
-        .catch(done.fail);
-    });
-  });
-
   describe('updateFilesAfterCommit', () => {
     const data = {
       id: '123',
@@ -314,6 +243,9 @@ describe('IDE commit module actions', () => {
         ...file('changed'),
         type: 'blob',
         active: true,
+        lastCommit: {
+          id: '123456789',
+        },
       };
       store.state.stagedFiles.push(f);
       store.state.changedFiles = [
@@ -366,6 +298,7 @@ describe('IDE commit module actions', () => {
                   file_path: jasmine.anything(),
                   content: jasmine.anything(),
                   encoding: jasmine.anything(),
+                  last_commit_id: undefined,
                 },
               ],
               start_branch: 'master',
@@ -376,6 +309,32 @@ describe('IDE commit module actions', () => {
           .catch(done.fail);
       });
 
+      it('sends lastCommit ID when not creating new branch', done => {
+        store.state.commit.commitAction = '1';
+
+        store
+          .dispatch('commit/commitChanges')
+          .then(() => {
+            expect(service.commit).toHaveBeenCalledWith('abcproject', {
+              branch: jasmine.anything(),
+              commit_message: 'testing 123',
+              actions: [
+                {
+                  action: 'update',
+                  file_path: jasmine.anything(),
+                  content: jasmine.anything(),
+                  encoding: jasmine.anything(),
+                  last_commit_id: '123456789',
+                },
+              ],
+              start_branch: undefined,
+            });
+
+            done();
+          })
+          .catch(done.fail);
+      });
+
       it('sets last Commit Msg', done => {
         store
           .dispatch('commit/commitChanges')
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index f38ac6dd82f..3f06f287ba9 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -1,4 +1,5 @@
 import * as utils from '~/ide/stores/utils';
+import { file } from '../helpers';
 
 describe('Multi-file store utils', () => {
   describe('setPageTitle', () => {
@@ -63,4 +64,63 @@ describe('Multi-file store utils', () => {
       expect(foundEntry).toBeUndefined();
     });
   });
+
+  describe('createCommitPayload', () => {
+    it('returns API payload', () => {
+      const state = {
+        commitMessage: 'commit message',
+      };
+      const rootState = {
+        stagedFiles: [
+          {
+            ...file('staged'),
+            path: 'staged',
+            content: 'updated file content',
+            lastCommit: {
+              id: '123456789',
+            },
+          },
+          {
+            ...file('newFile'),
+            path: 'added',
+            tempFile: true,
+            content: 'new file content',
+            base64: true,
+            lastCommit: {
+              id: '123456789',
+            },
+          },
+        ],
+        currentBranchId: 'master',
+      };
+      const payload = utils.createCommitPayload({
+        branch: 'master',
+        newBranch: false,
+        state,
+        rootState,
+      });
+
+      expect(payload).toEqual({
+        branch: 'master',
+        commit_message: 'commit message',
+        actions: [
+          {
+            action: 'update',
+            file_path: 'staged',
+            content: 'updated file content',
+            encoding: 'text',
+            last_commit_id: '123456789',
+          },
+          {
+            action: 'create',
+            file_path: 'added',
+            content: 'new file content',
+            encoding: 'base64',
+            last_commit_id: '123456789',
+          },
+        ],
+        start_branch: undefined,
+      });
+    });
+  });
 });
-- 
cgit v1.2.1


From 3530274b13ec4f20731f9f98ea94dabe1494072d Mon Sep 17 00:00:00 2001
From: Phil Hughes 
Date: Fri, 15 Jun 2018 11:12:13 +0100
Subject: only return last_commit_sha in the JSON

---
 spec/javascripts/ide/stores/modules/commit/actions_spec.js | 4 +---
 spec/javascripts/ide/stores/utils_spec.js                  | 8 ++------
 2 files changed, 3 insertions(+), 9 deletions(-)

(limited to 'spec')

diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index 45101304a4c..133ad627f34 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -243,9 +243,7 @@ describe('IDE commit module actions', () => {
         ...file('changed'),
         type: 'blob',
         active: true,
-        lastCommit: {
-          id: '123456789',
-        },
+        lastCommitSha: '123456789',
       };
       store.state.stagedFiles.push(f);
       store.state.changedFiles = [
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index 3f06f287ba9..a7bd443af51 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -76,9 +76,7 @@ describe('Multi-file store utils', () => {
             ...file('staged'),
             path: 'staged',
             content: 'updated file content',
-            lastCommit: {
-              id: '123456789',
-            },
+            lastCommitSha: '123456789',
           },
           {
             ...file('newFile'),
@@ -86,9 +84,7 @@ describe('Multi-file store utils', () => {
             tempFile: true,
             content: 'new file content',
             base64: true,
-            lastCommit: {
-              id: '123456789',
-            },
+            lastCommitSha: '123456789',
           },
         ],
         currentBranchId: 'master',
-- 
cgit v1.2.1


From 5fc3b8a08d9264eedbf886c45b4ef7b418df380d Mon Sep 17 00:00:00 2001
From: Douwe Maan 
Date: Fri, 15 Jun 2018 12:28:23 +0200
Subject: Remove FaviconUploader favicon_main version

See https://gitlab.com/gitlab-org/gitlab-ce/issues/47677 for more information
---
 spec/controllers/uploads_controller_spec.rb       | 17 -------------
 spec/lib/gitlab/favicon_spec.rb                   |  2 +-
 spec/models/project_services/jira_service_spec.rb |  2 +-
 spec/uploaders/favicon_uploader_spec.rb           | 29 -----------------------
 4 files changed, 2 insertions(+), 48 deletions(-)
 delete mode 100644 spec/uploaders/favicon_uploader_spec.rb

(limited to 'spec')

diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 1df2c954893..eb94d395a9e 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -580,23 +580,6 @@ describe UploadsController do
           expect(response).to have_gitlab_http_status(404)
         end
       end
-
-      context 'has a valid filename on the version file' do
-        it 'successfully returns the file' do
-          get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png'
-
-          expect(response).to have_gitlab_http_status(200)
-          expect(response.header['Content-Disposition']).to end_with 'filename="favicon_main_dk.png"'
-        end
-      end
-
-      context 'has an invalid filename on the version file' do
-        it 'returns a 404' do
-          get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png'
-
-          expect(response).to have_gitlab_http_status(404)
-        end
-      end
     end
   end
 end
diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb
index 08c4a474217..f36111a4946 100644
--- a/spec/lib/gitlab/favicon_spec.rb
+++ b/spec/lib/gitlab/favicon_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::Favicon, :request_store do
 
     it 'uses the custom favicon if a favicon appearance is present' do
       create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
-      expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png}
+      expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/dk.png}
     end
   end
 
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index b9f1c7dd5df..6c637533c6b 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -478,7 +478,7 @@ describe JiraService do
       create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
 
       props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
-      expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$}
+      expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/dk.png$}
     end
   end
 end
diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb
deleted file mode 100644
index 37deea8ab90..00000000000
--- a/spec/uploaders/favicon_uploader_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe FaviconUploader do
-  include CarrierWave::Test::Matchers
-
-  let(:uploader) { described_class.new(build_stubbed(:user)) }
-
-  after do
-    uploader.remove!
-  end
-
-  def upload_fixture(filename)
-    fixture_file_upload("spec/fixtures/#{filename}")
-  end
-
-  context 'versions' do
-    before do
-      uploader.store!(upload_fixture('dk.png'))
-    end
-
-    it 'has the correct format' do
-      expect(uploader.favicon_main).to be_format('png')
-    end
-
-    it 'has the correct dimensions' do
-      expect(uploader.favicon_main).to have_dimensions(32, 32)
-    end
-  end
-end
-- 
cgit v1.2.1


From 1fbf6f186948e29dfcd09332a083962904e674ae Mon Sep 17 00:00:00 2001
From: Imre Farkas 
Date: Fri, 15 Jun 2018 10:44:59 +0200
Subject: HTML escape the name of the user in ProjectsHelper#link_to_member

---
 spec/helpers/projects_helper_spec.rb | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

(limited to 'spec')

diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 5cf9e9e8f12..80147b13739 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -248,7 +248,7 @@ describe ProjectsHelper do
   describe '#link_to_member' do
     let(:group)   { build_stubbed(:group) }
     let(:project) { build_stubbed(:project, group: group) }
-    let(:user)    { build_stubbed(:user) }
+    let(:user)    { build_stubbed(:user, name: '

Administrator

') } describe 'using the default options' do it 'returns an HTML link to the user' do @@ -256,6 +256,13 @@ describe ProjectsHelper do expect(link).to match(%r{/#{user.username}}) end + + it 'HTML escapes the name of the user' do + link = helper.link_to_member(project, user) + + expect(link).to include(ERB::Util.html_escape(user.name)) + expect(link).not_to include(user.name) + end end end -- cgit v1.2.1 From d304f883520277334339bf2be1dd20c53a51dc16 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 15 Jun 2018 13:29:55 +0100 Subject: Expose a clean_backtrace method from Gitlab::Profiler This method makes it easier for other parts of the app to get clean backtraces, as well as console users. --- spec/lib/gitlab/profiler_spec.rb | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb index 548eb28fe4d..4059188fba1 100644 --- a/spec/lib/gitlab/profiler_spec.rb +++ b/spec/lib/gitlab/profiler_spec.rb @@ -135,6 +135,51 @@ describe Gitlab::Profiler do end end + describe '.clean_backtrace' do + it 'uses the Rails backtrace cleaner' do + backtrace = [] + + expect(Rails.backtrace_cleaner).to receive(:clean).with(backtrace) + + described_class.clean_backtrace(backtrace) + end + + it 'removes lines from IGNORE_BACKTRACES' do + backtrace = [ + "lib/gitlab/gitaly_client.rb:294:in `block (2 levels) in migrate'", + "lib/gitlab/gitaly_client.rb:331:in `allow_n_plus_1_calls'", + "lib/gitlab/gitaly_client.rb:280:in `block in migrate'", + "lib/gitlab/metrics/influx_db.rb:103:in `measure'", + "lib/gitlab/gitaly_client.rb:278:in `migrate'", + "lib/gitlab/git/repository.rb:1451:in `gitaly_migrate'", + "lib/gitlab/git/commit.rb:66:in `find'", + "app/models/repository.rb:1047:in `find_commit'", + "lib/gitlab/metrics/instrumentation.rb:159:in `block in find_commit'", + "lib/gitlab/metrics/method_call.rb:36:in `measure'", + "lib/gitlab/metrics/instrumentation.rb:159:in `find_commit'", + "app/models/repository.rb:113:in `commit'", + "lib/gitlab/i18n.rb:50:in `with_locale'", + "lib/gitlab/middleware/multipart.rb:95:in `call'", + "lib/gitlab/request_profiler/middleware.rb:14:in `call'", + "ee/lib/gitlab/database/load_balancing/rack_middleware.rb:37:in `call'", + "ee/lib/gitlab/jira/middleware.rb:15:in `call'" + ] + + expect(described_class.clean_backtrace(backtrace)) + .to eq([ + "lib/gitlab/gitaly_client.rb:294:in `block (2 levels) in migrate'", + "lib/gitlab/gitaly_client.rb:331:in `allow_n_plus_1_calls'", + "lib/gitlab/gitaly_client.rb:280:in `block in migrate'", + "lib/gitlab/gitaly_client.rb:278:in `migrate'", + "lib/gitlab/git/repository.rb:1451:in `gitaly_migrate'", + "lib/gitlab/git/commit.rb:66:in `find'", + "app/models/repository.rb:1047:in `find_commit'", + "app/models/repository.rb:113:in `commit'", + "ee/lib/gitlab/jira/middleware.rb:15:in `call'" + ]) + end + end + describe '.with_custom_logger' do context 'when the logger is set' do it 'uses the replacement logger for the duration of the block' do -- cgit v1.2.1 From 9403b1d951c47aca67d2bac1369b86c125c5119d Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Thu, 14 Jun 2018 15:06:53 +0200 Subject: Allow querying a single MR within a project This allows the user to get a single MR nested in a GraphQL project query. Since we need the full path and the iid anyway, this makes more sense than having a root query that needs the full path as well. --- .../resolvers/merge_request_resolver_spec.rb | 23 ++------ spec/graphql/types/project_type_spec.rb | 9 ++++ spec/graphql/types/query_type_spec.rb | 16 +----- .../api/graphql/merge_request_query_spec.rb | 49 ----------------- spec/requests/api/graphql/project_query_spec.rb | 63 +++++++++++++++++++--- spec/support/helpers/graphql_helpers.rb | 15 ++++-- spec/support/matchers/graphql_matchers.rb | 6 +++ 7 files changed, 89 insertions(+), 92 deletions(-) delete mode 100644 spec/requests/api/graphql/merge_request_query_spec.rb (limited to 'spec') diff --git a/spec/graphql/resolvers/merge_request_resolver_spec.rb b/spec/graphql/resolvers/merge_request_resolver_spec.rb index af015533209..73993b3a039 100644 --- a/spec/graphql/resolvers/merge_request_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_request_resolver_spec.rb @@ -10,49 +10,36 @@ describe Resolvers::MergeRequestResolver do set(:other_project) { create(:project, :repository) } set(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) } - let(:full_path) { project.full_path } let(:iid_1) { merge_request_1.iid } let(:iid_2) { merge_request_2.iid } - let(:other_full_path) { other_project.full_path } let(:other_iid) { other_merge_request.iid } describe '#resolve' do it 'batch-resolves merge requests by target project full path and IID' do - path = full_path # avoid database query - result = batch(max_queries: 2) do - [resolve_mr(path, iid_1), resolve_mr(path, iid_2)] + [resolve_mr(project, iid_1), resolve_mr(project, iid_2)] end expect(result).to contain_exactly(merge_request_1, merge_request_2) end it 'can batch-resolve merge requests from different projects' do - path = project.full_path # avoid database queries - other_path = other_full_path - result = batch(max_queries: 3) do - [resolve_mr(path, iid_1), resolve_mr(path, iid_2), resolve_mr(other_path, other_iid)] + [resolve_mr(project, iid_1), resolve_mr(project, iid_2), resolve_mr(other_project, other_iid)] end expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request) end it 'resolves an unknown iid to nil' do - result = batch { resolve_mr(full_path, -1) } - - expect(result).to be_nil - end - - it 'resolves a known iid for an unknown full_path to nil' do - result = batch { resolve_mr('unknown/project', iid_1) } + result = batch { resolve_mr(project, -1) } expect(result).to be_nil end end - def resolve_mr(full_path, iid) - resolve(described_class, args: { full_path: full_path, iid: iid }) + def resolve_mr(project, iid) + resolve(described_class, obj: project, args: { iid: iid }) end end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index e0f89105b86..b4eeca2e3f1 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -2,4 +2,13 @@ require 'spec_helper' describe GitlabSchema.types['Project'] do it { expect(described_class.graphql_name).to eq('Project') } + + describe 'nested merge request' do + it { expect(described_class).to have_graphql_field(:merge_request) } + + it 'authorizes the merge request' do + expect(described_class.fields['mergeRequest']) + .to require_graphql_authorizations(:read_merge_request) + end + end end diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb index 8488252fd59..e1df6f9811d 100644 --- a/spec/graphql/types/query_type_spec.rb +++ b/spec/graphql/types/query_type_spec.rb @@ -5,7 +5,7 @@ describe GitlabSchema.types['Query'] do expect(described_class.graphql_name).to eq('Query') end - it { is_expected.to have_graphql_fields(:project, :merge_request, :echo) } + it { is_expected.to have_graphql_fields(:project, :echo) } describe 'project field' do subject { described_class.fields['project'] } @@ -20,18 +20,4 @@ describe GitlabSchema.types['Query'] do is_expected.to require_graphql_authorizations(:read_project) end end - - describe 'merge_request field' do - subject { described_class.fields['mergeRequest'] } - - it 'finds MRs by project and IID' do - is_expected.to have_graphql_arguments(:full_path, :iid) - is_expected.to have_graphql_type(Types::MergeRequestType) - is_expected.to have_graphql_resolver(Resolvers::MergeRequestResolver) - end - - it 'authorizes with read_merge_request' do - is_expected.to require_graphql_authorizations(:read_merge_request) - end - end end diff --git a/spec/requests/api/graphql/merge_request_query_spec.rb b/spec/requests/api/graphql/merge_request_query_spec.rb deleted file mode 100644 index 12b1d5d18a2..00000000000 --- a/spec/requests/api/graphql/merge_request_query_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -describe 'getting merge request information' do - include GraphqlHelpers - - let(:project) { create(:project, :repository) } - let(:merge_request) { create(:merge_request, source_project: project) } - let(:current_user) { create(:user) } - - let(:query) do - attributes = { - 'fullPath' => merge_request.project.full_path, - 'iid' => merge_request.iid - } - graphql_query_for('mergeRequest', attributes) - end - - context 'when the user has access to the merge request' do - before do - project.add_developer(current_user) - post_graphql(query, current_user: current_user) - end - - it 'returns the merge request' do - expect(graphql_data['mergeRequest']).not_to be_nil - end - - # This is a field coming from the `MergeRequestPresenter` - it 'includes a web_url' do - expect(graphql_data['mergeRequest']['webUrl']).to be_present - end - - it_behaves_like 'a working graphql query' - end - - context 'when the user does not have access to the merge request' do - before do - post_graphql(query, current_user: current_user) - end - - it 'returns an empty field' do - post_graphql(query, current_user: current_user) - - expect(graphql_data['mergeRequest']).to be_nil - end - - it_behaves_like 'a working graphql query' - end -end diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb index 8196bcfa87c..796ffc9d569 100644 --- a/spec/requests/api/graphql/project_query_spec.rb +++ b/spec/requests/api/graphql/project_query_spec.rb @@ -13,27 +13,76 @@ describe 'getting project information' do context 'when the user has access to the project' do before do project.add_developer(current_user) - post_graphql(query, current_user: current_user) end it 'includes the project' do + post_graphql(query, current_user: current_user) + expect(graphql_data['project']).not_to be_nil end - it_behaves_like 'a working graphql query' - end + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end - context 'when the user does not have access to the project' do - before do - post_graphql(query, current_user: current_user) + context 'when requesting a nested merge request' do + let(:merge_request) { create(:merge_request, source_project: project) } + let(:merge_request_graphql_data) { graphql_data['project']['mergeRequest'] } + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('mergeRequest', iid: merge_request.iid) + ) + end + + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end + + it 'contains merge request information' do + post_graphql(query, current_user: current_user) + + expect(merge_request_graphql_data).not_to be_nil + end + + # This is a field coming from the `MergeRequestPresenter` + it 'includes a web_url' do + post_graphql(query, current_user: current_user) + + expect(merge_request_graphql_data['webUrl']).to be_present + end + + context 'when the user does not have access to the merge request' do + let(:project) { create(:project, :public, :repository) } + + it 'returns nil' do + project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE) + + post_graphql(query) + + expect(merge_request_graphql_data).to be_nil + end + end end + end + context 'when the user does not have access to the project' do it 'returns an empty field' do post_graphql(query, current_user: current_user) expect(graphql_data['project']).to be_nil end - it_behaves_like 'a working graphql query' + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end end end diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 30ff9a1196a..0930b9da368 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -34,14 +34,20 @@ module GraphqlHelpers end def graphql_query_for(name, attributes = {}, fields = nil) + <<~QUERY + { + #{query_graphql_field(name, attributes, fields)} + } + QUERY + end + + def query_graphql_field(name, attributes = {}, fields = nil) fields ||= all_graphql_fields_for(name.classify) attributes = attributes_to_graphql(attributes) <<~QUERY - { #{name}(#{attributes}) { #{fields} } - } QUERY end @@ -50,12 +56,15 @@ module GraphqlHelpers return "" unless type type.fields.map do |name, field| + # We can't guess arguments, so skip fields that require them + next if field.arguments.any? + if scalar?(field) name else "#{name} { #{all_graphql_fields_for(field_type(field))} }" end - end.join("\n") + end.compact.join("\n") end def attributes_to_graphql(attributes) diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb index ba7a1c8cde0..d23cbaf4beb 100644 --- a/spec/support/matchers/graphql_matchers.rb +++ b/spec/support/matchers/graphql_matchers.rb @@ -13,6 +13,12 @@ RSpec::Matchers.define :have_graphql_fields do |*expected| end end +RSpec::Matchers.define :have_graphql_field do |field_name| + match do |kls| + expect(kls.fields.keys).to include(GraphqlHelpers.fieldnamerize(field_name)) + end +end + RSpec::Matchers.define :have_graphql_arguments do |*expected| include GraphqlHelpers -- cgit v1.2.1 From ca065e493e7a62f22cb330659e75f8567fa6131f Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 7 May 2018 16:04:11 +0200 Subject: Forbids combining named and unnamed variables in `gitlab.pot` This would break with an argument error when interpolating using `sprintf` in ruby. --- spec/lib/gitlab/i18n/po_linter_spec.rb | 102 ++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 40 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/i18n/po_linter_spec.rb b/spec/lib/gitlab/i18n/po_linter_spec.rb index 3a962ba7f22..9bbd51fe212 100644 --- a/spec/lib/gitlab/i18n/po_linter_spec.rb +++ b/spec/lib/gitlab/i18n/po_linter_spec.rb @@ -5,6 +5,25 @@ describe Gitlab::I18n::PoLinter do let(:linter) { described_class.new(po_path) } let(:po_path) { 'spec/fixtures/valid.po' } + def fake_translation(id:, translation:, plural_id: nil, plurals: []) + data = { msgid: id, msgid_plural: plural_id } + + if plural_id + [translation, *plurals].each_with_index do |plural, index| + allow(FastGettext::Translation).to receive(:n_).with(plural_id, index).and_return(plural) + data.merge!("msgstr[#{index}]" => plural) + end + else + allow(FastGettext::Translation).to receive(:_).with(id).and_return(translation) + data[:msgstr] = translation + end + + Gitlab::I18n::TranslationEntry.new( + data, + plurals.size + 1 + ) + end + describe '#errors' do it 'only calls validation once' do expect(linter).to receive(:validate_po).once.and_call_original @@ -155,9 +174,8 @@ describe Gitlab::I18n::PoLinter do describe '#validate_entries' do it 'keeps track of errors for entries' do - fake_invalid_entry = Gitlab::I18n::TranslationEntry.new( - { msgid: "Hello %{world}", msgstr: "Bonjour %{monde}" }, 2 - ) + fake_invalid_entry = fake_translation(id: "Hello %{world}", + translation: "Bonjour %{monde}") allow(linter).to receive(:translation_entries) { [fake_invalid_entry] } expect(linter).to receive(:validate_entry) @@ -177,6 +195,7 @@ describe Gitlab::I18n::PoLinter do expect(linter).to receive(:validate_newlines).with([], fake_entry) expect(linter).to receive(:validate_number_of_plurals).with([], fake_entry) expect(linter).to receive(:validate_unescaped_chars).with([], fake_entry) + expect(linter).to receive(:validate_translation).with([], fake_entry) linter.validate_entry(fake_entry) end @@ -201,13 +220,12 @@ describe Gitlab::I18n::PoLinter do end describe '#validate_variables' do - it 'validates both signular and plural in a pluralized string when the entry has a singular' do - pluralized_entry = Gitlab::I18n::TranslationEntry.new( - { msgid: 'Hello %{world}', - msgid_plural: 'Hello all %{world}', - 'msgstr[0]' => 'Bonjour %{world}', - 'msgstr[1]' => 'Bonjour tous %{world}' }, - 2 + it 'validates both singular and plural in a pluralized string when the entry has a singular' do + pluralized_entry = fake_translation( + id: 'Hello %{world}', + translation: 'Bonjour %{world}', + plural_id: 'Hello all %{world}', + plurals: ['Bonjour tous %{world}'] ) expect(linter).to receive(:validate_variables_in_message) @@ -221,11 +239,10 @@ describe Gitlab::I18n::PoLinter do end it 'only validates plural when there is no separate singular' do - pluralized_entry = Gitlab::I18n::TranslationEntry.new( - { msgid: 'Hello %{world}', - msgid_plural: 'Hello all %{world}', - 'msgstr[0]' => 'Bonjour %{world}' }, - 1 + pluralized_entry = fake_translation( + id: 'Hello %{world}', + translation: 'Bonjour %{world}', + plural_id: 'Hello all %{world}' ) expect(linter).to receive(:validate_variables_in_message) @@ -235,10 +252,7 @@ describe Gitlab::I18n::PoLinter do end it 'validates the message variables' do - entry = Gitlab::I18n::TranslationEntry.new( - { msgid: 'Hello', msgstr: 'Bonjour' }, - 2 - ) + entry = fake_translation(id: 'Hello', translation: 'Bonjour') expect(linter).to receive(:validate_variables_in_message) .with([], 'Hello', 'Bonjour') @@ -251,21 +265,34 @@ describe Gitlab::I18n::PoLinter do it 'detects when a variables are used incorrectly' do errors = [] - expected_errors = [' is missing: [%{hello}]', - ' is using unknown variables: [%{world}]', - 'is combining multiple unnamed variables'] + expected_errors = ['<%d hello %{world} %s> is missing: [%{hello}]', + '<%d hello %{world} %s> is using unknown variables: [%{world}]', + 'is combining multiple unnamed variables', + 'is combining named variables with unnamed variables'] - linter.validate_variables_in_message(errors, '%{hello} world %d', 'hello %{world} %d') + linter.validate_variables_in_message(errors, '%d %{hello} world %s', '%d hello %{world} %s') expect(errors).to include(*expected_errors) end + + it 'does not allow combining 1 `%d` unnamed variable with named variables' do + errors = [] + + linter.validate_variables_in_message(errors, + '%{type} detected %d vulnerability', + '%{type} detecteerde %d kwetsbaarheid') + + expect(errors).not_to be_empty + end end describe '#validate_translation' do + let(:entry) { fake_translation(id: 'Hello %{world}', translation: 'Bonjour %{world}') } + it 'succeeds with valid variables' do errors = [] - linter.validate_translation(errors, 'Hello %{world}', ['%{world}']) + linter.validate_translation(errors, entry) expect(errors).to be_empty end @@ -275,43 +302,38 @@ describe Gitlab::I18n::PoLinter do expect(FastGettext::Translation).to receive(:_) { raise 'broken' } - linter.validate_translation(errors, 'Hello', []) + linter.validate_translation(errors, entry) - expect(errors).to include('Failure translating to en with []: broken') + expect(errors).to include('Failure translating to en: broken') end it 'adds an error message when translating fails when translating with context' do + entry = fake_translation(id: 'Tests|Hello', translation: 'broken') errors = [] expect(FastGettext::Translation).to receive(:s_) { raise 'broken' } - linter.validate_translation(errors, 'Tests|Hello', []) + linter.validate_translation(errors, entry) - expect(errors).to include('Failure translating to en with []: broken') + expect(errors).to include('Failure translating to en: broken') end it "adds an error when trying to translate with incorrect variables when using unnamed variables" do + entry = fake_translation(id: 'Hello %s', translation: 'Hello %d') errors = [] - linter.validate_translation(errors, 'Hello %d', ['%s']) + linter.validate_translation(errors, entry) - expect(errors.first).to start_with("Failure translating to en with") + expect(errors.first).to start_with("Failure translating to en") end it "adds an error when trying to translate with named variables when unnamed variables are expected" do + entry = fake_translation(id: 'Hello %s', translation: 'Hello %{thing}') errors = [] - linter.validate_translation(errors, 'Hello %d', ['%{world}']) - - expect(errors.first).to start_with("Failure translating to en with") - end - - it 'adds an error when translated with incorrect variables using named variables' do - errors = [] - - linter.validate_translation(errors, 'Hello %{thing}', ['%d']) + linter.validate_translation(errors, entry) - expect(errors.first).to start_with("Failure translating to en with") + expect(errors.first).to start_with("Failure translating to en") end end -- cgit v1.2.1 From 3b5ce6945dee059717855962271245bbb29f24e1 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 7 May 2018 17:56:18 +0200 Subject: Validate PO-variable usage in message ids That way we can detect incorrect usage before the strings are added to Crowdin for translation --- spec/lib/gitlab/i18n/po_linter_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/i18n/po_linter_spec.rb b/spec/lib/gitlab/i18n/po_linter_spec.rb index 9bbd51fe212..045916e6dc2 100644 --- a/spec/lib/gitlab/i18n/po_linter_spec.rb +++ b/spec/lib/gitlab/i18n/po_linter_spec.rb @@ -220,6 +220,10 @@ describe Gitlab::I18n::PoLinter do end describe '#validate_variables' do + before do + allow(linter).to receive(:validate_variables_in_message).and_call_original + end + it 'validates both singular and plural in a pluralized string when the entry has a singular' do pluralized_entry = fake_translation( id: 'Hello %{world}', @@ -259,6 +263,24 @@ describe Gitlab::I18n::PoLinter do linter.validate_variables([], entry) end + + it 'validates variable usage in message ids' do + entry = fake_translation( + id: 'Hello %{world}', + translation: 'Bonjour %{world}', + plural_id: 'Hello all %{world}', + plurals: ['Bonjour tous %{world}'] + ) + + expect(linter).to receive(:validate_variables_in_message) + .with([], 'Hello %{world}', 'Hello %{world}') + .and_call_original + expect(linter).to receive(:validate_variables_in_message) + .with([], 'Hello all %{world}', 'Hello all %{world}') + .and_call_original + + linter.validate_variables([], entry) + end end describe '#validate_variables_in_message' do -- cgit v1.2.1 From 17fc178cb5a68be787cb3fa243aed4cf1abb24a3 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 14 May 2018 15:28:39 +0200 Subject: Correctly translate all forms in tests --- spec/lib/gitlab/i18n/metadata_entry_spec.rb | 6 +-- spec/lib/gitlab/i18n/po_linter_spec.rb | 73 +++++++++++++++++++++----- spec/lib/gitlab/i18n/translation_entry_spec.rb | 6 +-- 3 files changed, 65 insertions(+), 20 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/i18n/metadata_entry_spec.rb b/spec/lib/gitlab/i18n/metadata_entry_spec.rb index ab71d6454a9..a399517cc04 100644 --- a/spec/lib/gitlab/i18n/metadata_entry_spec.rb +++ b/spec/lib/gitlab/i18n/metadata_entry_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::I18n::MetadataEntry do - describe '#expected_plurals' do + describe '#expected_forms' do it 'returns the number of plurals' do data = { msgid: "", @@ -22,7 +22,7 @@ describe Gitlab::I18n::MetadataEntry do } entry = described_class.new(data) - expect(entry.expected_plurals).to eq(2) + expect(entry.expected_forms).to eq(2) end it 'returns 0 for the POT-metadata' do @@ -45,7 +45,7 @@ describe Gitlab::I18n::MetadataEntry do } entry = described_class.new(data) - expect(entry.expected_plurals).to eq(0) + expect(entry.expected_forms).to eq(0) end end end diff --git a/spec/lib/gitlab/i18n/po_linter_spec.rb b/spec/lib/gitlab/i18n/po_linter_spec.rb index 045916e6dc2..3dbc23d2aaf 100644 --- a/spec/lib/gitlab/i18n/po_linter_spec.rb +++ b/spec/lib/gitlab/i18n/po_linter_spec.rb @@ -1,20 +1,22 @@ require 'spec_helper' require 'simple_po_parser' +# Disabling this cop to allow for multi-language examples in comments +# rubocop:disable Style/AsciiComments describe Gitlab::I18n::PoLinter do let(:linter) { described_class.new(po_path) } let(:po_path) { 'spec/fixtures/valid.po' } - def fake_translation(id:, translation:, plural_id: nil, plurals: []) - data = { msgid: id, msgid_plural: plural_id } + def fake_translation(msgid:, translation:, plural_id: nil, plurals: []) + data = { msgid: msgid, msgid_plural: plural_id } if plural_id [translation, *plurals].each_with_index do |plural, index| - allow(FastGettext::Translation).to receive(:n_).with(plural_id, index).and_return(plural) + allow(FastGettext::Translation).to receive(:n_).with(msgid, plural_id, index).and_return(plural) data.merge!("msgstr[#{index}]" => plural) end else - allow(FastGettext::Translation).to receive(:_).with(id).and_return(translation) + allow(FastGettext::Translation).to receive(:_).with(msgid).and_return(translation) data[:msgstr] = translation end @@ -174,7 +176,7 @@ describe Gitlab::I18n::PoLinter do describe '#validate_entries' do it 'keeps track of errors for entries' do - fake_invalid_entry = fake_translation(id: "Hello %{world}", + fake_invalid_entry = fake_translation(msgid: "Hello %{world}", translation: "Bonjour %{monde}") allow(linter).to receive(:translation_entries) { [fake_invalid_entry] } @@ -204,7 +206,7 @@ describe Gitlab::I18n::PoLinter do describe '#validate_number_of_plurals' do it 'validates when there are an incorrect number of translations' do fake_metadata = double - allow(fake_metadata).to receive(:expected_plurals).and_return(2) + allow(fake_metadata).to receive(:expected_forms).and_return(2) allow(linter).to receive(:metadata_entry).and_return(fake_metadata) fake_entry = Gitlab::I18n::TranslationEntry.new( @@ -226,7 +228,7 @@ describe Gitlab::I18n::PoLinter do it 'validates both singular and plural in a pluralized string when the entry has a singular' do pluralized_entry = fake_translation( - id: 'Hello %{world}', + msgid: 'Hello %{world}', translation: 'Bonjour %{world}', plural_id: 'Hello all %{world}', plurals: ['Bonjour tous %{world}'] @@ -244,7 +246,7 @@ describe Gitlab::I18n::PoLinter do it 'only validates plural when there is no separate singular' do pluralized_entry = fake_translation( - id: 'Hello %{world}', + msgid: 'Hello %{world}', translation: 'Bonjour %{world}', plural_id: 'Hello all %{world}' ) @@ -256,7 +258,7 @@ describe Gitlab::I18n::PoLinter do end it 'validates the message variables' do - entry = fake_translation(id: 'Hello', translation: 'Bonjour') + entry = fake_translation(msgid: 'Hello', translation: 'Bonjour') expect(linter).to receive(:validate_variables_in_message) .with([], 'Hello', 'Bonjour') @@ -266,7 +268,7 @@ describe Gitlab::I18n::PoLinter do it 'validates variable usage in message ids' do entry = fake_translation( - id: 'Hello %{world}', + msgid: 'Hello %{world}', translation: 'Bonjour %{world}', plural_id: 'Hello all %{world}', plurals: ['Bonjour tous %{world}'] @@ -309,7 +311,7 @@ describe Gitlab::I18n::PoLinter do end describe '#validate_translation' do - let(:entry) { fake_translation(id: 'Hello %{world}', translation: 'Bonjour %{world}') } + let(:entry) { fake_translation(msgid: 'Hello %{world}', translation: 'Bonjour %{world}') } it 'succeeds with valid variables' do errors = [] @@ -330,7 +332,7 @@ describe Gitlab::I18n::PoLinter do end it 'adds an error message when translating fails when translating with context' do - entry = fake_translation(id: 'Tests|Hello', translation: 'broken') + entry = fake_translation(msgid: 'Tests|Hello', translation: 'broken') errors = [] expect(FastGettext::Translation).to receive(:s_) { raise 'broken' } @@ -341,7 +343,7 @@ describe Gitlab::I18n::PoLinter do end it "adds an error when trying to translate with incorrect variables when using unnamed variables" do - entry = fake_translation(id: 'Hello %s', translation: 'Hello %d') + entry = fake_translation(msgid: 'Hello %s', translation: 'Hello %d') errors = [] linter.validate_translation(errors, entry) @@ -350,13 +352,55 @@ describe Gitlab::I18n::PoLinter do end it "adds an error when trying to translate with named variables when unnamed variables are expected" do - entry = fake_translation(id: 'Hello %s', translation: 'Hello %{thing}') + entry = fake_translation(msgid: 'Hello %s', translation: 'Hello %{thing}') errors = [] linter.validate_translation(errors, entry) expect(errors.first).to start_with("Failure translating to en") end + + it 'tests translation for all given forms' do + # Fake a language that has 3 forms to translate + fake_metadata = double + allow(fake_metadata).to receive(:forms_to_test).and_return(3) + allow(linter).to receive(:metadata_entry).and_return(fake_metadata) + entry = fake_translation( + msgid: '%d exception', + translation: '%d uitzondering', + plural_id: '%d exceptions', + plurals: ['%d uitzonderingen', '%d uitzonderingetjes'] + ) + + # Make each count use a different index + allow(linter).to receive(:index_for_pluralization).with(0).and_return(0) + allow(linter).to receive(:index_for_pluralization).with(1).and_return(1) + allow(linter).to receive(:index_for_pluralization).with(2).and_return(2) + + expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 0).and_call_original + expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 1).and_call_original + expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 2).and_call_original + + linter.validate_translation([], entry) + end + end + + describe '#numbers_covering_all_plurals' do + it 'can correctly find all required numbers to translate to Polish' do + # Polish used as an example with 3 different forms: + # 0, all plurals except the ones ending in 2,3,4: Kotów + # 1: Kot + # 2-3-4: Koty + # So translating with [0, 1, 2] will give us all different posibilities + fake_metadata = double + allow(fake_metadata).to receive(:forms_to_test).and_return(4) + allow(linter).to receive(:metadata_entry).and_return(fake_metadata) + allow(linter).to receive(:locale).and_return('pl_PL') + + numbers = linter.numbers_covering_all_plurals + + expect(numbers).to contain_exactly(0, 1, 2) + end end describe '#fill_in_variables' do @@ -380,3 +424,4 @@ describe Gitlab::I18n::PoLinter do end end end +# rubocop:enable Style/AsciiComments diff --git a/spec/lib/gitlab/i18n/translation_entry_spec.rb b/spec/lib/gitlab/i18n/translation_entry_spec.rb index f68bc8feff9..b301e6ea443 100644 --- a/spec/lib/gitlab/i18n/translation_entry_spec.rb +++ b/spec/lib/gitlab/i18n/translation_entry_spec.rb @@ -109,7 +109,7 @@ describe Gitlab::I18n::TranslationEntry do data = { msgid: %w(hello world) } entry = described_class.new(data, 2) - expect(entry.msgid_contains_newlines?).to be_truthy + expect(entry.msgid_has_multiple_lines?).to be_truthy end end @@ -118,7 +118,7 @@ describe Gitlab::I18n::TranslationEntry do data = { msgid_plural: %w(hello world) } entry = described_class.new(data, 2) - expect(entry.plural_id_contains_newlines?).to be_truthy + expect(entry.plural_id_has_multiple_lines?).to be_truthy end end @@ -127,7 +127,7 @@ describe Gitlab::I18n::TranslationEntry do data = { msgstr: %w(hello world) } entry = described_class.new(data, 2) - expect(entry.translations_contain_newlines?).to be_truthy + expect(entry.translations_have_multiple_lines?).to be_truthy end end -- cgit v1.2.1 From 564c819b3deec8617f87d59f904c0364b031e05c Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 15 Jun 2018 09:39:27 -0500 Subject: Fix specs --- spec/features/labels_hierarchy_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 4700ada1aae..5573148f8bc 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -34,7 +34,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do wait_for_requests - expect(page).to have_selector('span.badge', text: label.title) + expect(page).to have_selector('.badge', text: label.title) end end @@ -45,7 +45,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do wait_for_requests - expect(page).not_to have_selector('span.badge', text: child_group_label.title) + expect(page).not_to have_selector('.badge', text: child_group_label.title) end end -- cgit v1.2.1 From bce890820f6ed0e75537b546d5ef1a0090c53b52 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Fri, 15 Jun 2018 15:58:27 +0000 Subject: Enable no-restricted-globals in JS files --- spec/javascripts/blob/viewer/index_spec.js | 4 ++-- spec/javascripts/bootstrap_linked_tabs_spec.js | 2 +- spec/javascripts/commits_spec.js | 2 +- spec/javascripts/environments/environments_app_spec.js | 2 +- spec/javascripts/environments/folder/environments_folder_view_spec.js | 2 +- spec/javascripts/ide/stores/actions/file_spec.js | 4 ++-- spec/javascripts/issue_show/components/app_spec.js | 4 ++-- spec/javascripts/lib/utils/common_utils_spec.js | 4 ++-- spec/javascripts/pipelines/pipelines_spec.js | 2 +- spec/javascripts/settings_panels_spec.js | 4 ++-- 10 files changed, 15 insertions(+), 15 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js index f920c4ca945..8b79624d9f4 100644 --- a/spec/javascripts/blob/viewer/index_spec.js +++ b/spec/javascripts/blob/viewer/index_spec.js @@ -32,7 +32,7 @@ describe('Blob viewer', () => { afterEach(() => { mock.restore(); - location.hash = ''; + window.location.hash = ''; }); it('loads source file after switching views', (done) => { @@ -49,7 +49,7 @@ describe('Blob viewer', () => { }); it('loads source file when line number is in hash', (done) => { - location.hash = '#L1'; + window.location.hash = '#L1'; new BlobViewer(); diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js index 93dc60d59fe..6f679369289 100644 --- a/spec/javascripts/bootstrap_linked_tabs_spec.js +++ b/spec/javascripts/bootstrap_linked_tabs_spec.js @@ -36,7 +36,7 @@ import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs'; describe('on click', () => { it('should change the url according to the clicked tab', () => { - const historySpy = spyOn(history, 'replaceState').and.callFake(() => {}); + const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {}); const linkedTabs = new LinkedTabs({ action: 'show', diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js index 60d100e8544..28b89157bd3 100644 --- a/spec/javascripts/commits_spec.js +++ b/spec/javascripts/commits_spec.js @@ -56,7 +56,7 @@ describe('Commits List', () => { beforeEach(() => { commitsList.searchField.val(''); - spyOn(history, 'replaceState').and.stub(); + spyOn(window.history, 'replaceState').and.stub(); mock = new MockAdapter(axios); mock.onGet('/h5bp/html5-boilerplate/commits/master').reply(200, { diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js index 615168645b4..6968fbc7ce7 100644 --- a/spec/javascripts/environments/environments_app_spec.js +++ b/spec/javascripts/environments/environments_app_spec.js @@ -220,7 +220,7 @@ describe('Environment', () => { ); component = mountComponent(EnvironmentsComponent, mockData); - spyOn(history, 'pushState').and.stub(); + spyOn(window.history, 'pushState').and.stub(); }); describe('updateContent', () => { diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js index f5ce4df0bfe..51d4213c38f 100644 --- a/spec/javascripts/environments/folder/environments_folder_view_spec.js +++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js @@ -177,7 +177,7 @@ describe('Environments Folder View', () => { }); component = mountComponent(Component, mockData); - spyOn(history, 'pushState').and.stub(); + spyOn(window.history, 'pushState').and.stub(); }); describe('updateContent', () => { diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index 7bebc2288e3..5746683917e 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -166,12 +166,12 @@ describe('IDE store file actions', () => { }); it('resets location.hash for line highlighting', done => { - location.hash = 'test'; + window.location.hash = 'test'; store .dispatch('setFileActive', localFile.path) .then(() => { - expect(location.hash).not.toBe('test'); + expect(window.location.hash).not.toBe('test'); done(); }) diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index bf1f0c822fe..eb5e0bddb74 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -145,7 +145,7 @@ describe('Issuable output', () => { resolve({ data: { confidential: false, - web_url: location.pathname, + web_url: window.location.pathname, }, }); })); @@ -177,7 +177,7 @@ describe('Issuable output', () => { spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { resolve({ data: { - web_url: location.pathname, + web_url: window.location.pathname, confidential: vm.isConfidential, }, }); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 2d7cc3443cf..a9ec7f42a9d 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -40,13 +40,13 @@ describe('common_utils', () => { }); it('should decode params', () => { - history.pushState('', '', '?label_name%5B%5D=test'); + window.history.pushState('', '', '?label_name%5B%5D=test'); expect( commonUtils.getUrlParamsArray()[0], ).toBe('label_name[]=test'); - history.pushState('', '', '?'); + window.history.pushState('', '', '?'); }); }); diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js index ff17602da2b..50141bd99b4 100644 --- a/spec/javascripts/pipelines/pipelines_spec.js +++ b/spec/javascripts/pipelines/pipelines_spec.js @@ -427,7 +427,7 @@ describe('Pipelines', () => { describe('methods', () => { beforeEach(() => { - spyOn(history, 'pushState').and.stub(); + spyOn(window.history, 'pushState').and.stub(); }); describe('updateContent', () => { diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js index 4fba36bd4de..c1a69bd7018 100644 --- a/spec/javascripts/settings_panels_spec.js +++ b/spec/javascripts/settings_panels_spec.js @@ -9,11 +9,11 @@ describe('Settings Panels', () => { describe('initSettingsPane', () => { afterEach(() => { - location.hash = ''; + window.location.hash = ''; }); it('should expand linked hash fragment panel', () => { - location.hash = '#autodevops-settings'; + window.location.hash = '#autodevops-settings'; const pipelineSettingsPanel = document.querySelector('#autodevops-settings'); // Our test environment automatically expands everything so we need to clear that out first -- cgit v1.2.1 From 5104098fab7f72fff15d5e1a69f1597391b59b31 Mon Sep 17 00:00:00 2001 From: Isaac Smith Date: Fri, 15 Jun 2018 13:22:56 -0500 Subject: Fix #47796 "Web IDE autocomplete should be disabled in markdown files" --- spec/javascripts/ide/lib/editor_spec.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js index c1932284d53..c2cb964ea87 100644 --- a/spec/javascripts/ide/lib/editor_spec.js +++ b/spec/javascripts/ide/lib/editor_spec.js @@ -263,4 +263,23 @@ describe('Multi-file editor library', () => { expect(instance.isDiffEditorType).toBe(false); }); }); + + it('sets quickSuggestions to false when language is markdown', () => { + instance.createInstance(holder); + + spyOn(instance.instance, 'updateOptions').and.callThrough(); + + const model = instance.createModel({ + ...file(), + key: 'index.md', + path: 'index.md', + }); + + instance.attachModel(model); + + expect(instance.instance.updateOptions).toHaveBeenCalledWith({ + readOnly: false, + quickSuggestions: false, + }); + }); }); -- cgit v1.2.1 From dbc74800e22c9ea417d30fd4c357d6154cc71870 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Wed, 13 Jun 2018 23:18:05 -0700 Subject: Enable no-multi-assignment in JS files Partially addresses #47006. --- spec/javascripts/gl_field_errors_spec.js | 4 +++- spec/javascripts/helpers/user_mock_data_helper.js | 2 +- spec/javascripts/profile/account/components/update_username_spec.js | 3 ++- spec/javascripts/test_bundle.js | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index 4e93fd91751..108e0064c47 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -8,7 +8,9 @@ describe('GL Style Field Errors', function() { beforeEach(function() { loadFixtures('static/gl_field_errors.html.raw'); - const $form = this.$form = $('form.gl-show-field-errors'); + const $form = $('form.gl-show-field-errors'); + + this.$form = $form; this.fieldErrors = new GlFieldErrors($form); }); diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js index 323fee3767e..f6c3ce5aecc 100644 --- a/spec/javascripts/helpers/user_mock_data_helper.js +++ b/spec/javascripts/helpers/user_mock_data_helper.js @@ -1,7 +1,7 @@ export default { createNumberRandomUsers(numberUsers) { const users = []; - for (let i = 0; i < numberUsers; i = i += 1) { + for (let i = 0; i < numberUsers; i += 1) { users.push( { avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', diff --git a/spec/javascripts/profile/account/components/update_username_spec.js b/spec/javascripts/profile/account/components/update_username_spec.js index bac306edf5a..5311499fb73 100644 --- a/spec/javascripts/profile/account/components/update_username_spec.js +++ b/spec/javascripts/profile/account/components/update_username_spec.js @@ -90,7 +90,8 @@ describe('UpdateUsername component', () => { it('confirmation modal should escape usernames properly', done => { const { modalBody } = findElements(); - vm.username = vm.newUsername = 'Italic'; + vm.username = 'Italic'; + vm.newUsername = vm.username; Vue.nextTick() .then(() => { diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 2411d33a496..994011b262a 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -39,7 +39,8 @@ jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH; beforeAll(() => jasmine.addMatchers(customMatchers)); // globalize common libraries -window.$ = window.jQuery = $; +window.$ = $; +window.jQuery = window.$; // stub expected globals window.gl = window.gl || {}; -- cgit v1.2.1 From c6420ce2ab18681d6a5b06621d89775e3cf81065 Mon Sep 17 00:00:00 2001 From: Mario de la Ossa Date: Thu, 14 Jun 2018 13:39:17 -0600 Subject: Fix milestones disappearing when changed on MR --- .../projects/merge_requests_controller_spec.rb | 2 +- .../api/schemas/entities/merge_request_basic.json | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 22858de0475..a412e74581d 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -234,7 +234,7 @@ describe Projects::MergeRequestsController do body = JSON.parse(response.body) expect(body['assignee'].keys) - .to match_array(%w(name username avatar_url)) + .to match_array(%w(name username avatar_url id state web_url)) end end diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json index f7bc137c90c..cf257ac00de 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_basic.json +++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json @@ -14,7 +14,21 @@ "subscribed": { "type": ["boolean", "null"] }, "participants": { "type": "array" }, "allow_collaboration": { "type": "boolean"}, - "allow_maintainer_to_push": { "type": "boolean"} + "allow_maintainer_to_push": { "type": "boolean"}, + "assignee": { + "oneOf": [ + { "type": "null" }, + { "$ref": "user.json" } + ] + }, + "milestone": { + "type": [ "object", "null" ] + }, + "labels": { + "type": [ "array", "null" ] + }, + "task_status": { "type": "string" }, + "task_status_short": { "type": "string" } }, "additionalProperties": false } -- cgit v1.2.1 From a4f9ef7d79f0a9e61e6c3aaf1c4a5670324d837d Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Sat, 16 Jun 2018 13:16:48 +1100 Subject: [Rails5] Fix "'-1' is not a valid data_store" Part of https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19882 --- spec/models/ci/build_trace_chunk_spec.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'spec') diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index b5a6d959ccb..c5d550cba1b 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -125,14 +125,6 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end end end - - context 'when data_store is others' do - before do - build_trace_chunk.send(:write_attribute, :data_store, -1) - end - - it { expect { subject }.to raise_error('Unsupported data store') } - end end describe '#truncate' do -- cgit v1.2.1 From 00c68e1b03ed92eef6aa6ab3fb84b827b14b9daa Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Fri, 15 Jun 2018 13:40:14 +0800 Subject: Fix xss for Markdown elements where [[_TOC_]] is enabled --- spec/lib/banzai/filter/table_of_contents_filter_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'spec') diff --git a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb index 0cfef4ff5bf..7213cd58ea7 100644 --- a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb +++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb @@ -139,5 +139,14 @@ describe Banzai::Filter::TableOfContentsFilter do expect(items[5].ancestors).to include(items[4]) end end + + context 'header text contains escaped content' do + let(:content) { '<img src="x" onerror="alert(42)">' } + let(:results) { result(header(1, content)) } + + it 'outputs escaped content' do + expect(doc.inner_html).to include(content) + end + end end end -- cgit v1.2.1 From 2b1153f71a680b0c5a4fb787897c20068a405fbf Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Sat, 16 Jun 2018 15:26:15 +1100 Subject: [Rails5] Fix pipeline_schedules_controller_spec In Rails 5.0 there's a bug [1] in controller tests that doesn't parse nested parameters correctly. This commit brings a workaround to make specs work again both in rails4 and rails5. [1]: https://github.com/rails/rails/issues/23997 --- .../projects/pipeline_schedules_controller_spec.rb | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb index 3506305f755..4cdaa54e0bc 100644 --- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb +++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb @@ -310,9 +310,19 @@ describe Projects::PipelineSchedulesController do end def go - put :update, namespace_id: project.namespace.to_param, - project_id: project, id: pipeline_schedule, - schedule: schedule + if Gitlab.rails5? + put :update, params: { namespace_id: project.namespace.to_param, + project_id: project, + id: pipeline_schedule, + schedule: schedule }, + as: :html + + else + put :update, namespace_id: project.namespace.to_param, + project_id: project, + id: pipeline_schedule, + schedule: schedule + end end end -- cgit v1.2.1 From 79c3ec014c7db667457268a75f9c47bee8653650 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Sat, 16 Jun 2018 19:12:46 +1100 Subject: [Rails5] Fix sessions_controller_spec Refactor sessions_controller_spec to work both in rails4 and rails5. Remove the stubbed `request.referer` method, set real header instead. --- spec/controllers/sessions_controller_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'spec') diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 555b186fe31..2b61e0d4a85 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -257,15 +257,15 @@ describe SessionsController do end end - describe '#new' do + describe "#new" do before do set_devise_mapping(context: @request) end - it 'redirects correctly for referer on same host with params' do - search_path = '/search?search=seed_project' - allow(controller.request).to receive(:referer) - .and_return('http://%{host}%{path}' % { host: 'test.host', path: search_path }) + it "redirects correctly for referer on same host with params" do + host = "test.host" + search_path = "/search?search=seed_project" + request.headers[:HTTP_REFERER] = "http://#{host}#{search_path}" get(:new, redirect_to_referer: :yes) -- cgit v1.2.1 From 20adfb53693879a4b5c29b752fc06a130df18d42 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Sat, 16 Jun 2018 21:20:58 +0200 Subject: Fix nil formats test One of the spec files sets `nil` value for env varialbe `action_dispatch.request.formats`. Rather than setting `nil` value, we can delete the `action_dispatch.request.formats` key from `env` (which should be closer to real use case anyway). The reason is that in Rails 5, `formats` method does not set default array of formats if the env key is already set (even if set to `nil`). --- spec/lib/gitlab/auth/user_auth_finders_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb index 136646bd4ee..454ad1589b9 100644 --- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb @@ -99,7 +99,7 @@ describe Gitlab::Auth::UserAuthFinders do context 'when the request format is empty' do it 'the method call does not modify the original value' do - env['action_dispatch.request.formats'] = nil + env.delete('action_dispatch.request.formats') find_user_from_feed_token -- cgit v1.2.1 From cc48d67ca57ac99e5c61b422a56d96a1643a7de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Sun, 17 Jun 2018 16:47:25 +0200 Subject: Optimised paused runners to not re-query every 3s We have long polling mechanism which is not being used by paused runners. This makes the paused runner to generate a request every 3s, where it could be fired only every 1h. --- spec/requests/api/runner_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 16e6f19773f..e7639599874 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -351,11 +351,13 @@ describe API::Runner, :clean_gitlab_redis_shared_state do context 'when valid token is provided' do context 'when Runner is not active' do let(:runner) { create(:ci_runner, :inactive) } + let(:update_value) { runner.ensure_runner_queue_value } it 'returns 204 error' do request_job - expect(response).to have_gitlab_http_status 204 + expect(response).to have_gitlab_http_status(204) + expect(response.header['X-GitLab-Last-Update']).to eq(update_value) end end -- cgit v1.2.1 From 14000a561b8ed59905b6700541703fa7bcdb68d0 Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Mon, 18 Jun 2018 08:19:11 +0000 Subject: Make Gitaly wiki RPC's mandatory --- spec/lib/gitlab/git/committer_with_hooks_spec.rb | 216 ++++++++++++----------- spec/lib/gitlab/git/wiki_spec.rb | 6 +- 2 files changed, 111 insertions(+), 111 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb index 267056b96e6..2100690f873 100644 --- a/spec/lib/gitlab/git/committer_with_hooks_spec.rb +++ b/spec/lib/gitlab/git/committer_with_hooks_spec.rb @@ -1,154 +1,156 @@ require 'spec_helper' describe Gitlab::Git::CommitterWithHooks, seed_helper: true do - shared_examples 'calling wiki hooks' do - let(:project) { create(:project) } - let(:user) { project.owner } - let(:project_wiki) { ProjectWiki.new(project, user) } - let(:wiki) { project_wiki.wiki } - let(:options) do - { - id: user.id, - username: user.username, - name: user.name, - email: user.email, - message: 'commit message' - } - end - - subject { described_class.new(wiki, options) } + # TODO https://gitlab.com/gitlab-org/gitaly/issues/1234 + skip 'needs to be moved to gitaly-ruby test suite' do + shared_examples 'calling wiki hooks' do + let(:project) { create(:project) } + let(:user) { project.owner } + let(:project_wiki) { ProjectWiki.new(project, user) } + let(:wiki) { project_wiki.wiki } + let(:options) do + { + id: user.id, + username: user.username, + name: user.name, + email: user.email, + message: 'commit message' + } + end - before do - project_wiki.create_page('home', 'test content') - end + subject { described_class.new(wiki, options) } - shared_examples 'failing pre-receive hook' do before do - expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([false, '']) - expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('update') - expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive') + project_wiki.create_page('home', 'test content') end - it 'raises exception' do - expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) - end + shared_examples 'failing pre-receive hook' do + before do + expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([false, '']) + expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('update') + expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive') + end - it 'does not create a new commit inside the repository' do - current_rev = find_current_rev + it 'raises exception' do + expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) + end - expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) + it 'does not create a new commit inside the repository' do + current_rev = find_current_rev - expect(current_rev).to eq find_current_rev - end - end + expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) - shared_examples 'failing update hook' do - before do - expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, '']) - expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([false, '']) - expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive') + expect(current_rev).to eq find_current_rev + end end - it 'raises exception' do - expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) - end + shared_examples 'failing update hook' do + before do + expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, '']) + expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([false, '']) + expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive') + end - it 'does not create a new commit inside the repository' do - current_rev = find_current_rev + it 'raises exception' do + expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) + end - expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) + it 'does not create a new commit inside the repository' do + current_rev = find_current_rev - expect(current_rev).to eq find_current_rev - end - end + expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError) - shared_examples 'failing post-receive hook' do - before do - expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, '']) - expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([true, '']) - expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('post-receive').and_return([false, '']) + expect(current_rev).to eq find_current_rev + end end - it 'does not raise exception' do - expect { subject.commit }.not_to raise_error - end + shared_examples 'failing post-receive hook' do + before do + expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, '']) + expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([true, '']) + expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('post-receive').and_return([false, '']) + end + + it 'does not raise exception' do + expect { subject.commit }.not_to raise_error + end - it 'creates the commit' do - current_rev = find_current_rev + it 'creates the commit' do + current_rev = find_current_rev - subject.commit + subject.commit - expect(current_rev).not_to eq find_current_rev + expect(current_rev).not_to eq find_current_rev + end end - end - shared_examples 'when hooks call succceeds' do - let(:hook) { double(:hook) } + shared_examples 'when hooks call succceeds' do + let(:hook) { double(:hook) } - it 'calls the three hooks' do - expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) - expect(hook).to receive(:trigger).exactly(3).times.and_return([true, nil]) + it 'calls the three hooks' do + expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) + expect(hook).to receive(:trigger).exactly(3).times.and_return([true, nil]) - subject.commit - end + subject.commit + end - it 'creates the commit' do - current_rev = find_current_rev + it 'creates the commit' do + current_rev = find_current_rev - subject.commit + subject.commit - expect(current_rev).not_to eq find_current_rev + expect(current_rev).not_to eq find_current_rev + end end - end - context 'when creating a page' do - before do - project_wiki.create_page('index', 'test content') + context 'when creating a page' do + before do + project_wiki.create_page('index', 'test content') + end + + it_behaves_like 'failing pre-receive hook' + it_behaves_like 'failing update hook' + it_behaves_like 'failing post-receive hook' + it_behaves_like 'when hooks call succceeds' end - it_behaves_like 'failing pre-receive hook' - it_behaves_like 'failing update hook' - it_behaves_like 'failing post-receive hook' - it_behaves_like 'when hooks call succceeds' - end + context 'when updating a page' do + before do + project_wiki.update_page(find_page('home'), content: 'some other content', format: :markdown) + end - context 'when updating a page' do - before do - project_wiki.update_page(find_page('home'), content: 'some other content', format: :markdown) + it_behaves_like 'failing pre-receive hook' + it_behaves_like 'failing update hook' + it_behaves_like 'failing post-receive hook' + it_behaves_like 'when hooks call succceeds' end - it_behaves_like 'failing pre-receive hook' - it_behaves_like 'failing update hook' - it_behaves_like 'failing post-receive hook' - it_behaves_like 'when hooks call succceeds' - end + context 'when deleting a page' do + before do + project_wiki.delete_page(find_page('home')) + end - context 'when deleting a page' do - before do - project_wiki.delete_page(find_page('home')) + it_behaves_like 'failing pre-receive hook' + it_behaves_like 'failing update hook' + it_behaves_like 'failing post-receive hook' + it_behaves_like 'when hooks call succceeds' end - it_behaves_like 'failing pre-receive hook' - it_behaves_like 'failing update hook' - it_behaves_like 'failing post-receive hook' - it_behaves_like 'when hooks call succceeds' - end + def find_current_rev + wiki.gollum_wiki.repo.commits.first&.sha + end - def find_current_rev - wiki.gollum_wiki.repo.commits.first&.sha + def find_page(name) + wiki.page(title: name) + end end - def find_page(name) - wiki.page(title: name) + context 'when Gitaly is enabled' do + it_behaves_like 'calling wiki hooks' end - end - - # TODO: Uncomment once Gitaly updates the ruby vendor code - # context 'when Gitaly is enabled' do - # it_behaves_like 'calling wiki hooks' - # end - context 'when Gitaly is disabled', :skip_gitaly_mock do - it_behaves_like 'calling wiki hooks' + context 'when Gitaly is disabled', :disable_gitaly do + it_behaves_like 'calling wiki hooks' + end end end diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb index 35b06b14620..b63658e1b3b 100644 --- a/spec/lib/gitlab/git/wiki_spec.rb +++ b/spec/lib/gitlab/git/wiki_spec.rb @@ -6,9 +6,7 @@ describe Gitlab::Git::Wiki do let(:project_wiki) { ProjectWiki.new(project, user) } subject { project_wiki.wiki } - # Remove skip_gitaly_mock flag when gitaly_find_page when - # https://gitlab.com/gitlab-org/gitlab-ce/issues/42039 is solved - describe '#page', :skip_gitaly_mock do + describe '#page' do before do create_page('page1', 'content') create_page('foo/page1', 'content foo/page1') @@ -25,7 +23,7 @@ describe Gitlab::Git::Wiki do end end - describe '#delete_page', :skip_gitaly_mock do + describe '#delete_page' do after do destroy_page('page1') end -- cgit v1.2.1 From 6954ebbe55688633c6e4b43c28088e195191e8fc Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Tue, 12 Jun 2018 11:35:34 +0200 Subject: Render calendar feed inline when accessed from GitLab With text/calendar as Content-Type, the browser always downloads the content as a file (even ignoring the Content-Disposition header). We want to display the content inline when accessed from GitLab, similarly to the RSS feed. --- spec/features/ics/dashboard_issues_spec.rb | 26 ++++++++++++++++++-------- spec/features/ics/group_issues_spec.rb | 26 ++++++++++++++++++-------- spec/features/ics/project_issues_spec.rb | 26 ++++++++++++++++++-------- 3 files changed, 54 insertions(+), 24 deletions(-) (limited to 'spec') diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb index 5d6cd44ad1c..90d02f7e40f 100644 --- a/spec/features/ics/dashboard_issues_spec.rb +++ b/spec/features/ics/dashboard_issues_spec.rb @@ -11,13 +11,25 @@ describe 'Dashboard Issues Calendar Feed' do end context 'when authenticated' do - it 'renders calendar feed' do - sign_in user - visit issues_dashboard_path(:ics) + context 'with no referer' do + it 'renders calendar feed' do + sign_in user + visit issues_dashboard_path(:ics) - expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') - expect(body).to have_text('BEGIN:VCALENDAR') + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'with GitLab as the referer' do + it 'renders calendar feed as text/plain' do + sign_in user + page.driver.header('Referer', issues_dashboard_url(host: Settings.gitlab.base_url)) + visit issues_dashboard_path(:ics) + + expect(response_headers['Content-Type']).to have_content('text/plain') + expect(body).to have_text('BEGIN:VCALENDAR') + end end end @@ -28,7 +40,6 @@ describe 'Dashboard Issues Calendar Feed' do visit issues_dashboard_path(:ics, private_token: personal_access_token.token) expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') expect(body).to have_text('BEGIN:VCALENDAR') end end @@ -38,7 +49,6 @@ describe 'Dashboard Issues Calendar Feed' do visit issues_dashboard_path(:ics, feed_token: user.feed_token) expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') expect(body).to have_text('BEGIN:VCALENDAR') end end diff --git a/spec/features/ics/group_issues_spec.rb b/spec/features/ics/group_issues_spec.rb index 0a049be2ffe..24de5b4b7c6 100644 --- a/spec/features/ics/group_issues_spec.rb +++ b/spec/features/ics/group_issues_spec.rb @@ -13,13 +13,25 @@ describe 'Group Issues Calendar Feed' do end context 'when authenticated' do - it 'renders calendar feed' do - sign_in user - visit issues_group_path(group, :ics) + context 'with no referer' do + it 'renders calendar feed' do + sign_in user + visit issues_group_path(group, :ics) - expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') - expect(body).to have_text('BEGIN:VCALENDAR') + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'with GitLab as the referer' do + it 'renders calendar feed as text/plain' do + sign_in user + page.driver.header('Referer', issues_group_url(group, host: Settings.gitlab.base_url)) + visit issues_group_path(group, :ics) + + expect(response_headers['Content-Type']).to have_content('text/plain') + expect(body).to have_text('BEGIN:VCALENDAR') + end end end @@ -30,7 +42,6 @@ describe 'Group Issues Calendar Feed' do visit issues_group_path(group, :ics, private_token: personal_access_token.token) expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') expect(body).to have_text('BEGIN:VCALENDAR') end end @@ -40,7 +51,6 @@ describe 'Group Issues Calendar Feed' do visit issues_group_path(group, :ics, feed_token: user.feed_token) expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') expect(body).to have_text('BEGIN:VCALENDAR') end end diff --git a/spec/features/ics/project_issues_spec.rb b/spec/features/ics/project_issues_spec.rb index b99e9607f1d..2ca3d52a5be 100644 --- a/spec/features/ics/project_issues_spec.rb +++ b/spec/features/ics/project_issues_spec.rb @@ -12,13 +12,25 @@ describe 'Project Issues Calendar Feed' do end context 'when authenticated' do - it 'renders calendar feed' do - sign_in user - visit project_issues_path(project, :ics) + context 'with no referer' do + it 'renders calendar feed' do + sign_in user + visit project_issues_path(project, :ics) - expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') - expect(body).to have_text('BEGIN:VCALENDAR') + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end + + context 'with GitLab as the referer' do + it 'renders calendar feed as text/plain' do + sign_in user + page.driver.header('Referer', project_issues_url(project, host: Settings.gitlab.base_url)) + visit project_issues_path(project, :ics) + + expect(response_headers['Content-Type']).to have_content('text/plain') + expect(body).to have_text('BEGIN:VCALENDAR') + end end end @@ -29,7 +41,6 @@ describe 'Project Issues Calendar Feed' do visit project_issues_path(project, :ics, private_token: personal_access_token.token) expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') expect(body).to have_text('BEGIN:VCALENDAR') end end @@ -39,7 +50,6 @@ describe 'Project Issues Calendar Feed' do visit project_issues_path(project, :ics, feed_token: user.feed_token) expect(response_headers['Content-Type']).to have_content('text/calendar') - expect(response_headers['Content-Disposition']).to have_content('inline') expect(body).to have_text('BEGIN:VCALENDAR') end end -- cgit v1.2.1 From e1a77fa757c65cf243e6b74bf83a394da923ef0f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 15 Jun 2018 14:05:26 +0200 Subject: Whitelist text-align property for th and td --- spec/lib/banzai/filter/sanitization_filter_spec.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index 17a620ef603..d930c608b18 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -93,6 +93,16 @@ describe Banzai::Filter::SanitizationFilter do expect(doc.at_css('td')['style']).to eq 'text-align: center' end + it 'disallows `text-align` property in `style` attribute on other elements' do + html = <<~HTML +
Text
+ HTML + + doc = filter(html) + + expect(doc.at_css('div')['style']).to be_nil + end + it 'allows `span` elements' do exp = act = %q{Hello} expect(filter(act).to_html).to eq exp @@ -224,7 +234,7 @@ describe Banzai::Filter::SanitizationFilter do 'protocol-based JS injection: spaces and entities' => { input: 'foo', - output: 'foo' + output: 'foo' }, 'protocol whitespace' => { -- cgit v1.2.1 From 1533dbf9018fc4ed91725b369a779b7d4c40f99f Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 18 Jun 2018 10:18:40 +0100 Subject: Uses long sha version of the merged commit in MR widget copy to clipboard button --- .../vue_mr_widget/components/states/mr_widget_merged_spec.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js index 3e2fd71b5b8..efa5c878678 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js @@ -39,7 +39,8 @@ describe('MRWidgetMerged', () => { readableClosedAt: '', }, updatedAt: 'mergedUpdatedAt', - shortMergeCommitSha: 'asdf1234', + shortMergeCommitSha: '958c0475', + mergeCommitSha: '958c047516e182dfc52317f721f696e8a1ee85ed', mergeCommitPath: 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d', sourceBranch: 'bar', targetBranch, @@ -153,7 +154,7 @@ describe('MRWidgetMerged', () => { it('shows button to copy commit SHA to clipboard', () => { expect(selectors.copyMergeShaButton).toExist(); - expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.shortMergeCommitSha); + expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.mergeCommitSha); }); it('shows merge commit SHA link', () => { -- cgit v1.2.1 From 07e95b0cd48192804324a3c47729b5641df54fe6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 18 Jun 2018 12:02:56 +0200 Subject: Expose whether current user can push into a branch on branches API --- spec/fixtures/api/schemas/public_api/v4/branch.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/fixtures/api/schemas/public_api/v4/branch.json b/spec/fixtures/api/schemas/public_api/v4/branch.json index a3581178974..a8891680d06 100644 --- a/spec/fixtures/api/schemas/public_api/v4/branch.json +++ b/spec/fixtures/api/schemas/public_api/v4/branch.json @@ -14,7 +14,8 @@ "merged": { "type": "boolean" }, "protected": { "type": "boolean" }, "developers_can_push": { "type": "boolean" }, - "developers_can_merge": { "type": "boolean" } + "developers_can_merge": { "type": "boolean" }, + "can_push": { "type": "boolean" } }, "additionalProperties": false } -- cgit v1.2.1 From b762fc4b460bed9f0510f19e20130fc331fa1efe Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 18 Jun 2018 13:28:21 +0200 Subject: Allow trailing whitespace on blockquote fence lines --- spec/lib/banzai/filter/blockquote_fence_filter_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb index 8224dc5a6b9..b645e49bd43 100644 --- a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb +++ b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb @@ -11,4 +11,8 @@ describe Banzai::Filter::BlockquoteFenceFilter do expect(output).to eq(expected) end + + it 'allows trailing whitespace on blockquote fence lines' do + expect(filter(">>> \ntest\n>>> ")).to eq("> test") + end end -- cgit v1.2.1 From f5e4adc9bf1ae7a617d4e0090bad676e00a4041f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 18 Jun 2018 15:14:20 +0200 Subject: add spec replicating both label and milestone duplicated title issues --- spec/lib/gitlab/import_export/project.light.json | 6 ++-- .../import_export/project_tree_restorer_spec.rb | 37 ++++++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json index c13cf4a0507..a401ce4c0c7 100644 --- a/spec/lib/gitlab/import_export/project.light.json +++ b/spec/lib/gitlab/import_export/project.light.json @@ -7,7 +7,7 @@ "milestones": [ { "id": 1, - "title": "Project milestone", + "title": "A milestone", "project_id": 8, "description": "Project-level milestone", "due_date": null, @@ -66,7 +66,7 @@ "group_milestone_id": null, "milestone": { "id": 1, - "title": "Project milestone", + "title": "A milestone", "project_id": 8, "description": "Project-level milestone", "due_date": null, @@ -86,7 +86,7 @@ "updated_at": "2017-08-15T18:37:40.795Z", "label": { "id": 6, - "title": "Another project label", + "title": "Another label", "color": "#A8D695", "project_id": null, "created_at": "2017-08-15T18:37:19.698Z", diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 68ddc947e02..613e4cb78d0 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -189,8 +189,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do @project.pipelines.zip([2, 2, 2, 2, 2]) .each do |(pipeline, expected_status_size)| - expect(pipeline.statuses.size).to eq(expected_status_size) - end + expect(pipeline.statuses.size).to eq(expected_status_size) + end end end @@ -368,5 +368,38 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do milestones: 1, first_issue_labels: 1 end + + context 'with existing group models' do + let!(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + before do + project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json") + end + + it 'imports labels' do + create(:group_label, name: 'Another label', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) + + restored_project_json + + expect(project.labels.count).to eq(1) + end + + it 'imports milestones' do + create(:milestone, name: 'A milestone', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) + + restored_project_json + + expect(project.milestones.count).to eq(1) + end + end end end -- cgit v1.2.1 From ac66547370c94ad07a55159a490e5e9e4706adbc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 18 Jun 2018 15:17:03 +0200 Subject: add spec replicating both label and milestone duplicated title issues --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 613e4cb78d0..71f84234e4e 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -385,6 +385,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it 'imports labels' do create(:group_label, name: 'Another label', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json @@ -394,6 +395,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it 'imports milestones' do create(:milestone, name: 'A milestone', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json -- cgit v1.2.1 From 96f8ad0683619bb852931f4005f0b83c8337e8e6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 18 Jun 2018 17:47:20 +0200 Subject: Properly detect label reference if followed by period or question mark --- spec/lib/banzai/filter/label_reference_filter_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index b30f3661e70..f34d3dc935b 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -149,8 +149,8 @@ describe Banzai::Filter::LabelReferenceFilter do end it 'links with adjacent text' do - doc = reference_filter("Label (#{reference}).") - expect(doc.to_html).to match(%r(\(\?g\.fm&\)\.)) + doc = reference_filter("Label #{reference}.") + expect(doc.to_html).to match(%r(\?g\.fm&\.)) end it 'ignores invalid label names' do -- cgit v1.2.1 From 404b49a74d790d7fd58a9540cd4b98a5d3b31854 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 18 Jun 2018 12:15:58 -0500 Subject: Bring CE-EE parity to migrations/remove_soft_removed_objects_spec.rb --- .../migrations/remove_soft_removed_objects_spec.rb | 36 +++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/migrations/remove_soft_removed_objects_spec.rb b/spec/migrations/remove_soft_removed_objects_spec.rb index fb70c284f5e..d0bde98b80e 100644 --- a/spec/migrations/remove_soft_removed_objects_spec.rb +++ b/spec/migrations/remove_soft_removed_objects_spec.rb @@ -3,6 +3,18 @@ require Rails.root.join('db', 'post_migrate', '20171207150343_remove_soft_remove describe RemoveSoftRemovedObjects, :migration do describe '#up' do + let!(:groups) do + table(:namespaces).tap do |t| + t.inheritance_column = nil + end + end + + let!(:routes) do + table(:routes).tap do |t| + t.inheritance_column = nil + end + end + it 'removes various soft removed objects' do 5.times do create_with_deleted_at(:issue) @@ -28,19 +40,20 @@ describe RemoveSoftRemovedObjects, :migration do it 'removes routes of soft removed personal namespaces' do namespace = create_with_deleted_at(:namespace) - group = create(:group) # rubocop:disable RSpec/FactoriesInMigrationSpecs + group = groups.create!(name: 'group', path: 'group_path', type: 'Group') + routes.create!(source_id: group.id, source_type: 'Group', name: 'group', path: 'group_path') - expect(Route.where(source: namespace).exists?).to eq(true) - expect(Route.where(source: group).exists?).to eq(true) + expect(routes.where(source_id: namespace.id).exists?).to eq(true) + expect(routes.where(source_id: group.id).exists?).to eq(true) run_migration - expect(Route.where(source: namespace).exists?).to eq(false) - expect(Route.where(source: group).exists?).to eq(true) + expect(routes.where(source_id: namespace.id).exists?).to eq(false) + expect(routes.where(source_id: group.id).exists?).to eq(true) end it 'schedules the removal of soft removed groups' do - group = create_with_deleted_at(:group) + group = create_deleted_group admin = create(:user, admin: true) # rubocop:disable RSpec/FactoriesInMigrationSpecs expect_any_instance_of(GroupDestroyWorker) @@ -51,7 +64,7 @@ describe RemoveSoftRemovedObjects, :migration do end it 'does not remove soft removed groups when no admin user could be found' do - create_with_deleted_at(:group) + create_deleted_group expect_any_instance_of(GroupDestroyWorker) .not_to receive(:perform) @@ -74,4 +87,13 @@ describe RemoveSoftRemovedObjects, :migration do row end + + def create_deleted_group + group = groups.create!(name: 'group', path: 'group_path', type: 'Group') + routes.create!(source_id: group.id, source_type: 'Group', name: 'group', path: 'group_path') + + groups.where(id: group.id).update_all(deleted_at: 1.year.ago) + + group + end end -- cgit v1.2.1 From 24865a4e17917fcf79d83508d18f683c5623ded8 Mon Sep 17 00:00:00 2001 From: "Marko, Peter" Date: Fri, 15 Jun 2018 12:16:21 +0200 Subject: Add id as second sort parameter for group sort by name Adding primary key to database query order rule generates deterministic sort result and thus pagination. This is needed because subgroups can have identical names. Signed-off-by: Marko, Peter --- spec/requests/api/groups_spec.rb | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index f5218ebfac2..da23fdd7dca 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -138,12 +138,15 @@ describe API::Groups do context "when using sorting" do let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") } - let(:group4) { create(:group, name: "z#{group1.name}", path: "y#{group1.path}") } + let(:group4) { create(:group, name: "same-name", path: "y#{group1.path}") } + let(:group5) { create(:group, name: "same-name") } let(:response_groups) { json_response.map { |group| group['name'] } } + let(:response_groups_ids) { json_response.map { |group| group['id'] } } before do group3.add_owner(user1) group4.add_owner(user1) + group5.add_owner(user1) end it "sorts by name ascending by default" do @@ -181,6 +184,33 @@ describe API::Groups do expect(json_response).to be_an Array expect(response_groups).to eq(Group.visible_to_user(user1).order(:id).pluck(:name)) end + + it "sorts also by descending id with pagination fix" do + get api("/groups", user1), order_by: "id", sort: "desc" + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(response_groups).to eq(Group.visible_to_user(user1).order(id: :desc).pluck(:name)) + end + + it "sorts identical keys by id for good pagination" do + get api("/groups", user1), search: "same-name", order_by: "name" + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort) + end + + it "sorts descending identical keys by id for good pagination" do + get api("/groups", user1), search: "same-name", order_by: "name", sort: "desc" + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(response_groups_ids).to eq(Group.select { |group| group['name'] == 'same-name' }.map { |group| group['id'] }.sort) + end end context 'when using owned in the request' do -- cgit v1.2.1 From 5679beb2285e4eade892324b24ded1754d277db6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 18 Jun 2018 14:21:37 -0500 Subject: CE-EE parity for spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index c5a4d9b4778..17419b05278 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -174,7 +174,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do end let(:pipeline) do - build(:ci_pipeline, ref: 'master', config: config) + build(:ci_pipeline, ref: 'master', project: project, config: config) end it_behaves_like 'a correct pipeline' -- cgit v1.2.1 From 9c48089190b8fafe0227f354a307e4ad01938437 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 18 Jun 2018 14:22:38 -0500 Subject: CE-EE parity to spec/lib/gitlab/ci/yaml_processor_spec.rb --- spec/lib/gitlab/ci/yaml_processor_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index ecb16daec96..fa5327c26f0 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' module Gitlab module Ci - describe YamlProcessor, :lib do + describe YamlProcessor do subject { described_class.new(config) } describe 'our current .gitlab-ci.yml' do -- cgit v1.2.1 From 557f4e6a437344d86409ea6b15f3454bc2c5b341 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 18 Jun 2018 14:25:28 -0500 Subject: CE-EE parity to spec/lib/gitlab/ci/config_spec.rb --- spec/lib/gitlab/ci/config_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index bc5a5e43103..2e204da307d 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -49,7 +49,7 @@ describe Gitlab::Ci::Config do describe '.new' do it 'raises error' do expect { config }.to raise_error( - Gitlab::Ci::Config::Loader::FormatError, + ::Gitlab::Ci::Config::Loader::FormatError, /Invalid configuration format/ ) end -- cgit v1.2.1 From 6a684432eebcc8bbf622e9e740381997f02d2a7e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 18 Jun 2018 15:22:04 -0500 Subject: Correct CE-EE parity in two CI-related specs --- spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb | 2 +- spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 17419b05278..284aed91e29 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Populate do - set(:project) { create(:project) } + set(:project) { create(:project, :repository) } set(:user) { create(:user) } let(:pipeline) do diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb index c53294d091c..a8dc5356413 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Ci::Pipeline::Chain::Validate::Config do - set(:project) { create(:project) } + set(:project) { create(:project, :repository) } set(:user) { create(:user) } let(:command) do -- cgit v1.2.1 From 4f9068dfc06269ca7e2247fb0be2796452546de1 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 18 Jun 2018 16:38:34 -0700 Subject: Eliminate N+1 queries in LFS file locks checks during a push This significantly improves performance when a user pushes many references. project.path_locks.any? doesn't cache the output and runs `SELECT 1 AS one FROM "path_locks" WHERE project_id = N` each time. When there are thousands of refs being pushed, this can time out the unicorn worker. CE port for https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6159. --- spec/lib/gitlab/git_access_spec.rb | 16 ++++++++++++++++ spec/models/project_spec.rb | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 0d5f6a0b576..ff32025253a 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -934,6 +934,22 @@ describe Gitlab::GitAccess do expect(project.repository).to receive(:clean_stale_repository_files).and_call_original expect { push_access_check }.not_to raise_error end + + it 'avoids N+1 queries', :request_store do + # Run this once to establish a baseline. Cached queries should get + # cached, so that when we introduce another change we shouldn't see + # additional queries. + access.check('git-receive-pack', changes) + + control_count = ActiveRecord::QueryRecorder.new do + access.check('git-receive-pack', changes) + end + + changes = ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature'] + + # There is still an N+1 query with protected branches + expect { access.check('git-receive-pack', changes) }.not_to exceed_query_limit(control_count).with_threshold(1) + end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index bc9cce6b0c3..a2f8fac2f38 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2339,6 +2339,22 @@ describe Project do end end + describe '#any_lfs_file_locks?', :request_store do + set(:project) { create(:project) } + + it 'returns false when there are no LFS file locks' do + expect(project.any_lfs_file_locks?).to be_falsey + end + + it 'returns a cached true when there are LFS file locks' do + create(:lfs_file_lock, project: project) + + expect(project.lfs_file_locks).to receive(:any?).once.and_call_original + + 2.times { expect(project.any_lfs_file_locks?).to be_truthy } + end + end + describe '#protected_for?' do let(:project) { create(:project) } -- cgit v1.2.1 From c46ff7de67d31bfc8875abf58d0fd096b6d725ae Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Mon, 18 Jun 2018 19:00:25 -0500 Subject: Fix flakey time-sensitive specs that rely on notes background Sidekiq jobs Fix https://gitlab.com/gitlab-org/gitlab-ce/issues/48093 We use `perform_enqueued_jobs` because it is "adapter-agnostic so it's better to use it over Sidekiq::Testing.inline!." -- https://gitlab.com/gitlab-org/gitlab-ce/issues/22991 --- spec/support/helpers/features/notes_helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/support/helpers/features/notes_helpers.rb b/spec/support/helpers/features/notes_helpers.rb index 1a1d5853a7a..2b9f8b30c60 100644 --- a/spec/support/helpers/features/notes_helpers.rb +++ b/spec/support/helpers/features/notes_helpers.rb @@ -13,7 +13,7 @@ module Spec module Features module NotesHelpers def add_note(text) - Sidekiq::Testing.fake! do + perform_enqueued_jobs do page.within(".js-main-target-form") do fill_in("note[note]", with: text) find(".js-comment-submit-button").click -- cgit v1.2.1 From 9ae8b57467ac8b38f1fa9020a466d94a93cbb9dd Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 14 Jun 2018 13:21:59 +0200 Subject: Move language detection to mandatory Closes https://gitlab.com/gitlab-org/gitaly/issues/732 Closes https://gitlab.com/gitlab-org/gitaly/issues/360 --- spec/lib/gitlab/git/repository_spec.rb | 46 ++++++++++++++-------------------- 1 file changed, 19 insertions(+), 27 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 5bae99101e6..10a264a5030 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1688,41 +1688,33 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#languages' do - shared_examples 'languages' do - it 'returns exactly the expected results' do - languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6') - expected_languages = [ - { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" }, - { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" }, - { value: 7.9, label: "HTML", color: "#e34c26", highlight: "#e34c26" }, - { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" } - ] + it 'returns exactly the expected results' do + languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6') + expected_languages = [ + { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" }, + { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" }, + { value: 7.9, label: "HTML", color: "#e34c26", highlight: "#e34c26" }, + { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" } + ] - expect(languages.size).to eq(expected_languages.size) + expect(languages.size).to eq(expected_languages.size) - expected_languages.size.times do |i| - a = expected_languages[i] - b = languages[i] + expected_languages.size.times do |i| + a = expected_languages[i] + b = languages[i] - expect(a.keys.sort).to eq(b.keys.sort) - expect(a[:value]).to be_within(0.1).of(b[:value]) + expect(a.keys.sort).to eq(b.keys.sort) + expect(a[:value]).to be_within(0.1).of(b[:value]) - non_float_keys = a.keys - [:value] - expect(a.values_at(*non_float_keys)).to eq(b.values_at(*non_float_keys)) - end - end - - it "uses the repository's HEAD when no ref is passed" do - lang = repository.languages.first - - expect(lang[:label]).to eq('Ruby') + non_float_keys = a.keys - [:value] + expect(a.values_at(*non_float_keys)).to eq(b.values_at(*non_float_keys)) end end - it_behaves_like 'languages' + it "uses the repository's HEAD when no ref is passed" do + lang = repository.languages.first - context 'with rugged', :skip_gitaly_mock do - it_behaves_like 'languages' + expect(lang[:label]).to eq('Ruby') end end -- cgit v1.2.1 From da8419a1ba1cbcc6c5c6365c99419cf17ab2d497 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 14 Jun 2018 13:24:17 +0200 Subject: License detection happens soley through Gitaly now Migration: https://gitlab.com/gitlab-org/gitaly/issues/1026 Conflicts: lib/gitlab/git/repository.rb --- spec/lib/gitlab/git/repository_spec.rb | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 5bae99101e6..f0e7e390f8b 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1727,31 +1727,21 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#license_short_name' do - shared_examples 'acquiring the Licensee license key' do - subject { repository.license_short_name } + subject { repository.license_short_name } - context 'when no license file can be found' do - let(:project) { create(:project, :repository) } - let(:repository) { project.repository.raw_repository } - - before do - project.repository.delete_file(project.owner, 'LICENSE', message: 'remove license', branch_name: 'master') - end - - it { is_expected.to be_nil } - end + context 'when no license file can be found' do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository.raw_repository } - context 'when an mit license is found' do - it { is_expected.to eq('mit') } + before do + project.repository.delete_file(project.owner, 'LICENSE', message: 'remove license', branch_name: 'master') end - end - context 'when gitaly is enabled' do - it_behaves_like 'acquiring the Licensee license key' + it { is_expected.to be_nil } end - context 'when gitaly is disabled', :disable_gitaly do - it_behaves_like 'acquiring the Licensee license key' + context 'when an mit license is found' do + it { is_expected.to eq('mit') } end end -- cgit v1.2.1 From b99bc6d38088b7b87f6ffa4a706ab5eacb489449 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 19 Jun 2018 09:54:47 +0200 Subject: add group finder spec and logic --- .../import_export/group_project_finder_spec.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 spec/lib/gitlab/import_export/group_project_finder_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb new file mode 100644 index 00000000000..17239c150ab --- /dev/null +++ b/spec/lib/gitlab/import_export/group_project_finder_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::GroupProjectFinder do + let(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + it 'finds the right group label' do + group_label = create(:group_label, 'name': 'group label', 'group': project.group) + + expect(described_class.find(Label, + title: 'group label', + 'project_id': project.id, + 'group_id': project.group.id)).to eq([group_label]) + end +end -- cgit v1.2.1 From c24e39ab88af8fa3087efa0c7c6335e2941d8641 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 14 Jun 2018 13:01:55 +0200 Subject: Counting commits is done by Gitaly Closes https://gitlab.com/gitlab-org/gitaly/issues/382 --- spec/lib/gitlab/git/repository_spec.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 5bae99101e6..b9eb002df3b 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1114,7 +1114,7 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#count_commits' do - shared_examples 'extended commit counting' do + describe 'extended commit counting' do context 'with after timestamp' do it 'returns the number of commits after timestamp' do options = { ref: 'master', after: Time.iso8601('2013-03-03T20:15:01+00:00') } @@ -1199,14 +1199,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end end - - context 'when Gitaly count_commits feature is enabled' do - it_behaves_like 'extended commit counting' - end - - context 'when Gitaly count_commits feature is disabled', :disable_gitaly do - it_behaves_like 'extended commit counting' - end end describe '#autocrlf' do -- cgit v1.2.1 From fe56d29d300ccbb26328886166e07c84fc247155 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 19 Jun 2018 11:25:58 +0200 Subject: update spec --- .../import_export/group_project_finder_spec.rb | 61 +++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb index 17239c150ab..a86f0c2dd44 100644 --- a/spec/lib/gitlab/import_export/group_project_finder_spec.rb +++ b/spec/lib/gitlab/import_export/group_project_finder_spec.rb @@ -10,12 +10,61 @@ describe Gitlab::ImportExport::GroupProjectFinder do group: create(:group)) end - it 'finds the right group label' do - group_label = create(:group_label, 'name': 'group label', 'group': project.group) + context 'labels' do + it 'finds the right group label' do + group_label = create(:group_label, 'name': 'group label', 'group': project.group) - expect(described_class.find(Label, - title: 'group label', - 'project_id': project.id, - 'group_id': project.group.id)).to eq([group_label]) + expect(described_class.find_or_new(Label, + title: 'group label', + 'project_id' => project.id, + 'group_id' => project.group.id)).to eq(group_label) + end + + it 'initializes a new label' do + label = described_class.find_or_new(Label, + title: 'group label', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(label.persisted?).to be false + end + + it 'creates a new label' do + label = described_class.find_or_create(Label, + title: 'group label', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(label.persisted?).to be true + end + end + + context 'milestones' do + it 'finds the right group milestone' do + milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group) + + expect(described_class.find_or_new(Milestone, + title: 'group milestone', + 'project_id' => project.id, + 'group_id' => project.group.id)).to eq(milestone) + end + + it 'initializes a new milestone' do + milestone = described_class.find_or_new(Milestone, + title: 'group milestone', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(milestone.persisted?).to be false + end + + it 'creates a new milestone' do + milestone = described_class.find_or_create(Milestone, + title: 'group milestone', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(milestone.persisted?).to be true + end end end -- cgit v1.2.1 From 6207a2de89c68eaa8d284116a6e790feb55ee65e Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Mon, 18 Jun 2018 15:29:27 +0200 Subject: HTML escape branch name in project graphs page --- spec/features/projects/graph_spec.rb | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb index 57172610aed..335174b7729 100644 --- a/spec/features/projects/graph_spec.rb +++ b/spec/features/projects/graph_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe 'Project Graph', :js do let(:user) { create :user } let(:project) { create(:project, :repository, namespace: user.namespace) } + let(:branch_name) { 'master' } before do project.add_master(user) @@ -12,7 +13,7 @@ describe 'Project Graph', :js do shared_examples 'page should have commits graphs' do it 'renders commits' do - expect(page).to have_content('Commit statistics for master') + expect(page).to have_content("Commit statistics for #{branch_name}") expect(page).to have_content('Commits per day of month') end end @@ -57,6 +58,23 @@ describe 'Project Graph', :js do it_behaves_like 'page should have languages graphs' end + context 'chart graph with HTML escaped branch name' do + let(:branch_name) { '

evil

' } + + before do + project.repository.create_branch(branch_name, 'master') + + visit charts_project_graph_path(project, branch_name) + end + + it_behaves_like 'page should have commits graphs' + + it 'HTML escapes branch name' do + expect(page.body).to include("Commit statistics for #{ERB::Util.html_escape(branch_name)}") + expect(page.body).not_to include(branch_name) + end + end + context 'when CI enabled' do before do project.enable_ci -- cgit v1.2.1 From 1dc0332726ffbcb488439e84373958753ee4ac5e Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 19 Jun 2018 12:17:54 +0200 Subject: Move forking to Gitaly Closes https://gitlab.com/gitlab-org/gitaly/issues/817 Closes https://gitlab.com/gitlab-org/gitaly/issues/1236 --- spec/lib/gitlab/shell_spec.rb | 34 ++++++++--------------------- spec/workers/repository_fork_worker_spec.rb | 10 --------- 2 files changed, 9 insertions(+), 35 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 155e1663298..c435f988cdd 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -498,34 +498,18 @@ describe Gitlab::Shell do ) end - context 'with gitaly' do - it 'returns true when the command succeeds' do - expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository) - .with(repository.raw_repository) { :gitaly_response_object } - - is_expected.to be_truthy - end - - it 'return false when the command fails' do - expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository) - .with(repository.raw_repository) { raise GRPC::BadStatus, 'bla' } + it 'returns true when the command succeeds' do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository) + .with(repository.raw_repository) { :gitaly_response_object } - is_expected.to be_falsy - end + is_expected.to be_truthy end - context 'without gitaly', :disable_gitaly do - it 'returns true when the command succeeds' do - expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { true } - - is_expected.to be_truthy - end - - it 'return false when the command fails' do - expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { false } + it 'return false when the command fails' do + expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository) + .with(repository.raw_repository) { raise GRPC::BadStatus, 'bla' } - is_expected.to be_falsy - end + is_expected.to be_falsy end end @@ -665,7 +649,7 @@ describe Gitlab::Shell do subject do gitlab_shell.fetch_remote(repository.raw_repository, remote_name, - forced: true, no_tags: true, ssh_auth: ssh_auth) + forced: true, no_tags: true, ssh_auth: ssh_auth) end it 'passes the correct params to the gitaly service' do diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index 5d83397e8df..ac8716ecfb1 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -92,16 +92,6 @@ describe RepositoryForkWorker do end it_behaves_like 'RepositoryForkWorker performing' - - it 'logs a message about forking with old-style arguments' do - allow(subject).to receive(:gitlab_shell).and_return(shell) - expect(shell).to receive(:fork_repository) { true } - - allow(Rails.logger).to receive(:info).with(anything) # To compensate for other logs - expect(Rails.logger).to receive(:info).with("Project #{fork_project.id} is being forked using old-style arguments.") - - perform! - end end end end -- cgit v1.2.1 From 3522623f0d13a1902b53dec208864941adab6a5c Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 13:22:56 +0100 Subject: Removed irrelevant test for edit milestone in milestone list --- spec/features/groups/milestone_spec.rb | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'spec') diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 20337f1d3b0..2108d763028 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -107,19 +107,6 @@ feature 'Group milestones' do expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1) end - it 'updates milestone' do - page.within(".milestones #milestone_#{active_group_milestone.id}") do - click_link('Edit') - end - - page.within('.milestone-form') do - fill_in 'milestone_title', with: 'new title' - click_button('Update milestone') - end - - expect(find('#content-body h2')).to have_content('new title') - end - it 'shows milestone detail and supports its edit' do page.within(".milestones #milestone_#{active_group_milestone.id}") do click_link(active_group_milestone.title) -- cgit v1.2.1 From 8849159986f8565a23ae1512831c50ae3e76ae9a Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 13:40:04 +0100 Subject: Updated test for user deleting a milestone --- spec/features/milestones/user_deletes_milestone_spec.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'spec') diff --git a/spec/features/milestones/user_deletes_milestone_spec.rb b/spec/features/milestones/user_deletes_milestone_spec.rb index 414702daba4..9d4a68239d3 100644 --- a/spec/features/milestones/user_deletes_milestone_spec.rb +++ b/spec/features/milestones/user_deletes_milestone_spec.rb @@ -13,6 +13,7 @@ describe "User deletes milestone", :js do end it "deletes milestone" do + click_link(milestone.title) click_button("Delete") click_button("Delete milestone") -- cgit v1.2.1 From c2cdefd4413e2b9946d2a50461b338fc60cef1af Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 19 Jun 2018 15:01:36 +0200 Subject: Force push is handled by Gitaly now Closes gitlab-org/gitaly#348 --- spec/lib/gitlab/checks/force_push_spec.rb | 18 +++++++----------- spec/lib/gitlab/git/rev_list_spec.rb | 10 ---------- 2 files changed, 7 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb index a65012d2314..0e0788ce974 100644 --- a/spec/lib/gitlab/checks/force_push_spec.rb +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -1,21 +1,17 @@ require 'spec_helper' describe Gitlab::Checks::ForcePush do - let(:project) { create(:project, :repository) } - let(:repository) { project.repository.raw } + set(:project) { create(:project, :repository) } - context "exit code checking", :skip_gitaly_mock do - it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do - allow(repository).to receive(:popen).and_return(['normal output', 0]) + describe '.force_push?' do + it 'returns false if the repo is empty' do + allow(project).to receive(:empty_repo?).and_return(true) - expect { described_class.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error + expect(described_class.force_push?(project, 'HEAD', 'HEAD~')).to be(false) end - it "raises a GitError error if the `popen` call to git returns a non-zero exit code" do - allow(repository).to receive(:popen).and_return(['error', 1]) - - expect { described_class.force_push?(project, 'oldrev', 'newrev') } - .to raise_error(Gitlab::Git::Repository::GitError) + it 'checks if old rev is an anchestor' do + expect(described_class.force_push?(project, 'HEAD', 'HEAD~')).to be(true) end end end diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 95dc47e2a00..b752c3e8341 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -93,14 +93,4 @@ describe Gitlab::Git::RevList do expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2]) end end - - context "#missed_ref" do - let(:rev_list) { described_class.new(repository, oldrev: 'oldrev', newrev: 'newrev') } - - it 'calls out to `popen`' do - stub_popen_rev_list('--max-count=1', 'oldrev', '^newrev', with_lazy_block: false, output: "sha1\nsha2") - - expect(rev_list.missed_ref).to eq(%w[sha1 sha2]) - end - end end -- cgit v1.2.1 From 64cda7746c08939306a6b98751d5a5c5280cc5b9 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 19 Jun 2018 15:57:27 +0200 Subject: LFS changes are detected by Gitaly Closes https://gitlab.com/gitlab-org/gitaly/issues/935 --- spec/lib/gitlab/git/lfs_changes_spec.rb | 41 ++++----------------------------- 1 file changed, 5 insertions(+), 36 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/lfs_changes_spec.rb b/spec/lib/gitlab/git/lfs_changes_spec.rb index d0dd8c6303f..c5e7ab959b2 100644 --- a/spec/lib/gitlab/git/lfs_changes_spec.rb +++ b/spec/lib/gitlab/git/lfs_changes_spec.rb @@ -1,50 +1,19 @@ require 'spec_helper' describe Gitlab::Git::LfsChanges do - let(:project) { create(:project, :repository) } + set(:project) { create(:project, :repository) } let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' } let(:blob_object_id) { '0c304a93cb8430108629bbbcaa27db3343299bc0' } subject { described_class.new(project.repository, newrev) } describe '#new_pointers' do - shared_examples 'new pointers' do - it 'filters new objects to find lfs pointers' do - expect(subject.new_pointers(not_in: []).first.id).to eq(blob_object_id) - end - - it 'limits new_objects using object_limit' do - expect(subject.new_pointers(object_limit: 1)).to eq([]) - end - end - - context 'with gitaly enabled' do - it_behaves_like 'new pointers' + it 'filters new objects to find lfs pointers' do + expect(subject.new_pointers(not_in: []).first.id).to eq(blob_object_id) end - context 'with gitaly disabled', :skip_gitaly_mock do - it_behaves_like 'new pointers' - - it 'uses rev-list to find new objects' do - rev_list = double - allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list) - - expect(rev_list).to receive(:new_objects).and_return([]) - - subject.new_pointers - end - end - end - - describe '#all_pointers', :skip_gitaly_mock do - it 'uses rev-list to find all objects' do - rev_list = double - allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list) - allow(rev_list).to receive(:all_objects).and_yield([blob_object_id]) - - expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [blob_object_id]) - - subject.all_pointers + it 'limits new_objects using object_limit' do + expect(subject.new_pointers(object_limit: 1)).to eq([]) end end end -- cgit v1.2.1 From 825c68e2ededed6f280ebd82c713cd932444d8ac Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 19 Jun 2018 17:08:02 +0200 Subject: fix some edge cases --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 71f84234e4e..a706ab0eb6f 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -400,7 +400,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do restored_project_json - expect(project.milestones.count).to eq(1) + expect(project.group.milestones.count).to eq(1) + expect(project.milestones.count).to eq(0) end end end -- cgit v1.2.1 From 7357209f91ae4c0b504f47e36220bd04a0e2feca Mon Sep 17 00:00:00 2001 From: Mario de la Ossa Date: Wed, 6 Jun 2018 18:14:10 -0600 Subject: Implement filtering by filename on code search --- spec/lib/gitlab/file_finder_spec.rb | 20 +++++++++++++++++- spec/lib/gitlab/search/query_spec.rb | 39 ++++++++++++++++++++++++++++++++++++ spec/requests/api/search_spec.rb | 24 ++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 spec/lib/gitlab/search/query_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/file_finder_spec.rb b/spec/lib/gitlab/file_finder_spec.rb index d6d9e4001a3..b49c5817131 100644 --- a/spec/lib/gitlab/file_finder_spec.rb +++ b/spec/lib/gitlab/file_finder_spec.rb @@ -3,11 +3,29 @@ require 'spec_helper' describe Gitlab::FileFinder do describe '#find' do let(:project) { create(:project, :public, :repository) } + subject { described_class.new(project, project.default_branch) } it_behaves_like 'file finder' do - subject { described_class.new(project, project.default_branch) } let(:expected_file_by_name) { 'files/images/wm.svg' } let(:expected_file_by_content) { 'CHANGELOG' } end + + it 'filters by name' do + results = subject.find('files filename:wm.svg') + + expect(results.count).to eq(1) + end + + it 'filters by path' do + results = subject.find('white path:images') + + expect(results.count).to eq(1) + end + + it 'filters by extension' do + results = subject.find('files extension:svg') + + expect(results.count).to eq(1) + end end end diff --git a/spec/lib/gitlab/search/query_spec.rb b/spec/lib/gitlab/search/query_spec.rb new file mode 100644 index 00000000000..2d00428fffa --- /dev/null +++ b/spec/lib/gitlab/search/query_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Gitlab::Search::Query do + let(:query) { 'base filter:wow anotherfilter:noway name:maybe other:mmm leftover' } + let(:subject) do + described_class.new(query) do + filter :filter + filter :name, parser: :upcase.to_proc + filter :other + end + end + + it { expect(described_class).to be < SimpleDelegator } + + it 'leaves undefined filters in the main query' do + expect(subject.term).to eq('base anotherfilter:noway leftover') + end + + it 'parses filters' do + expect(subject.filters.count).to eq(3) + expect(subject.filters.map { |f| f[:value] }).to match_array(%w[wow MAYBE mmm]) + end + + context 'with an empty filter' do + let(:query) { 'some bar name: baz' } + + it 'ignores empty filters' do + expect(subject.term).to eq('some bar name: baz') + end + end + + context 'with a pipe' do + let(:query) { 'base | nofilter' } + + it 'does not escape the pipe' do + expect(subject.term).to eq(query) + end + end +end diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb index aca4aa40027..f8e468be170 100644 --- a/spec/requests/api/search_spec.rb +++ b/spec/requests/api/search_spec.rb @@ -312,6 +312,30 @@ describe API::Search do end it_behaves_like 'response is correct', schema: 'public_api/v4/blobs', size: 2 + + context 'filters' do + it 'by filename' do + get api("/projects/#{repo_project.id}/search", user), scope: 'blobs', search: 'mon filename:PROCESS.md' + + expect(response).to have_gitlab_http_status(200) + expect(json_response.size).to eq(2) + expect(json_response.first['filename']).to eq('PROCESS.md') + end + + it 'by path' do + get api("/projects/#{repo_project.id}/search", user), scope: 'blobs', search: 'mon path:markdown' + + expect(response).to have_gitlab_http_status(200) + expect(json_response.size).to eq(8) + end + + it 'by extension' do + get api("/projects/#{repo_project.id}/search", user), scope: 'blobs', search: 'mon extension:md' + + expect(response).to have_gitlab_http_status(200) + expect(json_response.size).to eq(11) + end + end end end end -- cgit v1.2.1 From eb086a4bfd2495168483a2652571f247e6b768a5 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Tue, 19 Jun 2018 17:18:15 +0100 Subject: Disallow methods that copy data on large tables {change_column_type,rename_column}_concurrently both copy data from one column to another during a migration, which should not be done on GitLab.com. Instead, we should use background migrations. --- .../rubocop/cop/migration/update_large_table_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'spec') diff --git a/spec/rubocop/cop/migration/update_large_table_spec.rb b/spec/rubocop/cop/migration/update_large_table_spec.rb index ef724fc8bad..5e08eb4f772 100644 --- a/spec/rubocop/cop/migration/update_large_table_spec.rb +++ b/spec/rubocop/cop/migration/update_large_table_spec.rb @@ -32,6 +32,14 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do include_examples 'large tables', 'add_column_with_default' end + context 'for the change_column_type_concurrently method' do + include_examples 'large tables', 'change_column_type_concurrently' + end + + context 'for the rename_column_concurrently method' do + include_examples 'large tables', 'rename_column_concurrently' + end + context 'for the update_column_in_batches method' do include_examples 'large tables', 'update_column_in_batches' end @@ -60,6 +68,18 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do expect(cop.offenses).to be_empty end + it 'registers no offense for change_column_type_concurrently' do + inspect_source("change_column_type_concurrently :#{table}, :column, default: true") + + expect(cop.offenses).to be_empty + end + + it 'registers no offense for update_column_in_batches' do + inspect_source("rename_column_concurrently :#{table}, :column, default: true") + + expect(cop.offenses).to be_empty + end + it 'registers no offense for update_column_in_batches' do inspect_source("add_column_with_default :#{table}, :column, default: true") -- cgit v1.2.1 From dea03a0815ddceeadbb809cd4ed94f6603e4c411 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 16:43:57 +0000 Subject: =?UTF-8?q?Resolve=20"Remove=20=E2=80=9Ccommented=E2=80=9D=20from?= =?UTF-8?q?=20comment=20system=20notes"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/javascripts/notes/components/noteable_note_spec.js | 1 - 1 file changed, 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js index cfd037633e9..2ffdec7314d 100644 --- a/spec/javascripts/notes/components/noteable_note_spec.js +++ b/spec/javascripts/notes/components/noteable_note_spec.js @@ -32,7 +32,6 @@ describe('issue_note', () => { it('should render note header content', () => { expect(vm.$el.querySelector('.note-header .note-header-author-name').textContent.trim()).toEqual(note.author.name); - expect(vm.$el.querySelector('.note-header .note-headline-meta').textContent.trim()).toContain('commented'); }); it('should render note actions', () => { -- cgit v1.2.1 From 9214e273a8759cbcd21ea1ef62fd9d594cec1e1d Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Tue, 19 Jun 2018 18:33:18 +0200 Subject: Rails5 fix expected: 1 time with arguments: (97, anything, {"squash"=>false}) received: 0 times --- spec/controllers/projects/merge_requests_controller_spec.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index a412e74581d..7f5f0b76c51 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -337,7 +337,12 @@ describe Projects::MergeRequestsController do context 'when the sha parameter matches the source SHA' do def merge_with_sha(params = {}) - post :merge, base_params.merge(sha: merge_request.diff_head_sha).merge(params) + post_params = base_params.merge(sha: merge_request.diff_head_sha).merge(params) + if Gitlab.rails5? + post :merge, params: post_params, as: :json + else + post :merge, post_params + end end it 'returns :success' do -- cgit v1.2.1 From 73ade323c8993451199a965ec2330d9c7334c2e2 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 19 Jun 2018 15:45:39 -0500 Subject: Make the stubbed methods for SAML authorize paths more specific Globally stubbing any instance of `Object` seems like a very bad idea, and it led to test failures here: - https://gitlab.com/gitlab-org/gitlab-ee/-/jobs/76025159 - https://gitlab.com/gitlab-org/gitlab-ee/-/jobs/76024972 --- spec/support/helpers/login_helpers.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index f7b71bf42e3..329f18cd288 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -152,8 +152,13 @@ module LoginHelpers end def stub_saml_authorize_path_helpers - allow_any_instance_of(Object).to receive(:user_saml_omniauth_authorize_path).and_return('/users/auth/saml') - allow_any_instance_of(Object).to receive(:omniauth_authorize_path).with(:user, "saml").and_return('/users/auth/saml') + allow_any_instance_of(ActionDispatch::Routing::RoutesProxy) + .to receive(:user_saml_omniauth_authorize_path) + .and_return('/users/auth/saml') + allow(Devise::OmniAuth::UrlHelpers) + .to receive(:omniauth_authorize_path) + .with(:user, "saml") + .and_return('/users/auth/saml') end def stub_omniauth_config(messages) -- cgit v1.2.1 From 74bb2e728b8daab4073d15370ecee32ed8458a02 Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Tue, 19 Jun 2018 22:47:58 +0200 Subject: Rails5 fix Projects::PagesController spec --- spec/controllers/projects/pages_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb index 11f54eef531..8d2fa6a1740 100644 --- a/spec/controllers/projects/pages_controller_spec.rb +++ b/spec/controllers/projects/pages_controller_spec.rb @@ -71,7 +71,7 @@ describe Projects::PagesController do { namespace_id: project.namespace, project_id: project, - project: { pages_https_only: false } + project: { pages_https_only: 'false' } } end @@ -96,7 +96,7 @@ describe Projects::PagesController do it 'calls the update service' do expect(Projects::UpdateService) .to receive(:new) - .with(project, user, request_params[:project]) + .with(project, user, ActionController::Parameters.new(request_params[:project]).permit!) .and_return(update_service) patch :update, request_params -- cgit v1.2.1 From 59154779d81c6359428af5f5cbdedbbd7ed32d5c Mon Sep 17 00:00:00 2001 From: Hiroyuki Sato Date: Wed, 20 Jun 2018 12:34:58 +0900 Subject: The json keys of ref switcher should not be translated --- spec/controllers/projects_controller_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'spec') diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 705b30f0130..90e698925b6 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -597,6 +597,22 @@ describe ProjectsController do expect(parsed_body["Tags"]).to include("v1.0.0") expect(parsed_body["Commits"]).to include("123456") end + + context "when preferred language is Japanese" do + before do + user.update!(preferred_language: 'ja') + sign_in(user) + end + + it "gets a list of branches, tags and commits" do + get :refs, namespace_id: public_project.namespace, id: public_project, ref: "123456" + + parsed_body = JSON.parse(response.body) + expect(parsed_body["Branches"]).to include("master") + expect(parsed_body["Tags"]).to include("v1.0.0") + expect(parsed_body["Commits"]).to include("123456") + end + end end describe 'POST #preview_markdown' do -- cgit v1.2.1 From 46b56b18f5bf94662a8500e2b8789f504b5d6ea8 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 19 Jun 2018 17:55:02 +0200 Subject: Move mergablility check to Gitaly Closes https://gitlab.com/gitlab-org/gitaly/issues/889 --- spec/models/repository_spec.rb | 46 +++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'spec') diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index b6df048d4ca..d817a8376f4 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -434,44 +434,34 @@ describe Repository do end describe '#can_be_merged?' do - shared_examples 'can be merged' do - context 'mergeable branches' do - subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') } + context 'mergeable branches' do + subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') } - it { is_expected.to be_truthy } - end - - context 'non-mergeable branches without conflict sides missing' do - subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') } - - it { is_expected.to be_falsey } - end + it { is_expected.to be_truthy } + end - context 'non-mergeable branches with conflict sides missing' do - subject { repository.can_be_merged?('conflict-missing-side', 'conflict-start') } + context 'non-mergeable branches without conflict sides missing' do + subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') } - it { is_expected.to be_falsey } - end + it { is_expected.to be_falsey } + end - context 'non merged branch' do - subject { repository.merged_to_root_ref?('fix') } + context 'non-mergeable branches with conflict sides missing' do + subject { repository.can_be_merged?('conflict-missing-side', 'conflict-start') } - it { is_expected.to be_falsey } - end + it { is_expected.to be_falsey } + end - context 'non existent branch' do - subject { repository.merged_to_root_ref?('non_existent_branch') } + context 'non merged branch' do + subject { repository.merged_to_root_ref?('fix') } - it { is_expected.to be_nil } - end + it { is_expected.to be_falsey } end - context 'when Gitaly can_be_merged feature is enabled' do - it_behaves_like 'can be merged' - end + context 'non existent branch' do + subject { repository.merged_to_root_ref?('non_existent_branch') } - context 'when Gitaly can_be_merged feature is disabled', :disable_gitaly do - it_behaves_like 'can be merged' + it { is_expected.to be_nil } end end -- cgit v1.2.1 From 3df04bd17fe3823dfd178edfce2e4bc1e3c48e24 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Wed, 20 Jun 2018 07:36:50 +0000 Subject: Updated horizontal ellipsis icon for text-expander --- spec/javascripts/vue_shared/components/expand_button_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/vue_shared/components/expand_button_spec.js b/spec/javascripts/vue_shared/components/expand_button_spec.js index af9693c48fd..98fee9a74a5 100644 --- a/spec/javascripts/vue_shared/components/expand_button_spec.js +++ b/spec/javascripts/vue_shared/components/expand_button_spec.js @@ -19,7 +19,7 @@ describe('expand button', () => { }); it('renders a collpased button', () => { - expect(vm.$el.textContent.trim()).toEqual('...'); + expect(vm.$children[0].iconTestClass).toEqual('ic-ellipsis_h'); }); it('hides expander on click', done => { -- cgit v1.2.1 From f61dcfe816ce28427622e5a314e972ae4b33b9ea Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Thu, 14 Jun 2018 11:34:09 +0100 Subject: Operations and Kubernetes items are now omitted in the sidebar when repository or builds are disabled --- spec/policies/project_policy_spec.rb | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'spec') diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 6ac151f92f3..6d4676c25a5 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -151,6 +151,44 @@ describe ProjectPolicy do end end + context 'builds feature' do + subject { described_class.new(owner, project) } + + it 'disallows all permissions when the feature is disabled' do + project.project_feature.update(builds_access_level: ProjectFeature::DISABLED) + + builds_permissions = [ + :create_pipeline, :update_pipeline, :admin_pipeline, :destroy_pipeline, + :create_build, :read_build, :update_build, :admin_build, :destroy_build, + :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule, + :create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment, + :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster, + :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment + ] + + expect_disallowed(*builds_permissions) + end + end + + context 'repository feature' do + subject { described_class.new(owner, project) } + + it 'disallows all permissions when the feature is disabled' do + project.project_feature.update(repository_access_level: ProjectFeature::DISABLED) + + repository_permissions = [ + :create_pipeline, :update_pipeline, :admin_pipeline, :destroy_pipeline, + :create_build, :read_build, :update_build, :admin_build, :destroy_build, + :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule, + :create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment, + :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster, + :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment + ] + + expect_disallowed(*repository_permissions) + end + end + shared_examples 'archived project policies' do let(:feature_write_abilities) do described_class::READONLY_FEATURES_WHEN_ARCHIVED.flat_map do |feature| -- cgit v1.2.1 From cc061cd1c664f2003d4c7abb3930834704d2eb9e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 11:57:22 +0200 Subject: fix some more edge cases --- .../import_export/project_tree_restorer_spec.rb | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index a706ab0eb6f..94251e2280b 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -246,13 +246,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do expect(project.issues.size).to eq(results.fetch(:issues, 0)) end - it 'has issue with group label and project label' do - labels = project.issues.first.labels - - expect(labels.where(type: "ProjectLabel").count).to eq(results.fetch(:first_issue_labels, 0)) - expect(labels.where(type: "ProjectLabel").where.not(group_id: nil).count).to eq(0) - end - it 'does not set params that are excluded from import_export settings' do expect(project.import_type).to be_nil expect(project.creator_id).not_to eq 123 @@ -268,12 +261,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it 'has group milestone' do expect(project.group.milestones.size).to eq(results.fetch(:milestones, 0)) end - - it 'has issue with group label' do - labels = project.issues.first.labels - - expect(labels.where(type: "GroupLabel").count).to eq(results.fetch(:first_issue_labels, 0)) - end end context 'Light JSON' do @@ -360,12 +347,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it_behaves_like 'restores project correctly', issues: 2, labels: 1, - milestones: 1, + milestones: 2, first_issue_labels: 1 it_behaves_like 'restores group correctly', - labels: 1, - milestones: 1, + labels: 0, + milestones: 0, first_issue_labels: 1 end -- cgit v1.2.1 From 5841e92390051065d89a5b0602fa326eda018cd9 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Wed, 20 Jun 2018 12:10:14 +0000 Subject: Resolve "Unable to install Prometheus on Clusters: 'Error: Chart incompatible with Tiller v2.7.0'" --- spec/lib/gitlab/kubernetes/helm/api_spec.rb | 8 +-- .../gitlab/kubernetes/helm/install_command_spec.rb | 62 ++++++++++++++-------- spec/models/clusters/applications/ingress_spec.rb | 1 + spec/models/clusters/applications/jupyter_spec.rb | 1 + .../clusters/applications/prometheus_spec.rb | 1 + spec/models/clusters/applications/runner_spec.rb | 1 + 6 files changed, 44 insertions(+), 30 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb index 740466ea5cb..aa7e43dfb16 100644 --- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb @@ -7,13 +7,7 @@ describe Gitlab::Kubernetes::Helm::Api do let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) } let(:application) { create(:clusters_applications_prometheus) } - let(:command) do - Gitlab::Kubernetes::Helm::InstallCommand.new( - application.name, - chart: application.chart, - values: application.values - ) - end + let(:command) { application.install_command } subject { helm } diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb index 547f3f1752c..25c6fa3b9a3 100644 --- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb @@ -3,44 +3,60 @@ require 'rails_helper' describe Gitlab::Kubernetes::Helm::InstallCommand do let(:application) { create(:clusters_applications_prometheus) } let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE } - - let(:install_command) do - described_class.new( - application.name, - chart: application.chart, - values: application.values - ) - end + let(:install_command) { application.install_command } subject { install_command } - it_behaves_like 'helm commands' do - let(:commands) do - <<~EOS + context 'for ingress' do + let(:application) { create(:clusters_applications_ingress) } + + it_behaves_like 'helm commands' do + let(:commands) do + <<~EOS helm init --client-only >/dev/null helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null - EOS + EOS + end + end + end + + context 'for prometheus' do + let(:application) { create(:clusters_applications_prometheus) } + + it_behaves_like 'helm commands' do + let(:commands) do + <<~EOS + helm init --client-only >/dev/null + helm install #{application.chart} --name #{application.name} --version #{application.version} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null + EOS + end end end - context 'with an application with a repository' do + context 'for runner' do let(:ci_runner) { create(:ci_runner) } let(:application) { create(:clusters_applications_runner, runner: ci_runner) } - let(:install_command) do - described_class.new( - application.name, - chart: application.chart, - values: application.values, - repository: application.repository - ) + + it_behaves_like 'helm commands' do + let(:commands) do + <<~EOS + helm init --client-only >/dev/null + helm repo add #{application.name} #{application.repository} + helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null + EOS + end end + end + + context 'for jupyter' do + let(:application) { create(:clusters_applications_jupyter) } it_behaves_like 'helm commands' do let(:commands) do <<~EOS - helm init --client-only >/dev/null - helm repo add #{application.name} #{application.repository} - helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null + helm init --client-only >/dev/null + helm repo add #{application.name} #{application.repository} + helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null EOS end end diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index a47a07d908d..bb5b2ef3a47 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -73,6 +73,7 @@ describe Clusters::Applications::Ingress do it 'should be initialized with ingress arguments' do expect(subject.name).to eq('ingress') expect(subject.chart).to eq('stable/nginx-ingress') + expect(subject.version).to be_nil expect(subject.values).to eq(ingress.values) end end diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb index ca48a1d8072..65750141e65 100644 --- a/spec/models/clusters/applications/jupyter_spec.rb +++ b/spec/models/clusters/applications/jupyter_spec.rb @@ -36,6 +36,7 @@ describe Clusters::Applications::Jupyter do it 'should be initialized with 4 arguments' do expect(subject.name).to eq('jupyter') expect(subject.chart).to eq('jupyter/jupyterhub') + expect(subject.version).to be_nil expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/') expect(subject.values).to eq(jupyter.values) end diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index d2302583ac8..efd57040005 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -109,6 +109,7 @@ describe Clusters::Applications::Prometheus do it 'should be initialized with 3 arguments' do expect(subject.name).to eq('prometheus') expect(subject.chart).to eq('stable/prometheus') + expect(subject.version).to eq('6.7.3') expect(subject.values).to eq(prometheus.values) end end diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb index 3ef59457c5f..b12500d0acd 100644 --- a/spec/models/clusters/applications/runner_spec.rb +++ b/spec/models/clusters/applications/runner_spec.rb @@ -31,6 +31,7 @@ describe Clusters::Applications::Runner do it 'should be initialized with 4 arguments' do expect(subject.name).to eq('runner') expect(subject.chart).to eq('runner/gitlab-runner') + expect(subject.version).to be_nil expect(subject.repository).to eq('https://charts.gitlab.io') expect(subject.values).to eq(gitlab_runner.values) end -- cgit v1.2.1 From 0e6441164f74a76b4b3decd3c0b0d8d31e15c815 Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Sat, 16 Jun 2018 15:20:30 +0200 Subject: remove all unused eslint-disable rules --- spec/javascripts/activities_spec.js | 2 +- spec/javascripts/awards_handler_spec.js | 2 +- spec/javascripts/boards/boards_store_spec.js | 2 +- spec/javascripts/bootstrap_jquery_spec.js | 2 +- spec/javascripts/gl_dropdown_spec.js | 2 +- spec/javascripts/gl_field_errors_spec.js | 2 +- spec/javascripts/helpers/vue_resource_helper.js | 1 - spec/javascripts/issuable_time_tracker_spec.js | 2 +- spec/javascripts/issue_spec.js | 2 +- spec/javascripts/job_spec.js | 1 - spec/javascripts/line_highlighter_spec.js | 2 +- spec/javascripts/merge_request_spec.js | 2 +- spec/javascripts/mini_pipeline_graph_dropdown_spec.js | 2 -- spec/javascripts/monitoring/mock_data.js | 2 -- spec/javascripts/new_branch_spec.js | 2 +- spec/javascripts/notes_spec.js | 2 +- spec/javascripts/right_sidebar_spec.js | 2 +- spec/javascripts/search_autocomplete_spec.js | 2 +- spec/javascripts/syntax_highlight_spec.js | 2 +- spec/javascripts/u2f/mock_u2f_device.js | 3 +-- 20 files changed, 16 insertions(+), 23 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/activities_spec.js b/spec/javascripts/activities_spec.js index 5dbdcd24296..068b8eb65bc 100644 --- a/spec/javascripts/activities_spec.js +++ b/spec/javascripts/activities_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable no-unused-expressions, no-prototype-builtins, no-new, no-shadow, max-len */ +/* eslint-disable no-unused-expressions, no-prototype-builtins, no-new, no-shadow */ import $ from 'jquery'; import 'vendor/jquery.endless-scroll'; diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index e81055bc08f..ff6020c8fdd 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, max-len */ +/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, no-unused-vars, quotes, prefer-template, max-len */ import $ from 'jquery'; import Cookies from 'js-cookie'; diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js index 3f5ed4f3d07..f7af099b3bf 100644 --- a/spec/javascripts/boards/boards_store_spec.js +++ b/spec/javascripts/boards/boards_store_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable comma-dangle, one-var, no-unused-vars */ +/* eslint-disable comma-dangle, no-unused-vars */ /* global ListIssue */ import Vue from 'vue'; diff --git a/spec/javascripts/bootstrap_jquery_spec.js b/spec/javascripts/bootstrap_jquery_spec.js index 0fd6f9dc810..052465d8d88 100644 --- a/spec/javascripts/bootstrap_jquery_spec.js +++ b/spec/javascripts/bootstrap_jquery_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, no-var */ +/* eslint-disable no-var */ import $ from 'jquery'; import '~/commons/bootstrap'; diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js index 175f386b60e..af58dff7da7 100644 --- a/spec/javascripts/gl_dropdown_spec.js +++ b/spec/javascripts/gl_dropdown_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable comma-dangle, no-param-reassign, no-unused-expressions, max-len */ +/* eslint-disable comma-dangle, no-param-reassign */ import $ from 'jquery'; import GLDropdown from '~/gl_dropdown'; diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index 108e0064c47..2839020b2ca 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, arrow-body-style */ +/* eslint-disable arrow-body-style */ import $ from 'jquery'; import GlFieldErrors from '~/gl_field_errors'; diff --git a/spec/javascripts/helpers/vue_resource_helper.js b/spec/javascripts/helpers/vue_resource_helper.js index 0d1bf5e2e80..70b7ec4e574 100644 --- a/spec/javascripts/helpers/vue_resource_helper.js +++ b/spec/javascripts/helpers/vue_resource_helper.js @@ -5,7 +5,6 @@ export const headersInterceptor = (request, next) => { response.headers.forEach((value, key) => { headers[key] = value; }); - // eslint-disable-next-line no-param-reassign response.headers = headers; }); }; diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js index ba9040524b1..5add150f874 100644 --- a/spec/javascripts/issuable_time_tracker_spec.js +++ b/spec/javascripts/issuable_time_tracker_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable no-unused-vars, space-before-function-paren, func-call-spacing, no-spaced-func, semi, max-len, quotes, space-infix-ops, padded-blocks */ +/* eslint-disable no-unused-vars, func-call-spacing, no-spaced-func, semi, quotes, space-infix-ops, max-len */ import $ from 'jquery'; import Vue from 'vue'; diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js index 047ecab27db..e12419b835d 100644 --- a/spec/javascripts/issue_spec.js +++ b/spec/javascripts/issue_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */ +/* eslint-disable one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle */ import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js index da00b615c9b..79e375aa02e 100644 --- a/spec/javascripts/job_spec.js +++ b/spec/javascripts/job_spec.js @@ -304,7 +304,6 @@ describe('Job', () => { describe('getBuildTrace', () => { it('should request build trace with state parameter', (done) => { spyOn(axios, 'get').and.callThrough(); - // eslint-disable-next-line no-new job = new Job(); setTimeout(() => { diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js index d2bdc9e160c..8cf0017f4d8 100644 --- a/spec/javascripts/line_highlighter_spec.js +++ b/spec/javascripts/line_highlighter_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, no-var, no-param-reassign, quotes, prefer-template, no-else-return, new-cap, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, jasmine/no-spec-dupes, no-underscore-dangle, max-len */ +/* eslint-disable no-var, quotes, prefer-template, no-else-return, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, no-underscore-dangle, max-len */ import $ from 'jquery'; import LineHighlighter from '~/line_highlighter'; diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 74ceff76d37..22eb0ad7143 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, no-return-assign */ +/* eslint-disable no-return-assign */ import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js index 009b3fd75b7..1879424c629 100644 --- a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js +++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable no-new */ - import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index 50da6da2e07..799d03f6b57 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -1,5 +1,3 @@ -/* eslint-disable quote-props, indent, comma-dangle */ - export const mockApiEndpoint = `${gl.TEST_HOST}/monitoring/mock`; export const metricsGroupsAPIResponse = { diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js index 5e5d8f8f34f..122e5bc58b2 100644 --- a/spec/javascripts/new_branch_spec.js +++ b/spec/javascripts/new_branch_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */ +/* eslint-disable one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */ import $ from 'jquery'; import NewBranchForm from '~/new_branch_form'; diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index acbf23e2007..2854263a25a 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, max-len */ +/* eslint-disable no-unused-expressions, no-var, object-shorthand */ import $ from 'jquery'; import _ from 'underscore'; import MockAdapter from 'axios-mock-adapter'; diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index e264b16335f..6d49536a712 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, new-parens, no-return-assign, new-cap, vars-on-top, max-len */ +/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-return-assign, vars-on-top, max-len */ import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index 4f515f98a7e..86c001678c5 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, comma-dangle, object-shorthand, prefer-template, quotes, new-parens, vars-on-top, new-cap, max-len */ +/* eslint-disable max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, object-shorthand, prefer-template, vars-on-top, max-len */ import $ from 'jquery'; import '~/gl_dropdown'; diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js index 0d1fa680e00..1c3dac3584e 100644 --- a/spec/javascripts/syntax_highlight_spec.js +++ b/spec/javascripts/syntax_highlight_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable space-before-function-paren, no-var, no-return-assign, quotes */ +/* eslint-disable no-var, no-return-assign, quotes */ import $ from 'jquery'; import syntaxHighlight from '~/syntax_highlight'; diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js index 8fec6ae3fa4..012a1cefbbf 100644 --- a/spec/javascripts/u2f/mock_u2f_device.js +++ b/spec/javascripts/u2f/mock_u2f_device.js @@ -1,5 +1,4 @@ -/* eslint-disable prefer-rest-params, wrap-iife, -no-unused-expressions, no-return-assign, no-param-reassign */ +/* eslint-disable wrap-iife, no-unused-expressions, no-return-assign, no-param-reassign */ export default class MockU2FDevice { constructor() { -- cgit v1.2.1 From 6d8611567c1ac59c0e9889513ba9fe79d9eed408 Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Sat, 16 Jun 2018 15:20:57 +0200 Subject: ensure that `import/no-unresolved` does not trigger for our fixture files --- spec/javascripts/.eslintrc.yml | 4 ++++ spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js | 2 -- spec/javascripts/blob/pdf/index_spec.js | 2 -- spec/javascripts/pdf/index_spec.js | 2 -- spec/javascripts/pdf/page_spec.js | 2 -- 5 files changed, 4 insertions(+), 8 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/.eslintrc.yml b/spec/javascripts/.eslintrc.yml index 8bceb2c50fc..78e2f3b521f 100644 --- a/spec/javascripts/.eslintrc.yml +++ b/spec/javascripts/.eslintrc.yml @@ -32,3 +32,7 @@ rules: - branch no-console: off prefer-arrow-callback: off + import/no-unresolved: + - error + - ignore: + - 'fixtures/blob' diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js index acd0aaf2a86..c726fa8e428 100644 --- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js +++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable import/no-unresolved */ - import BalsamiqViewer from '~/blob/balsamiq/balsamiq_viewer'; import bmprPath from '../../fixtures/blob/balsamiq/test.bmpr'; diff --git a/spec/javascripts/blob/pdf/index_spec.js b/spec/javascripts/blob/pdf/index_spec.js index 51bf3086627..bbe2500f8e3 100644 --- a/spec/javascripts/blob/pdf/index_spec.js +++ b/spec/javascripts/blob/pdf/index_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable import/no-unresolved */ - import renderPDF from '~/blob/pdf'; import testPDF from '../../fixtures/blob/pdf/test.pdf'; diff --git a/spec/javascripts/pdf/index_spec.js b/spec/javascripts/pdf/index_spec.js index bebed432f91..69230bb0937 100644 --- a/spec/javascripts/pdf/index_spec.js +++ b/spec/javascripts/pdf/index_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable import/no-unresolved */ - import Vue from 'vue'; import { PDFJS } from 'vendor/pdf'; import workerSrc from 'vendor/pdf.worker.min'; diff --git a/spec/javascripts/pdf/page_spec.js b/spec/javascripts/pdf/page_spec.js index ac5b21e8f6c..9c686748c10 100644 --- a/spec/javascripts/pdf/page_spec.js +++ b/spec/javascripts/pdf/page_spec.js @@ -1,5 +1,3 @@ -/* eslint-disable import/no-unresolved */ - import Vue from 'vue'; import pdfjsLib from 'vendor/pdf'; import workerSrc from 'vendor/pdf.worker.min'; -- cgit v1.2.1 From d73e68deb55ea9a5b52facd2f701b9772c0717ac Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 20 Jun 2018 14:33:14 +0200 Subject: Limit the action suffixes in transaction metrics There seem to be a lot of cases where the suffix of an action (e.g. ".html") is set to bogus data, such as "*/*" or entire URLs. This can increase cardinality of our metrics, and isn't very useful for monitoring and filtering. To work around this, we enforce a whitelist containing a few suffixes we actually care about. Suffixes not supported will be grouped under the action without a suffix. This means that a request to "FooController#bar.jpeg" will be assigned to "FooController#bar". --- spec/lib/gitlab/metrics/web_transaction_spec.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/metrics/web_transaction_spec.rb b/spec/lib/gitlab/metrics/web_transaction_spec.rb index 6eb0600f49e..0b3b23e930f 100644 --- a/spec/lib/gitlab/metrics/web_transaction_spec.rb +++ b/spec/lib/gitlab/metrics/web_transaction_spec.rb @@ -194,7 +194,7 @@ describe Gitlab::Metrics::WebTransaction do expect(transaction.action).to eq('TestController#show') end - context 'when the response content type is not :html' do + context 'when the request content type is not :html' do let(:request) { double(:request, format: double(:format, ref: :json)) } it 'appends the mime type to the transaction action' do @@ -202,6 +202,15 @@ describe Gitlab::Metrics::WebTransaction do expect(transaction.action).to eq('TestController#show.json') end end + + context 'when the request content type is not' do + let(:request) { double(:request, format: double(:format, ref: 'http://example.com')) } + + it 'does not append the MIME type to the transaction action' do + expect(transaction.labels).to eq({ controller: 'TestController', action: 'show' }) + expect(transaction.action).to eq('TestController#show') + end + end end it 'returns no labels when no route information is present in env' do -- cgit v1.2.1 From d527b468025922c04ba03edffb7ef33300ec9bb6 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 20 Jun 2018 14:00:17 +0200 Subject: Remove remaining traces of the Allocations Gem In MR https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15834 we removed use of the data produced by the Allocations Gem. However, we never removed the code that just enables tracking of allocations. In this commit we remove all remaining traces of this Gem. --- spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb | 4 ---- spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb | 4 ---- 2 files changed, 8 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb index f66451c5188..81954fcf8c5 100644 --- a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb @@ -3,10 +3,6 @@ require 'spec_helper' describe Gitlab::Metrics::Samplers::InfluxSampler do let(:sampler) { described_class.new(5) } - after do - Allocations.stop if Gitlab::Metrics.mri? - end - describe '#start' do it 'runs once and gathers a sample at a given interval' do expect(sampler).to receive(:sleep).with(a_kind_of(Numeric)).twice diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb index 54781dd52fc..091645ee86f 100644 --- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb @@ -8,10 +8,6 @@ describe Gitlab::Metrics::Samplers::RubySampler do allow(Gitlab::Metrics::NullMetric).to receive(:instance).and_return(null_metric) end - after do - Allocations.stop if Gitlab::Metrics.mri? - end - describe '#sample' do it 'samples various statistics' do expect(Gitlab::Metrics::System).to receive(:memory_usage) -- cgit v1.2.1 From 8522173e0ad912c41aaa68f4e01f03ac73cc4514 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 15:45:15 +0200 Subject: refactor code once again to fix IID issues --- .../import_export/group_project_finder_spec.rb | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb index a86f0c2dd44..c36d80e51d4 100644 --- a/spec/lib/gitlab/import_export/group_project_finder_spec.rb +++ b/spec/lib/gitlab/import_export/group_project_finder_spec.rb @@ -16,15 +16,15 @@ describe Gitlab::ImportExport::GroupProjectFinder do expect(described_class.find_or_new(Label, title: 'group label', - 'project_id' => project.id, - 'group_id' => project.group.id)).to eq(group_label) + project: project, + group: project.group)).to eq(group_label) end it 'initializes a new label' do label = described_class.find_or_new(Label, title: 'group label', - 'project_id' => project.id, - 'group_id' => project.group.id) + project: project, + group: project.group) expect(label.persisted?).to be false end @@ -32,8 +32,8 @@ describe Gitlab::ImportExport::GroupProjectFinder do it 'creates a new label' do label = described_class.find_or_create(Label, title: 'group label', - 'project_id' => project.id, - 'group_id' => project.group.id) + project: project, + group: project.group) expect(label.persisted?).to be true end @@ -45,24 +45,24 @@ describe Gitlab::ImportExport::GroupProjectFinder do expect(described_class.find_or_new(Milestone, title: 'group milestone', - 'project_id' => project.id, - 'group_id' => project.group.id)).to eq(milestone) + project: project, + group: project.group)).to eq(milestone) end it 'initializes a new milestone' do milestone = described_class.find_or_new(Milestone, - title: 'group milestone', - 'project_id' => project.id, - 'group_id' => project.group.id) + title: 'group milestone', + project: project, + group: project.group) expect(milestone.persisted?).to be false end it 'creates a new milestone' do milestone = described_class.find_or_create(Milestone, - title: 'group milestone', - 'project_id' => project.id, - 'group_id' => project.group.id) + title: 'group milestone', + project: project, + group: project.group) expect(milestone.persisted?).to be true end -- cgit v1.2.1 From b612670cc0a9eabce5d0f8aa86e93823a4639c04 Mon Sep 17 00:00:00 2001 From: Alexis Reigel Date: Wed, 20 Jun 2018 14:00:13 +0000 Subject: Fix: Serve favicon image always from the main GitLab domain to avoid issues with CORS --- spec/lib/gitlab/favicon_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index f36111a4946..122dcd9634c 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -21,6 +21,21 @@ RSpec.describe Gitlab::Favicon, :request_store do create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png') expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/dk.png} end + + context 'asset host' do + before do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) + end + + it 'returns a relative url when the asset host is not configured' do + expect(described_class.main).to match %r{^/assets/favicon-(?:\h+).png$} + end + + it 'returns a full url when the asset host is configured' do + allow(Gitlab::Application.config).to receive(:asset_host).and_return('http://assets.local') + expect(described_class.main).to match %r{^http://localhost/assets/favicon-(?:\h+).png$} + end + end end describe '.status_overlay' do -- cgit v1.2.1 From b0ddc55dc79bc141ae02c79cff83ec51672a4160 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 16:18:40 +0200 Subject: refactor code once again to fix IID issues --- spec/lib/gitlab/import_export/project.light.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json index a401ce4c0c7..ba2248073f5 100644 --- a/spec/lib/gitlab/import_export/project.light.json +++ b/spec/lib/gitlab/import_export/project.light.json @@ -67,7 +67,7 @@ "milestone": { "id": 1, "title": "A milestone", - "project_id": 8, + "group_id": 8, "description": "Project-level milestone", "due_date": null, "created_at": "2016-06-14T15:02:04.415Z", -- cgit v1.2.1 From 1753efbc143d18fe0c29e33319b8779c1d03f7c6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 20 Jun 2018 16:34:19 +0200 Subject: Add spec for more examples of trailing punctuation that should be ignored --- spec/lib/banzai/filter/label_reference_filter_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index f34d3dc935b..00257ed7904 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -148,9 +148,11 @@ describe Banzai::Filter::LabelReferenceFilter do expect(doc.text).to eq 'See ?g.fm&' end - it 'links with adjacent text' do - doc = reference_filter("Label #{reference}.") - expect(doc.to_html).to match(%r(\?g\.fm&\.)) + it 'does not include trailing punctuation', :aggregate_failures do + ['.', ', ok?', '...', '?', '!', ': is that ok?'].each do |trailing_punctuation| + doc = filter("Label #{reference}#{trailing_punctuation}") + expect(doc.to_html).to match(%r(\?g\.fm&#{Regexp.escape(trailing_punctuation)})) + end end it 'ignores invalid label names' do -- cgit v1.2.1 From 5b994b8199a3679b6f5ef0d00edce22ea9662664 Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Thu, 7 Jun 2018 23:56:59 +0900 Subject: Notify only when unmergeable due to conflict There is still the edge case when 'no commits' changes to 'conflict' would not trigger notification, which we ignore for now. Calling can_be_merged? can cause exception (e.g. non-UTF8) Ignore those by rescueing. Remove unmergeable_reason as now only conflict is notified Update spec --- spec/mailers/notify_spec.rb | 8 +----- spec/models/merge_request_spec.rb | 16 +++++++++-- spec/presenters/merge_request_presenter_spec.rb | 35 ------------------------- 3 files changed, 15 insertions(+), 44 deletions(-) (limited to 'spec') diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 775ca4ba0eb..a9a45367b4a 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -416,16 +416,10 @@ describe Notify do end it 'has the correct subject and body' do - reasons = %w[foo bar] - - allow_any_instance_of(MergeRequestPresenter).to receive(:unmergeable_reasons).and_return(reasons) aggregate_failures do is_expected.to have_referable_subject(merge_request, reply: true) is_expected.to have_body_text(project_merge_request_path(project, merge_request)) - is_expected.to have_body_text('following reasons:') - reasons.each do |reason| - is_expected.to have_body_text(reason) - end + is_expected.to have_body_text('due to conflict.') end end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 3f028b3bd5c..7ae70c3afb4 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1324,6 +1324,7 @@ describe MergeRequest do context 'when broken' do before do allow(subject).to receive(:broken?) { true } + allow(project.repository).to receive(:can_be_merged?).and_return(false) end it 'becomes unmergeable' do @@ -2150,9 +2151,11 @@ describe MergeRequest do before do allow(NotificationService).to receive(:new).and_return(notification_service) allow(TodoService).to receive(:new).and_return(todo_service) + + allow(subject.project.repository).to receive(:can_be_merged?).and_return(false) end - it 'notifies, but does not notify again if rechecking still results in cannot_be_merged' do + it 'notifies conflict, but does not notify again if rechecking still results in cannot_be_merged' do expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once @@ -2161,7 +2164,7 @@ describe MergeRequest do subject.mark_as_unmergeable end - it 'notifies whenever merge request is newly unmergeable' do + it 'notifies conflict, whenever newly unmergeable' do expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice @@ -2171,6 +2174,15 @@ describe MergeRequest do subject.mark_as_unchecked subject.mark_as_unmergeable end + + it 'does not notify whenever merge request is newly unmergeable due to other reasons' do + allow(subject.project.repository).to receive(:can_be_merged?).and_return(true) + + expect(notification_service).not_to receive(:merge_request_unmergeable) + expect(todo_service).not_to receive(:merge_request_became_unmergeable) + + subject.mark_as_unmergeable + end end describe 'check_state?' do diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb index d5fb4a7742c..e3b37739e8e 100644 --- a/spec/presenters/merge_request_presenter_spec.rb +++ b/spec/presenters/merge_request_presenter_spec.rb @@ -70,41 +70,6 @@ describe MergeRequestPresenter do end end - describe "#unmergeable_reasons" do - let(:presenter) { described_class.new(resource, current_user: user) } - - before do - # Mergeable base state - allow(resource).to receive(:has_no_commits?).and_return(false) - allow(resource).to receive(:source_branch_exists?).and_return(true) - allow(resource).to receive(:target_branch_exists?).and_return(true) - allow(resource.project.repository).to receive(:can_be_merged?).and_return(true) - end - - it "handles mergeable request" do - expect(presenter.unmergeable_reasons).to eq([]) - end - - it "handles no commit" do - allow(resource).to receive(:has_no_commits?).and_return(true) - - expect(presenter.unmergeable_reasons).to eq(["no commits"]) - end - - it "handles branches missing" do - allow(resource).to receive(:source_branch_exists?).and_return(false) - allow(resource).to receive(:target_branch_exists?).and_return(false) - - expect(presenter.unmergeable_reasons).to eq(["source branch is missing", "target branch is missing"]) - end - - it "handles merge conflict" do - allow(resource.project.repository).to receive(:can_be_merged?).and_return(false) - - expect(presenter.unmergeable_reasons).to eq(["has merge conflicts"]) - end - end - describe '#conflict_resolution_path' do let(:project) { create :project } let(:user) { create :user } -- cgit v1.2.1 From f3f1df1476ba7fe223e5d8d6707a7675dc9fa597 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 18 Jun 2018 10:31:41 +0200 Subject: Add a cop for `FinderMethods` This notifies developers when calling `find(_by!)` chained on `execute`. And suggests using the methods from `FinderMethods`. These will perform the correct authorization checks on the resource when it is found. --- .../rubocop/cop/gitlab/finder_with_find_by_spec.rb | 56 ++++++++++++++++++++++ spec/services/issues/resolve_discussions_spec.rb | 4 +- 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb (limited to 'spec') diff --git a/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb b/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb new file mode 100644 index 00000000000..7f689b196c5 --- /dev/null +++ b/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../../rubocop/cop/gitlab/finder_with_find_by' + +describe RuboCop::Cop::Gitlab::FinderWithFindBy do + include CopHelper + + subject(:cop) { described_class.new } + + context 'when calling execute.find' do + let(:source) do + <<~SRC + DummyFinder.new(some_args) + .execute + .find_by!(1) + SRC + end + let(:corrected_source) do + <<~SRC + DummyFinder.new(some_args) + .find_by!(1) + SRC + end + + it 'registers an offence' do + inspect_source(source) + + expect(cop.offenses.size).to eq(1) + end + + it 'can autocorrect the source' do + expect(autocorrect_source(source)).to eq(corrected_source) + end + + context 'when called within the `FinderMethods` module' do + let(:source) do + <<~SRC + module FinderMethods + def find_by!(*args) + execute.find_by!(args) + end + end + SRC + end + + it 'does not register an offence' do + inspect_source(source) + + expect(cop.offenses).to be_empty + end + end + end +end diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb index 13accc6ae1b..b6cfc09da65 100644 --- a/spec/services/issues/resolve_discussions_spec.rb +++ b/spec/services/issues/resolve_discussions_spec.rb @@ -31,10 +31,8 @@ describe Issues::ResolveDiscussions do it "only queries for the merge request once" do fake_finder = double - fake_results = double - expect(fake_finder).to receive(:execute).and_return(fake_results).exactly(1) - expect(fake_results).to receive(:find_by).exactly(1) + expect(fake_finder).to receive(:find_by).exactly(1) expect(MergeRequestsFinder).to receive(:new).and_return(fake_finder).exactly(1) 2.times { service.merge_request_to_resolve_discussions_of } -- cgit v1.2.1 From 727bba8ef5840501a83f4e2fc5f895530079c264 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 20 Jun 2018 14:16:14 -0300 Subject: Add enabled prefix to feature keys to be consistent with other keys --- spec/lib/gitlab/usage_data_spec.rb | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 22d921716aa..20def4fefe2 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -29,20 +29,20 @@ describe Gitlab::UsageData do active_user_count counts recorded_at - mattermost_enabled edition version installation_type uuid hostname - signup - ldap - gravatar - omniauth - reply_by_email - container_registry + mattermost_enabled + signup_enabled + ldap_enabled + gravatar_enabled + omniauth_enabled + reply_by_email_enabled + container_registry_enabled + gitlab_shared_runners_enabled gitlab_pages - gitlab_shared_runners git database avg_cycle_analytics @@ -129,13 +129,14 @@ describe Gitlab::UsageData do subject { described_class.features_usage_data_ce } it 'gathers feature usage data' do - expect(subject[:signup]).to eq(Gitlab::CurrentSettings.allow_signup?) - expect(subject[:ldap]).to eq(Gitlab.config.ldap.enabled) - expect(subject[:gravatar]).to eq(Gitlab::CurrentSettings.gravatar_enabled?) - expect(subject[:omniauth]).to eq(Gitlab.config.omniauth.enabled) - expect(subject[:reply_by_email]).to eq(Gitlab::IncomingEmail.enabled?) - expect(subject[:container_registry]).to eq(Gitlab.config.registry.enabled) - expect(subject[:gitlab_shared_runners]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled) + expect(subject[:mattermost_enabled]).to eq(Gitlab.config.mattermost.enabled) + expect(subject[:signup_enabled]).to eq(Gitlab::CurrentSettings.allow_signup?) + expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled) + expect(subject[:gravatar_enabled]).to eq(Gitlab::CurrentSettings.gravatar_enabled?) + expect(subject[:omniauth_enabled]).to eq(Gitlab.config.omniauth.enabled) + expect(subject[:reply_by_email_enabled]).to eq(Gitlab::IncomingEmail.enabled?) + expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled) + expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled) end end -- cgit v1.2.1 From 7d91984ced810a3fb4e11416c057012fbae687ce Mon Sep 17 00:00:00 2001 From: Michael Kozono Date: Tue, 19 Jun 2018 12:32:01 -0700 Subject: Avoid N+1 queries while processing uploads --- spec/lib/gitlab/verify/uploads_spec.rb | 45 ++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/verify/uploads_spec.rb b/spec/lib/gitlab/verify/uploads_spec.rb index 296866d3319..38c30fab1ba 100644 --- a/spec/lib/gitlab/verify/uploads_spec.rb +++ b/spec/lib/gitlab/verify/uploads_spec.rb @@ -47,20 +47,49 @@ describe Gitlab::Verify::Uploads do before do stub_uploads_object_storage(AvatarUploader) upload.update!(store: ObjectStorage::Store::REMOTE) - expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file) end - it 'passes uploads in object storage that exist' do - expect(file).to receive(:exists?).and_return(true) + describe 'returned hash object' do + before do + expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file) + end - expect(failures).to eq({}) + it 'passes uploads in object storage that exist' do + expect(file).to receive(:exists?).and_return(true) + + expect(failures).to eq({}) + end + + it 'fails uploads in object storage that do not exist' do + expect(file).to receive(:exists?).and_return(false) + + expect(failures.keys).to contain_exactly(upload) + expect(failure).to include('Remote object does not exist') + end end - it 'fails uploads in object storage that do not exist' do - expect(file).to receive(:exists?).and_return(false) + describe 'performance' do + before do + allow(file).to receive(:exists?) + allow(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file) + end + + it "avoids N+1 queries" do + control_count = ActiveRecord::QueryRecorder.new { perform_task } + + # Create additional uploads in object storage + projects = create_list(:project, 3, :with_avatar) + uploads = projects.flat_map(&:uploads) + uploads.each do |upload| + upload.update!(store: ObjectStorage::Store::REMOTE) + end + + expect { perform_task }.not_to exceed_query_limit(control_count) + end - expect(failures.keys).to contain_exactly(upload) - expect(failure).to include('Remote object does not exist') + def perform_task + described_class.new(batch_size: 100).run_batches { } + end end end end -- cgit v1.2.1 From 2c15890edcf6b292e52e6fcbf6d2d1a7ef0aabe0 Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 20 Jun 2018 19:36:15 +0200 Subject: Rails 5 fix Capybara::ElementNotFound: Unable to find visible css #modal-revert-commit and expected: "/bar" got: "/foo" --- spec/features/profiles/account_spec.rb | 2 ++ .../features/projects/merge_requests/user_reverts_merge_request_spec.rb | 2 ++ 2 files changed, 4 insertions(+) (limited to 'spec') diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb index 215b658eb7b..95947d2f111 100644 --- a/spec/features/profiles/account_spec.rb +++ b/spec/features/profiles/account_spec.rb @@ -67,4 +67,6 @@ def update_username(new_username) page.within('.modal') do find('.js-modal-primary-action').click end + + wait_for_requests end diff --git a/spec/features/projects/merge_requests/user_reverts_merge_request_spec.rb b/spec/features/projects/merge_requests/user_reverts_merge_request_spec.rb index f3e97bc9eb2..67b6aefb2d8 100644 --- a/spec/features/projects/merge_requests/user_reverts_merge_request_spec.rb +++ b/spec/features/projects/merge_requests/user_reverts_merge_request_spec.rb @@ -13,6 +13,8 @@ describe 'User reverts a merge request', :js do click_button('Merge') + wait_for_requests + visit(merge_request_path(merge_request)) end -- cgit v1.2.1 From 8af62a64c4e7cae9d62e13e1c026948f02675bfa Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 20 Jun 2018 14:18:45 -0700 Subject: Fall back to the first sign-in tab if the local storage value is bad If `current_signin_tab` is `#crowd`, then no tab will be active. Fix this by using the first available tab. Relates to https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20049#note_82996324 --- spec/javascripts/signin_tabs_memoizer_spec.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js index 423432c9e5d..9d3905fa1d8 100644 --- a/spec/javascripts/signin_tabs_memoizer_spec.js +++ b/spec/javascripts/signin_tabs_memoizer_spec.js @@ -45,6 +45,21 @@ import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer'; expect(fakeTab.click).toHaveBeenCalled(); }); + it('clicks the first tab if value in local storage is bad', () => { + createMemoizer().saveData('#bogus'); + const fakeTab = { + click: () => {}, + }; + spyOn(document, 'querySelector').and.callFake(selector => (selector === `${tabSelector} a[href="#bogus"]` ? null : fakeTab)); + spyOn(fakeTab, 'click'); + + memo.bootstrap(); + + // verify that triggers click on stored selector and fallback + expect(document.querySelector.calls.allArgs()).toEqual([['ul.new-session-tabs a[href="#bogus"]'], ['ul.new-session-tabs a']]); + expect(fakeTab.click).toHaveBeenCalled(); + }); + it('saves last selected tab on change', () => { createMemoizer(); -- cgit v1.2.1 From 2ac672280b914b24acecd2d4b113dea79023a5db Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 21 Jun 2018 10:12:51 +0200 Subject: Raw changes calculation is Gitaly only now Closes https://gitlab.com/gitlab-org/gitaly/issues/1138 --- spec/lib/gitlab/git/repository_spec.rb | 60 ++++++++++++++-------------------- 1 file changed, 25 insertions(+), 35 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 595482f76d5..45f0006dc85 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1043,50 +1043,40 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#raw_changes_between' do - shared_examples 'raw changes' do - let(:old_rev) { } - let(:new_rev) { } - let(:changes) { repository.raw_changes_between(old_rev, new_rev) } + let(:old_rev) { } + let(:new_rev) { } + let(:changes) { repository.raw_changes_between(old_rev, new_rev) } - context 'initial commit' do - let(:old_rev) { Gitlab::Git::BLANK_SHA } - let(:new_rev) { '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863' } + context 'initial commit' do + let(:old_rev) { Gitlab::Git::BLANK_SHA } + let(:new_rev) { '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863' } - it 'returns the changes' do - expect(changes).to be_present - expect(changes.size).to eq(3) - end + it 'returns the changes' do + expect(changes).to be_present + expect(changes.size).to eq(3) end + end - context 'with an invalid rev' do - let(:old_rev) { 'foo' } - let(:new_rev) { 'bar' } + context 'with an invalid rev' do + let(:old_rev) { 'foo' } + let(:new_rev) { 'bar' } - it 'returns an error' do - expect { changes }.to raise_error(Gitlab::Git::Repository::GitError) - end - end - - context 'with valid revs' do - let(:old_rev) { 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6' } - let(:new_rev) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' } - - it 'returns the changes' do - expect(changes.size).to eq(9) - expect(changes.first.operation).to eq(:modified) - expect(changes.first.new_path).to eq('.gitmodules') - expect(changes.last.operation).to eq(:added) - expect(changes.last.new_path).to eq('files/lfs/picture-invalid.png') - end + it 'returns an error' do + expect { changes }.to raise_error(Gitlab::Git::Repository::GitError) end end - context 'when gitaly is enabled' do - it_behaves_like 'raw changes' - end + context 'with valid revs' do + let(:old_rev) { 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6' } + let(:new_rev) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' } - context 'when gitaly is disabled', :disable_gitaly do - it_behaves_like 'raw changes' + it 'returns the changes' do + expect(changes.size).to eq(9) + expect(changes.first.operation).to eq(:modified) + expect(changes.first.new_path).to eq('.gitmodules') + expect(changes.last.operation).to eq(:added) + expect(changes.last.new_path).to eq('files/lfs/picture-invalid.png') + end end end -- cgit v1.2.1 From 562f357ba5cf59f8325a87b9fba178e4c9830ad5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jun 2018 21:04:44 +0800 Subject: Implement `expect_next_instance_of` and use it We need this because `expect_any_instance_of` doesn't work on prepended models. Now we could use the same code between CE/EE --- spec/lib/gitlab/bitbucket_import/project_creator_spec.rb | 4 +++- spec/lib/gitlab/gitlab_import/project_creator_spec.rb | 4 +++- spec/lib/gitlab/google_code_import/project_creator_spec.rb | 4 +++- .../lib/gitlab/legacy_github_import/project_creator_spec.rb | 5 ++++- spec/spec_helper.rb | 1 + spec/support/helpers/expect_next_instance_of.rb | 13 +++++++++++++ spec/workers/update_merge_requests_worker_spec.rb | 10 +++------- 7 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 spec/support/helpers/expect_next_instance_of.rb (limited to 'spec') diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb index c3f528dd6fc..ed6fa3d229f 100644 --- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb @@ -25,7 +25,9 @@ describe Gitlab::BitbucketImport::ProjectCreator do end it 'creates project' do - allow_any_instance_of(Project).to receive(:add_import_job) + expect_next_instance_of(Project) do |project| + expect(project).to receive(:add_import_job) + end project_creator = described_class.new(repo, 'vim', namespace, user, access_params) project = project_creator.execute diff --git a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb index 5ea086e4abd..b814f5fc76c 100644 --- a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb +++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb @@ -21,7 +21,9 @@ describe Gitlab::GitlabImport::ProjectCreator do end it 'creates project' do - allow_any_instance_of(Project).to receive(:add_import_job) + expect_next_instance_of(Project) do |project| + expect(project).to receive(:add_import_job) + end project_creator = described_class.new(repo, namespace, user, access_params) project = project_creator.execute diff --git a/spec/lib/gitlab/google_code_import/project_creator_spec.rb b/spec/lib/gitlab/google_code_import/project_creator_spec.rb index 24cd518c77b..b959e006292 100644 --- a/spec/lib/gitlab/google_code_import/project_creator_spec.rb +++ b/spec/lib/gitlab/google_code_import/project_creator_spec.rb @@ -16,7 +16,9 @@ describe Gitlab::GoogleCodeImport::ProjectCreator do end it 'creates project' do - allow_any_instance_of(Project).to receive(:add_import_job) + expect_next_instance_of(Project) do |project| + expect(project).to receive(:add_import_job) + end project_creator = described_class.new(repo, namespace, user) project = project_creator.execute diff --git a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb index 972b17d5b12..3d4240fa4ba 100644 --- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb @@ -17,7 +17,10 @@ describe Gitlab::LegacyGithubImport::ProjectCreator do before do namespace.add_owner(user) - allow_any_instance_of(Project).to receive(:add_import_job) + + expect_next_instance_of(Project) do |project| + expect(project).to receive(:add_import_job) + end end describe '#execute' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dac609e2545..fdce8e84620 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -69,6 +69,7 @@ RSpec.configure do |config| config.include StubFeatureFlags config.include StubGitlabCalls config.include StubGitlabData + config.include ExpectNextInstanceOf config.include TestEnv config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::IntegrationHelpers, type: :feature diff --git a/spec/support/helpers/expect_next_instance_of.rb b/spec/support/helpers/expect_next_instance_of.rb new file mode 100644 index 00000000000..b95046b2b42 --- /dev/null +++ b/spec/support/helpers/expect_next_instance_of.rb @@ -0,0 +1,13 @@ +module ExpectNextInstanceOf + def expect_next_instance_of(klass, *new_args) + receive_new = receive(:new) + receive_new.with(*new_args) if new_args.any? + + expect(klass).to receive_new + .and_wrap_original do |method, *original_args| + method.call(*original_args).tap do |instance| + yield(instance) + end + end + end +end diff --git a/spec/workers/update_merge_requests_worker_spec.rb b/spec/workers/update_merge_requests_worker_spec.rb index 80137815d2b..0b553db0ca4 100644 --- a/spec/workers/update_merge_requests_worker_spec.rb +++ b/spec/workers/update_merge_requests_worker_spec.rb @@ -18,13 +18,9 @@ describe UpdateMergeRequestsWorker do end it 'executes MergeRequests::RefreshService with expected values' do - expect(MergeRequests::RefreshService).to receive(:new) - .with(project, user).and_wrap_original do |method, *args| - method.call(*args).tap do |refresh_service| - expect(refresh_service) - .to receive(:execute).with(oldrev, newrev, ref) - end - end + expect_next_instance_of(MergeRequests::RefreshService, project, user) do |refresh_service| + expect(refresh_service).to receive(:execute).with(oldrev, newrev, ref) + end perform end -- cgit v1.2.1 From 3e66795ef1ff1228906239763910b051d8afcc37 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 21 Jun 2018 12:22:40 +0000 Subject: Changes tab VUE refactoring --- spec/controllers/projects/blob_controller_spec.rb | 60 ++- .../projects/discussions_controller_spec.rb | 23 +- .../controllers/projects/issues_controller_spec.rb | 2 +- .../merge_requests/diffs_controller_spec.rb | 29 +- spec/controllers/projects/notes_controller_spec.rb | 14 +- .../issuables/markdown_references/jira_spec.rb | 2 +- spec/features/issuables/shortcuts_issuable_spec.rb | 23 +- ..._issue_for_discussions_in_merge_request_spec.rb | 14 +- ..._for_single_discussion_in_merge_request_spec.rb | 22 +- .../user_creates_image_diff_notes_spec.rb | 6 +- .../merge_request/user_locks_discussion_spec.rb | 4 +- .../merge_request/user_posts_diff_notes_spec.rb | 27 +- .../merge_request/user_posts_notes_spec.rb | 24 +- .../merge_request/user_resolves_conflicts_spec.rb | 21 +- ...lves_diff_notes_and_discussions_resolve_spec.rb | 44 +- ...user_resolves_outdated_diff_discussions_spec.rb | 2 +- .../user_scrolls_to_note_on_load_spec.rb | 10 +- .../user_sees_avatar_on_diff_notes_spec.rb | 6 +- spec/features/merge_request/user_sees_diff_spec.rb | 15 +- .../merge_request/user_sees_discussions_spec.rb | 8 +- .../user_sees_mini_pipeline_graph_spec.rb | 3 +- .../user_sees_notes_from_forked_project_spec.rb | 2 +- .../merge_request/user_sees_system_notes_spec.rb | 2 +- .../merge_request/user_sees_versions_spec.rb | 6 +- .../merge_request/user_uses_slash_commands_spec.rb | 33 +- spec/features/participants_autocomplete_spec.rb | 6 +- .../commit/user_comments_on_commit_spec.rb | 109 ++++ .../merge_requests/user_comments_on_diff_spec.rb | 8 +- .../user_comments_on_merge_request_spec.rb | 3 +- .../merge_requests/user_views_diffs_spec.rb | 12 +- spec/features/projects/view_on_env_spec.rb | 4 +- spec/finders/notes_finder_spec.rb | 2 +- .../api/schemas/entities/merge_request_widget.json | 1 + spec/javascripts/awards_handler_spec.js | 106 ++-- spec/javascripts/behaviors/quick_submit_spec.js | 81 +-- spec/javascripts/diffs/components/app_spec.js | 1 + .../components/changed_files_dropdown_spec.js | 1 + .../diffs/components/changed_files_spec.js | 100 ++++ .../components/compare_versions_dropdown_spec.js | 1 + .../diffs/components/compare_versions_spec.js | 1 + .../diffs/components/diff_content_spec.js | 1 + .../diffs/components/diff_discussions_spec.js | 24 + .../diffs/components/diff_file_header_spec.js | 433 ++++++++++++++++ .../javascripts/diffs/components/diff_file_spec.js | 88 ++++ .../diffs/components/diff_gutter_avatars_spec.js | 115 +++++ .../components/diff_line_gutter_content_spec.js | 153 ++++++ .../diffs/components/diff_line_note_form_spec.js | 68 +++ .../diffs/components/edit_button_spec.js | 1 + .../diffs/components/hidden_files_warning_spec.js | 1 + .../diffs/components/inline_diff_view_spec.js | 111 ++++ .../diffs/components/no_changes_spec.js | 1 + .../diffs/components/parallel_diff_view_spec.js | 224 ++++++++ .../diffs/mock_data/diff_discussions.js | 496 ++++++++++++++++++ spec/javascripts/diffs/mock_data/diff_file.js | 220 ++++++++ spec/javascripts/diffs/store/actions_spec.js | 210 ++++++++ spec/javascripts/diffs/store/getters_spec.js | 24 + spec/javascripts/diffs/store/mutations_spec.js | 147 ++++++ spec/javascripts/diffs/store/utils_spec.js | 179 +++++++ spec/javascripts/fixtures/commit.rb | 33 ++ spec/javascripts/fixtures/snippet.rb | 1 + spec/javascripts/helpers/index.js | 3 + .../javascripts/helpers/init_vue_mr_page_helper.js | 40 ++ spec/javascripts/lib/utils/common_utils_spec.js | 70 +++ spec/javascripts/lib/utils/text_utility_spec.js | 16 + spec/javascripts/matchers.js | 26 +- spec/javascripts/merge_request_notes_spec.js | 108 ---- spec/javascripts/merge_request_tabs_spec.js | 565 ++++++--------------- .../notes/components/comment_form_spec.js | 94 ++-- .../notes/components/diff_file_header_spec.js | 93 ---- .../notes/components/diff_with_note_spec.js | 20 +- .../notes/components/discussion_counter_spec.js | 58 +++ .../notes/components/note_actions_spec.js | 12 +- spec/javascripts/notes/components/note_app_spec.js | 68 ++- .../notes/components/note_awards_list_spec.js | 8 +- .../javascripts/notes/components/note_body_spec.js | 7 +- .../javascripts/notes/components/note_form_spec.js | 28 +- .../notes/components/note_header_spec.js | 30 +- .../components/note_signed_out_widget_spec.js | 20 +- .../notes/components/noteable_discussion_spec.js | 83 ++- .../notes/components/noteable_note_spec.js | 15 +- spec/javascripts/notes/mock_data.js | 13 + spec/javascripts/notes/stores/actions_spec.js | 40 +- spec/javascripts/notes/stores/getters_spec.js | 20 +- spec/javascripts/notes/stores/mutation_spec.js | 104 ++-- spec/javascripts/notes_spec.js | 151 ++---- .../pipelines/pipelines_table_row_spec.js | 27 +- spec/javascripts/shortcuts_issuable_spec.js | 77 ++- spec/javascripts/shortcuts_spec.js | 19 +- spec/javascripts/test_bundle.js | 3 +- .../components/notes/placeholder_note_spec.js | 20 +- .../components/notes/system_note_spec.js | 14 +- spec/javascripts/zen_mode_spec.js | 59 ++- spec/models/note_spec.rb | 10 + spec/serializers/blob_entity_spec.rb | 20 + spec/serializers/diff_file_entity_spec.rb | 48 +- spec/serializers/diffs_entity_spec.rb | 28 + spec/serializers/discussion_entity_spec.rb | 34 +- spec/serializers/merge_request_diff_entity_spec.rb | 24 + spec/serializers/merge_request_user_entity_spec.rb | 19 + .../features/reportable_note_shared_examples.rb | 2 +- spec/support/helpers/merge_request_diff_helpers.rb | 2 +- .../serializers/note_entity_examples.rb | 4 +- 102 files changed, 4237 insertions(+), 1233 deletions(-) create mode 100644 spec/features/projects/commit/user_comments_on_commit_spec.rb create mode 100644 spec/javascripts/diffs/components/app_spec.js create mode 100644 spec/javascripts/diffs/components/changed_files_dropdown_spec.js create mode 100644 spec/javascripts/diffs/components/changed_files_spec.js create mode 100644 spec/javascripts/diffs/components/compare_versions_dropdown_spec.js create mode 100644 spec/javascripts/diffs/components/compare_versions_spec.js create mode 100644 spec/javascripts/diffs/components/diff_content_spec.js create mode 100644 spec/javascripts/diffs/components/diff_discussions_spec.js create mode 100644 spec/javascripts/diffs/components/diff_file_header_spec.js create mode 100644 spec/javascripts/diffs/components/diff_file_spec.js create mode 100644 spec/javascripts/diffs/components/diff_gutter_avatars_spec.js create mode 100644 spec/javascripts/diffs/components/diff_line_gutter_content_spec.js create mode 100644 spec/javascripts/diffs/components/diff_line_note_form_spec.js create mode 100644 spec/javascripts/diffs/components/edit_button_spec.js create mode 100644 spec/javascripts/diffs/components/hidden_files_warning_spec.js create mode 100644 spec/javascripts/diffs/components/inline_diff_view_spec.js create mode 100644 spec/javascripts/diffs/components/no_changes_spec.js create mode 100644 spec/javascripts/diffs/components/parallel_diff_view_spec.js create mode 100644 spec/javascripts/diffs/mock_data/diff_discussions.js create mode 100644 spec/javascripts/diffs/mock_data/diff_file.js create mode 100644 spec/javascripts/diffs/store/actions_spec.js create mode 100644 spec/javascripts/diffs/store/getters_spec.js create mode 100644 spec/javascripts/diffs/store/mutations_spec.js create mode 100644 spec/javascripts/diffs/store/utils_spec.js create mode 100644 spec/javascripts/fixtures/commit.rb create mode 100644 spec/javascripts/helpers/index.js create mode 100644 spec/javascripts/helpers/init_vue_mr_page_helper.js delete mode 100644 spec/javascripts/merge_request_notes_spec.js delete mode 100644 spec/javascripts/notes/components/diff_file_header_spec.js create mode 100644 spec/javascripts/notes/components/discussion_counter_spec.js create mode 100644 spec/serializers/blob_entity_spec.rb create mode 100644 spec/serializers/diffs_entity_spec.rb create mode 100644 spec/serializers/merge_request_diff_entity_spec.rb create mode 100644 spec/serializers/merge_request_user_entity_spec.rb (limited to 'spec') diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 9e696e9cb29..4dcb7dc6c87 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -122,10 +122,64 @@ describe Projects::BlobController do end context 'when essential params are present' do - it 'renders the diff content' do - do_get(since: 1, to: 5, offset: 10) + context 'when rendering for commit' do + it 'renders the diff content' do + do_get(since: 1, to: 5, offset: 10) - expect(response.body).to be_present + expect(response.body).to be_present + end + end + + context 'when rendering for merge request' do + it 'renders diff context lines Gitlab::Diff::Line array' do + do_get(since: 1, to: 5, offset: 10, from_merge_request: true) + + lines = JSON.parse(response.body) + + expect(lines.first).to have_key('type') + expect(lines.first).to have_key('rich_text') + expect(lines.first).to have_key('rich_text') + end + + context 'when rendering match lines' do + it 'adds top match line when "since" is less than 1' do + do_get(since: 5, to: 10, offset: 10, from_merge_request: true) + + match_line = JSON.parse(response.body).first + + expect(match_line['type']).to eq('match') + expect(match_line['meta_data']).to have_key('old_pos') + expect(match_line['meta_data']).to have_key('new_pos') + end + + it 'does not add top match line when when "since" is equal 1' do + do_get(since: 1, to: 10, offset: 10, from_merge_request: true) + + match_line = JSON.parse(response.body).first + + expect(match_line['type']).to eq('context') + end + + it 'adds bottom match line when "t"o is less than blob size' do + do_get(since: 1, to: 5, offset: 10, from_merge_request: true, bottom: true) + + match_line = JSON.parse(response.body).last + + expect(match_line['type']).to eq('match') + expect(match_line['meta_data']).to have_key('old_pos') + expect(match_line['meta_data']).to have_key('new_pos') + end + + it 'does not add bottom match line when "to" is less than blob size' do + commit_id = project.repository.commit('master').id + blob = project.repository.blob_at(commit_id, 'CHANGELOG') + do_get(since: 1, to: blob.lines.count, offset: 10, from_merge_request: true, bottom: true) + + match_line = JSON.parse(response.body).last + + expect(match_line['type']).to eq('context') + end + end end end end diff --git a/spec/controllers/projects/discussions_controller_spec.rb b/spec/controllers/projects/discussions_controller_spec.rb index 53647749a60..4aa33dbbb01 100644 --- a/spec/controllers/projects/discussions_controller_spec.rb +++ b/spec/controllers/projects/discussions_controller_spec.rb @@ -110,7 +110,7 @@ describe Projects::DiscussionsController do it "returns the name of the resolving user" do post :resolve, request_params - expect(JSON.parse(response.body)["resolved_by"]).to eq(user.name) + expect(JSON.parse(response.body)['resolved_by']['name']).to eq(user.name) end it "returns status 200" do @@ -119,16 +119,21 @@ describe Projects::DiscussionsController do expect(response).to have_gitlab_http_status(200) end - context "when vue_mr_discussions cookie is present" do - before do - allow(controller).to receive(:cookies).and_return(vue_mr_discussions: 'true') - end + it "renders discussion with serializer" do + expect_any_instance_of(DiscussionSerializer).to receive(:represent) + .with(instance_of(Discussion), { context: instance_of(described_class), render_truncated_diff_lines: true }) - it "renders discussion with serializer" do - expect_any_instance_of(DiscussionSerializer).to receive(:represent) - .with(instance_of(Discussion), { context: instance_of(described_class) }) + post :resolve, request_params + end + context 'diff discussion' do + let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) } + let(:discussion) { note.discussion } + + it "returns truncated diff lines" do post :resolve, request_params + + expect(JSON.parse(response.body)['truncated_diff_lines']).to be_present end end end @@ -187,7 +192,7 @@ describe Projects::DiscussionsController do it "renders discussion with serializer" do expect_any_instance_of(DiscussionSerializer).to receive(:represent) - .with(instance_of(Discussion), { context: instance_of(described_class) }) + .with(instance_of(Discussion), { context: instance_of(described_class), render_truncated_diff_lines: true }) delete :unresolve, request_params end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 106611b37c9..3a41f0fc07a 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -990,7 +990,7 @@ describe Projects::IssuesController do it 'returns discussion json' do get :discussions, namespace_id: project.namespace, project_id: project, id: issue.iid - expect(json_response.first.keys).to match_array(%w[id reply_id expanded notes diff_discussion individual_note resolvable resolved]) + expect(json_response.first.keys).to match_array(%w[id reply_id expanded notes diff_discussion discussion_path individual_note resolvable resolved resolved_at resolved_by resolved_by_push commit_id for_commit project_id]) end context 'with cross-reference system note', :request_store do diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb index 5d297c654bf..ec82b35f227 100644 --- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb @@ -26,12 +26,13 @@ describe Projects::MergeRequests::DiffsController do context 'with default params' do context 'for the same project' do before do - go + allow(controller).to receive(:rendered_for_merge_request?).and_return(true) end - it 'renders the diffs template to a string' do - expect(response).to render_template('projects/merge_requests/diffs/_diffs') - expect(json_response).to have_key('html') + it 'serializes merge request diff collection' do + expect_any_instance_of(DiffsSerializer).to receive(:represent).with(an_instance_of(Gitlab::Diff::FileCollection::MergeRequestDiff), an_instance_of(Hash)) + + go end end @@ -56,17 +57,6 @@ describe Projects::MergeRequests::DiffsController do end end - context 'with ignore_whitespace_change' do - before do - go(w: 1) - end - - it 'renders the diffs template to a string' do - expect(response).to render_template('projects/merge_requests/diffs/_diffs') - expect(json_response).to have_key('html') - end - end - context 'with view' do before do go(view: 'parallel') @@ -105,12 +95,11 @@ describe Projects::MergeRequests::DiffsController do end it 'only renders the diffs for the path given' do - expect(controller).to receive(:render_diff_for_path).and_wrap_original do |meth, diffs| - expect(diffs.diff_files.map(&:new_path)).to contain_exactly(existing_path) - meth.call(diffs) - end - diff_for_path(old_path: existing_path, new_path: existing_path) + + paths = JSON.parse(response.body)["diff_files"].map { |file| file['new_path'] } + + expect(paths).to include(existing_path) end end diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index de132dfaa21..1458113b90c 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -51,7 +51,7 @@ describe Projects::NotesController do let(:project) { create(:project, :repository) } let!(:note) { create(:discussion_note_on_merge_request, project: project) } - let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id) } + let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id, html: true) } it 'responds with the expected attributes' do get :index, params @@ -67,7 +67,7 @@ describe Projects::NotesController do let(:project) { create(:project, :repository) } let!(:note) { create(:diff_note_on_merge_request, project: project) } - let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id) } + let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id, html: true) } it 'responds with the expected attributes' do get :index, params @@ -86,7 +86,7 @@ describe Projects::NotesController do context 'when displayed on a merge request' do let(:merge_request) { create(:merge_request, source_project: project) } - let(:params) { request_params.merge(target_type: 'merge_request', target_id: merge_request.id) } + let(:params) { request_params.merge(target_type: 'merge_request', target_id: merge_request.id, html: true) } it 'responds with the expected attributes' do get :index, params @@ -99,7 +99,7 @@ describe Projects::NotesController do end context 'when displayed on the commit' do - let(:params) { request_params.merge(target_type: 'commit', target_id: note.commit_id) } + let(:params) { request_params.merge(target_type: 'commit', target_id: note.commit_id, html: true) } it 'responds with the expected attributes' do get :index, params @@ -128,7 +128,7 @@ describe Projects::NotesController do context 'for a regular note' do let!(:note) { create(:note_on_merge_request, project: project) } - let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id) } + let(:params) { request_params.merge(target_type: 'merge_request', target_id: note.noteable_id, html: true) } it 'responds with the expected attributes' do get :index, params @@ -293,7 +293,7 @@ describe Projects::NotesController do context 'when a noteable is not found' do it 'returns 404 status' do - request_params[:note][:noteable_id] = 9999 + request_params[:target_id] = 9999 post :create, request_params.merge(format: :json) expect(response).to have_gitlab_http_status(404) @@ -475,7 +475,7 @@ describe Projects::NotesController do end it "returns the name of the resolving user" do - post :resolve, request_params + post :resolve, request_params.merge(html: true) expect(JSON.parse(response.body)["resolved_by"]).to eq(user.name) end diff --git a/spec/features/issuables/markdown_references/jira_spec.rb b/spec/features/issuables/markdown_references/jira_spec.rb index fa0ab88624e..8eaccfc0949 100644 --- a/spec/features/issuables/markdown_references/jira_spec.rb +++ b/spec/features/issuables/markdown_references/jira_spec.rb @@ -163,7 +163,7 @@ describe "Jira", :js do HEREDOC page.within("#diff-notes-app") do - fill_in("note_note", with: markdown) + fill_in("note-body", with: markdown) end end diff --git a/spec/features/issuables/shortcuts_issuable_spec.rb b/spec/features/issuables/shortcuts_issuable_spec.rb index e25fd1a6249..0a19086ffbd 100644 --- a/spec/features/issuables/shortcuts_issuable_spec.rb +++ b/spec/features/issuables/shortcuts_issuable_spec.rb @@ -12,6 +12,15 @@ feature 'Blob shortcuts', :js do sign_in(user) end + shared_examples "quotes the selected text" do + it "quotes the selected text" do + select_element('.note-text') + find('body').native.send_key('r') + + expect(find('.js-main-target-form .js-vue-comment-form').value).to include(note_text) + end + end + describe 'pressing "r"' do describe 'On an Issue' do before do @@ -20,12 +29,7 @@ feature 'Blob shortcuts', :js do wait_for_requests end - it 'quotes the selected text' do - select_element('.note-text') - find('body').native.send_key('r') - - expect(find('.js-main-target-form .js-vue-comment-form').value).to include(note_text) - end + include_examples 'quotes the selected text' end describe 'On a Merge Request' do @@ -35,12 +39,7 @@ feature 'Blob shortcuts', :js do wait_for_requests end - it 'quotes the selected text' do - select_element('.note-text') - find('body').native.send_key('r') - - expect(find('.js-main-target-form #note_note').value).to include(note_text) - end + include_examples 'quotes the selected text' end end end diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb index e0466aaf422..52962002c33 100644 --- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb @@ -6,6 +6,12 @@ feature 'Resolving all open discussions in a merge request from an issue', :js d let(:merge_request) { create(:merge_request, source_project: project) } let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion } + def resolve_all_discussions_link_selector + text = "Resolve all discussions in new issue" + url = new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) + %Q{a[data-original-title="#{text}"][href="#{url}"]} + end + describe 'as a user with access to the project' do before do project.add_master(user) @@ -14,8 +20,8 @@ feature 'Resolving all open discussions in a merge request from an issue', :js d end it 'shows a button to resolve all discussions by creating a new issue' do - within('#resolve-count-app') do - expect(page).to have_link "Resolve all discussions in new issue", href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) + within('.line-resolve-all-container') do + expect(page).to have_selector resolve_all_discussions_link_selector end end @@ -25,13 +31,13 @@ feature 'Resolving all open discussions in a merge request from an issue', :js d end it 'hides the link for creating a new issue' do - expect(page).not_to have_link "Resolve all discussions in new issue", href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) + expect(page).not_to have_selector resolve_all_discussions_link_selector end end context 'creating an issue for discussions' do before do - click_link "Resolve all discussions in new issue", href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid) + find(resolve_all_discussions_link_selector).click end it_behaves_like 'creating an issue for a discussion' diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb index 34beb282bad..9170f9295f0 100644 --- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb @@ -1,11 +1,17 @@ require 'rails_helper' -feature 'Resolve an open discussion in a merge request by creating an issue' do +feature 'Resolve an open discussion in a merge request by creating an issue', :js do let(:user) { create(:user) } let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) } let(:merge_request) { create(:merge_request, source_project: project) } let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion } + def resolve_discussion_selector + title = 'Resolve this discussion in a new issue' + url = new_project_issue_path(project, discussion_to_resolve: discussion.id, merge_request_to_resolve_discussions_of: merge_request.iid) + "a[data-original-title=\"#{title}\"][href=\"#{url}\"]" + end + describe 'As a user with access to the project' do before do project.add_master(user) @@ -20,17 +26,17 @@ feature 'Resolve an open discussion in a merge request by creating an issue' do end it 'does not show a link to create a new issue' do - expect(page).not_to have_link 'Resolve this discussion in a new issue' + expect(page).not_to have_css resolve_discussion_selector end end - context 'resolving the discussion', :js do + context 'resolving the discussion' do before do click_button 'Resolve discussion' end it 'hides the link for creating a new issue' do - expect(page).not_to have_link 'Resolve this discussion in a new issue' + expect(page).not_to have_css resolve_discussion_selector end it 'shows the link for creating a new issue when unresolving a discussion' do @@ -38,19 +44,17 @@ feature 'Resolve an open discussion in a merge request by creating an issue' do click_button 'Unresolve discussion' end - expect(page).to have_link 'Resolve this discussion in a new issue' + expect(page).to have_css resolve_discussion_selector end end it 'has a link to create a new issue for a discussion' do - new_issue_link = new_project_issue_path(project, discussion_to_resolve: discussion.id, merge_request_to_resolve_discussions_of: merge_request.iid) - - expect(page).to have_link 'Resolve this discussion in a new issue', href: new_issue_link + expect(page).to have_css resolve_discussion_selector end context 'creating the issue' do before do - click_link 'Resolve this discussion in a new issue', href: new_project_issue_path(project, discussion_to_resolve: discussion.id, merge_request_to_resolve_discussions_of: merge_request.iid) + find(resolve_discussion_selector).click end it 'has a hidden field for the discussion' do diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb index 25c408516d1..728e89db400 100644 --- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb +++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb @@ -114,7 +114,8 @@ feature 'Merge request > User creates image diff notes', :js do create_image_diff_note end - it 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'shows indicator and avatar badges, and allows collapsing/expanding the discussion notes' do indicator = find('.js-image-badge', match: :first) badge = find('.image-diff-avatar-link .badge', match: :first) @@ -156,7 +157,8 @@ feature 'Merge request > User creates image diff notes', :js do visit project_merge_request_path(project, merge_request) end - it 'render diff indicators within the image frame' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'render diff indicators within the image frame' do diff_note = create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) wait_for_requests diff --git a/spec/features/merge_request/user_locks_discussion_spec.rb b/spec/features/merge_request/user_locks_discussion_spec.rb index a68df872334..76c759ab8d3 100644 --- a/spec/features/merge_request/user_locks_discussion_spec.rb +++ b/spec/features/merge_request/user_locks_discussion_spec.rb @@ -38,9 +38,9 @@ describe 'Merge request > User locks discussion', :js do end it 'the user can not create a comment' do - page.within('.issuable-discussion #notes') do + page.within('.js-vue-notes-event') do expect(page).not_to have_selector('js-main-target-form') - expect(page.find('.disabled-comment')) + expect(page.find('.issuable-note-warning')) .to have_content('This merge request is locked. Only project members can comment.') end end diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb index 2b4623d6dc9..13cc5f256eb 100644 --- a/spec/features/merge_request/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb @@ -65,11 +65,13 @@ describe 'Merge request > User posts diff notes', :js do context 'with a match line' do it 'does not allow commenting on the left side' do - should_not_allow_commenting(find('.match', match: :first).find(:xpath, '..'), 'left') + line_holder = find('.match', match: :first).find(:xpath, '..') + should_not_allow_commenting(line_holder, 'left') end it 'does not allow commenting on the right side' do - should_not_allow_commenting(find('.match', match: :first).find(:xpath, '..'), 'right') + line_holder = find('.match', match: :first).find(:xpath, '..') + should_not_allow_commenting(line_holder, 'right') end end @@ -81,7 +83,7 @@ describe 'Merge request > User posts diff notes', :js do # The first `.js-unfold` unfolds upwards, therefore the first # `.line_holder` will be an unfolded line. - let(:line_holder) { first('.line_holder[id="1"]') } + let(:line_holder) { first('#a5cc2925ca8258af241be7e5b0381edf30266302 .line_holder') } it 'does not allow commenting on the left side' do should_not_allow_commenting(line_holder, 'left') @@ -143,7 +145,7 @@ describe 'Merge request > User posts diff notes', :js do # The first `.js-unfold` unfolds upwards, therefore the first # `.line_holder` will be an unfolded line. - let(:line_holder) { first('.line_holder[id="1"]') } + let(:line_holder) { first('.line_holder[id="a5cc2925ca8258af241be7e5b0381edf30266302_1_1"]') } it 'does not allow commenting' do should_not_allow_commenting line_holder @@ -183,7 +185,7 @@ describe 'Merge request > User posts diff notes', :js do end describe 'posting a note' do - it 'adds as discussion' do + xit 'adds as discussion' do expect(page).to have_css('.js-temp-notes-holder', count: 2) should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false) @@ -201,20 +203,23 @@ describe 'Merge request > User posts diff notes', :js do end context 'with a new line' do - it 'allows commenting' do - should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]')) + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'allows commenting' do + should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]').find(:xpath, '..')) end end context 'with an old line' do - it 'allows commenting' do - should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]')) + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'allows commenting' do + should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]').find(:xpath, '..')) end end context 'with an unchanged line' do - it 'allows commenting' do - should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]')) + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'allows commenting' do + should_allow_commenting(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"]').find(:xpath, '..')) end end diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index 3bd9f5e2298..fa819cbc385 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -24,10 +24,9 @@ describe 'Merge request > User posts notes', :js do describe 'the note form' do it 'is valid' do is_expected.to have_css('.js-main-target-form', visible: true, count: 1) - expect(find('.js-main-target-form .js-comment-button').value) - .to eq('Comment') + expect(find('.js-main-target-form')).to have_selector('button', text: 'Comment') page.within('.js-main-target-form') do - expect(page).not_to have_link('Cancel') + expect(page).not_to have_button('Cancel') end end @@ -60,8 +59,9 @@ describe 'Merge request > User posts notes', :js do is_expected.to have_content('This is awesome!') page.within('.js-main-target-form') do expect(page).to have_no_field('note[note]', with: 'This is awesome!') - expect(page).to have_css('.js-md-preview', visible: :hidden) + expect(page).to have_css('.js-vue-md-preview', visible: :hidden) end + wait_for_requests page.within('.js-main-target-form') do is_expected.to have_css('.js-note-text', visible: true) end @@ -76,6 +76,7 @@ describe 'Merge request > User posts notes', :js do end it 'hides the toolbar buttons when previewing a note' do + wait_for_requests find('.js-md-preview-button').click page.within('.js-main-target-form') do expect(page).not_to have_css('.md-header-toolbar.active') @@ -84,11 +85,6 @@ describe 'Merge request > User posts notes', :js do end describe 'when editing a note' do - it 'there should be a hidden edit form' do - is_expected.to have_css('.note-edit-form:not(.mr-note-edit-form)', visible: false, count: 1) - is_expected.to have_css('.note-edit-form.mr-note-edit-form', visible: false, count: 1) - end - describe 'editing the note' do before do find('.note').hover @@ -108,8 +104,8 @@ describe 'Merge request > User posts notes', :js do within('.current-note-edit-form') do fill_in 'note[note]', with: 'Some new content' find('.btn-cancel').click - expect(find('.js-note-text', visible: false).text).to eq '' end + expect(find('.js-note-text').text).to eq '' end it 'allows using markdown buttons after saving a note and then trying to edit it again' do @@ -118,8 +114,8 @@ describe 'Merge request > User posts notes', :js do find('.btn-save').click end - wait_for_requests find('.note').hover + wait_for_requests find('.js-note-edit').click @@ -151,13 +147,15 @@ describe 'Merge request > User posts notes', :js do find('.js-note-edit').click end - it 'shows the delete link' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'shows the delete link' do page.within('.note-attachment') do is_expected.to have_css('.js-note-attachment-delete') end end - it 'removes the attachment div and resets the edit form' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'removes the attachment div and resets the edit form' do accept_confirm { find('.js-note-attachment-delete').click } is_expected.not_to have_css('.note-attachment') is_expected.not_to have_css('.current-note-edit-form') diff --git a/spec/features/merge_request/user_resolves_conflicts_spec.rb b/spec/features/merge_request/user_resolves_conflicts_spec.rb index 59aa90fc86f..629052442b4 100644 --- a/spec/features/merge_request/user_resolves_conflicts_spec.rb +++ b/spec/features/merge_request/user_resolves_conflicts_spec.rb @@ -44,7 +44,9 @@ describe 'Merge request > User resolves conflicts', :js do within find('.diff-file', text: 'files/ruby/regex.rb') do expect(page).to have_selector('.line_content.new', text: "def username_regexp") + expect(page).not_to have_selector('.line_content.new', text: "def username_regex") expect(page).to have_selector('.line_content.new', text: "def project_name_regexp") + expect(page).not_to have_selector('.line_content.new', text: "def project_name_regex") expect(page).to have_selector('.line_content.new', text: "def path_regexp") expect(page).to have_selector('.line_content.new', text: "def archive_formats_regexp") expect(page).to have_selector('.line_content.new', text: "def git_reference_regexp") @@ -108,8 +110,12 @@ describe 'Merge request > User resolves conflicts', :js do click_link('conflicts', href: %r{/conflicts\Z}) end - include_examples "conflicts are resolved in Interactive mode" - include_examples "conflicts are resolved in Edit inline mode" + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + # include_examples "conflicts are resolved in Interactive mode" + # include_examples "conflicts are resolved in Edit inline mode" + + it 'prevents RSpec/EmptyExampleGroup' do + end end context 'in Parallel view mode' do @@ -118,8 +124,12 @@ describe 'Merge request > User resolves conflicts', :js do click_button 'Side-by-side' end - include_examples "conflicts are resolved in Interactive mode" - include_examples "conflicts are resolved in Edit inline mode" + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + # include_examples "conflicts are resolved in Interactive mode" + # include_examples "conflicts are resolved in Edit inline mode" + + it 'prevents RSpec/EmptyExampleGroup' do + end end end @@ -138,7 +148,8 @@ describe 'Merge request > User resolves conflicts', :js do end end - it 'conflicts are resolved in Edit inline mode' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'conflicts are resolved in Edit inline mode' do within find('.files-wrapper .diff-file', text: 'files/markdown/ruby-style-guide.md') do wait_for_requests find('.files-wrapper .diff-file pre') diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb index 0fd2840c426..a0b9d6cb059 100644 --- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb +++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb @@ -102,7 +102,8 @@ describe 'Merge request > User resolves diff notes and discussions', :js do describe 'timeline view' do it 'hides when resolve discussion is clicked' do - expect(page).to have_selector('.discussion-body', visible: false) + expect(page).to have_selector('.discussion-header') + expect(page).not_to have_selector('.discussion-body') end it 'shows resolved discussion when toggled' do @@ -129,7 +130,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do end it 'hides when resolve discussion is clicked' do - expect(page).to have_selector('.diffs .diff-file .notes_holder', visible: false) + expect(page).not_to have_selector('.diffs .diff-file .notes_holder') end it 'shows resolved discussion when toggled' do @@ -218,10 +219,13 @@ describe 'Merge request > User resolves diff notes and discussions', :js do it 'updates updated text after resolving note' do page.within '.diff-content .note' do - find('.line-resolve-btn').click - end + resolve_button = find('.line-resolve-btn') + + resolve_button.click + wait_for_requests - expect(page).to have_content("Resolved by #{user.name}") + expect(resolve_button['data-original-title']).to eq("Resolved by #{user.name}") + end end it 'hides jump to next discussion button' do @@ -254,11 +258,16 @@ describe 'Merge request > User resolves diff notes and discussions', :js do end it 'resolves discussion' do - page.all('.note .line-resolve-btn').each do |button| + resolve_buttons = page.all('.note .line-resolve-btn', count: 2) + resolve_buttons.each do |button| button.click end - expect(page).to have_content('Resolved by') + wait_for_requests + + resolve_buttons.each do |button| + expect(button['data-original-title']).to eq("Resolved by #{user.name}") + end page.within '.line-resolve-all-container' do expect(page).to have_content('1/1 discussion resolved') @@ -287,7 +296,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do end it 'allows user to mark all notes as resolved' do - page.all('.line-resolve-btn').each do |btn| + page.all('.note .line-resolve-btn', count: 2).each do |btn| btn.click end @@ -298,7 +307,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do end it 'allows user user to mark all discussions as resolved' do - page.all('.discussion-reply-holder').each do |reply_holder| + page.all('.discussion-reply-holder', count: 2).each do |reply_holder| page.within reply_holder do click_button 'Resolve discussion' end @@ -311,7 +320,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do end it 'allows user to quickly scroll to next unresolved discussion' do - page.within first('.discussion-reply-holder') do + page.within('.discussion-reply-holder', match: :first) do click_button 'Resolve discussion' end @@ -323,19 +332,22 @@ describe 'Merge request > User resolves diff notes and discussions', :js do end it 'updates updated text after resolving note' do - page.within first('.diff-content .note') do - find('.line-resolve-btn').click - end + page.within('.diff-content .note', match: :first) do + resolve_button = find('.line-resolve-btn') - expect(page).to have_content("Resolved by #{user.name}") + resolve_button.click + wait_for_requests + + expect(resolve_button['data-original-title']).to eq("Resolved by #{user.name}") + end end it 'shows jump to next discussion button' do - expect(page.all('.discussion-reply-holder')).to all(have_selector('.discussion-next-btn')) + expect(page.all('.discussion-reply-holder', count: 2)).to all(have_selector('.discussion-next-btn')) end it 'displays next discussion even if hidden' do - page.all('.note-discussion').each do |discussion| + page.all('.note-discussion', count: 2).each do |discussion| page.within discussion do click_button 'Toggle discussion' end diff --git a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb index 9ba9e8b9585..fdf9a84e997 100644 --- a/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb +++ b/spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb @@ -63,7 +63,7 @@ feature 'Merge request > User resolves outdated diff discussions', :js do it 'shows that as automatically resolved' do within(".discussion[data-discussion-id='#{outdated_discussion.id}']") do - expect(page).to have_css('.discussion-body', visible: false) + expect(page).not_to have_css('.discussion-body') expect(page).to have_content('Automatically resolved') end end diff --git a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb index 3b6fffb7abd..8c2599615cb 100644 --- a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb +++ b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb @@ -17,11 +17,12 @@ describe 'Merge request > User scrolls to note on load', :js do it 'scrolls note into view' do visit "#{project_merge_request_path(project, merge_request)}#{fragment_id}" + wait_for_requests + page_height = page.current_window.size[1] page_scroll_y = page.evaluate_script("window.scrollY") fragment_position_top = page.evaluate_script("Math.round($('#{fragment_id}').offset().top)") - expect(find('.js-toggle-content').visible?).to eq true expect(find(fragment_id).visible?).to eq true expect(fragment_position_top).to be >= page_scroll_y expect(fragment_position_top).to be < (page_scroll_y + page_height) @@ -35,7 +36,7 @@ describe 'Merge request > User scrolls to note on load', :js do page.execute_script "window.scrollTo(0,0)" note_element = find(fragment_id) - note_container = note_element.ancestor('.js-toggle-container') + note_container = note_element.ancestor('.js-discussion-container') expect(note_element.visible?).to eq true @@ -44,10 +45,11 @@ describe 'Merge request > User scrolls to note on load', :js do end end - it 'expands collapsed notes' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'expands collapsed notes' do visit "#{project_merge_request_path(project, merge_request)}#{collapsed_fragment_id}" note_element = find(collapsed_fragment_id) - note_container = note_element.ancestor('.js-toggle-container') + note_container = note_element.ancestor('.timeline-content') expect(note_element.visible?).to eq true expect(note_container.find('.line_content.noteable_line.old', match: :first).visible?).to eq true diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb index 9c0a04405a6..0a8296bd722 100644 --- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb +++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb @@ -35,7 +35,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do expect(page).not_to have_selector('.diff-comment-avatar-holders') end - it 'does not render avatars after commening on discussion tab' do + it 'does not render avatars after commenting on discussion tab' do click_button 'Reply...' page.within('.js-discussion-note-form') do @@ -75,7 +75,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do end end - %w(inline parallel).each do |view| + %w(parallel).each do |view| context "#{view} view" do before do visit diffs_project_merge_request_path(project, merge_request, view: view) @@ -104,7 +104,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do find('.diff-notes-collapse').send_keys(:return) end - expect(page).to have_selector('.notes_holder', visible: false) + expect(page).not_to have_selector('.notes_holder') page.within find_line(position.line_code(project.repository)) do first('img.js-diff-comment-avatar').click diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb index a9063f2bcb3..d6e7ff33d5d 100644 --- a/spec/features/merge_request/user_sees_diff_spec.rb +++ b/spec/features/merge_request/user_sees_diff_spec.rb @@ -6,20 +6,6 @@ describe 'Merge request > User sees diff', :js do let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } - context 'when visit with */* as accept header' do - it 'renders the notes' do - create :note_on_merge_request, project: project, noteable: merge_request, note: 'Rebasing with master' - - inspect_requests(inject_headers: { 'Accept' => '*/*' }) do - visit diffs_project_merge_request_path(project, merge_request) - end - - # Load notes and diff through AJAX - expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master') - expect(page).to have_css('.diffs.tab-pane.active') - end - end - context 'when linking to note' do describe 'with unresolved note' do let(:note) { create :diff_note_on_merge_request, project: project, noteable: merge_request } @@ -51,6 +37,7 @@ describe 'Merge request > User sees diff', :js do context 'when merge request has overflow' do it 'displays warning' do allow(Commit).to receive(:max_diff_options).and_return(max_files: 3) + allow_any_instance_of(DiffHelper).to receive(:render_overflow_warning?).and_return(true) visit diffs_project_merge_request_path(project, merge_request) diff --git a/spec/features/merge_request/user_sees_discussions_spec.rb b/spec/features/merge_request/user_sees_discussions_spec.rb index d6e8c8e86ba..10390bd5864 100644 --- a/spec/features/merge_request/user_sees_discussions_spec.rb +++ b/spec/features/merge_request/user_sees_discussions_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Merge request > User sees discussions' do +describe 'Merge request > User sees discussions', :js do let(:project) { create(:project, :public, :repository) } let(:user) { project.creator } let(:merge_request) { create(:merge_request, source_project: project) } @@ -53,11 +53,13 @@ describe 'Merge request > User sees discussions' do shared_examples 'a functional discussion' do let(:discussion_id) { note.discussion_id(merge_request) } - it 'is displayed' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'is displayed' do expect(page).to have_css(".discussion[data-discussion-id='#{discussion_id}']") end - it 'can be replied to' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'can be replied to' do within(".discussion[data-discussion-id='#{discussion_id}']") do click_button 'Reply...' fill_in 'note[note]', with: 'Test!' diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb index d3104b448e0..0272d300e06 100644 --- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb +++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb @@ -31,7 +31,8 @@ describe 'Merge request < User sees mini pipeline graph', :js do create(:ci_build, :manual, pipeline: pipeline, when: 'manual') end - it 'avoids repeated database queries' do + # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 + xit 'avoids repeated database queries' do before = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') } create(:ci_build, :success, :trace_artifact, pipeline: pipeline, legacy_artifacts_file: artifacts_file2) diff --git a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb index b4cda269852..d4ad0b0a377 100644 --- a/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb +++ b/spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb @@ -25,7 +25,7 @@ describe 'Merge request > User sees notes from forked project', :js do page.within('.discussion-notes') do find('.btn-text-field').click find('#note_note').send_keys('A reply comment') - find('.comment-btn').click + find('.js-comment-button').click end wait_for_requests diff --git a/spec/features/merge_request/user_sees_system_notes_spec.rb b/spec/features/merge_request/user_sees_system_notes_spec.rb index a00a682757d..c6811d4161a 100644 --- a/spec/features/merge_request/user_sees_system_notes_spec.rb +++ b/spec/features/merge_request/user_sees_system_notes_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Merge request > User sees system notes' do +describe 'Merge request > User sees system notes', :js do let(:public_project) { create(:project, :public, :repository) } let(:private_project) { create(:project, :private, :repository) } let(:user) { private_project.creator } diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb index 3a15d70979a..11e0806ba62 100644 --- a/spec/features/merge_request/user_sees_versions_spec.rb +++ b/spec/features/merge_request/user_sees_versions_spec.rb @@ -143,9 +143,9 @@ describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', - line_code: '4_4', - comment: 'Typo, please fix.' + file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', + line_code: '4_4', + comment: 'Typo, please fix.' end describe 'compare with same version' do diff --git a/spec/features/merge_request/user_uses_slash_commands_spec.rb b/spec/features/merge_request/user_uses_slash_commands_spec.rb index 7f261b580f7..83ad4b45b5a 100644 --- a/spec/features/merge_request/user_uses_slash_commands_spec.rb +++ b/spec/features/merge_request/user_uses_slash_commands_spec.rb @@ -22,16 +22,24 @@ describe 'Merge request > User uses quick actions', :js do before do project.add_master(user) - sign_in(user) - visit project_merge_request_path(project, merge_request) end describe 'time tracking' do + before do + sign_in(user) + visit project_merge_request_path(project, merge_request) + end + it_behaves_like 'issuable time tracker' end describe 'toggling the WIP prefix in the title from note' do context 'when the current user can toggle the WIP prefix' do + before do + sign_in(user) + visit project_merge_request_path(project, merge_request) + end + it 'adds the WIP: prefix to the title' do add_note("/wip") @@ -56,7 +64,6 @@ describe 'Merge request > User uses quick actions', :js do context 'when the current user cannot toggle the WIP prefix' do before do project.add_guest(guest) - sign_out(:user) sign_in(guest) visit project_merge_request_path(project, merge_request) end @@ -74,6 +81,11 @@ describe 'Merge request > User uses quick actions', :js do describe 'merging the MR from the note' do context 'when the current user can merge the MR' do + before do + sign_in(user) + visit project_merge_request_path(project, merge_request) + end + it 'merges the MR' do add_note("/merge") @@ -87,6 +99,8 @@ describe 'Merge request > User uses quick actions', :js do before do merge_request.source_branch = 'another_branch' merge_request.save + sign_in(user) + visit project_merge_request_path(project, merge_request) end it 'does not merge the MR' do @@ -101,7 +115,6 @@ describe 'Merge request > User uses quick actions', :js do context 'when the current user cannot merge the MR' do before do project.add_guest(guest) - sign_out(:user) sign_in(guest) visit project_merge_request_path(project, merge_request) end @@ -117,6 +130,11 @@ describe 'Merge request > User uses quick actions', :js do end describe 'adding a due date from note' do + before do + sign_in(user) + visit project_merge_request_path(project, merge_request) + end + it 'does not recognize the command nor create a note' do add_note('/due 2016-08-28') @@ -129,7 +147,6 @@ describe 'Merge request > User uses quick actions', :js do let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } } before do - sign_out(:user) another_project.add_master(user) sign_in(user) end @@ -161,6 +178,11 @@ describe 'Merge request > User uses quick actions', :js do describe '/target_branch command from note' do context 'when the current user can change target branch' do + before do + sign_in(user) + visit project_merge_request_path(project, merge_request) + end + it 'changes target branch from a note' do add_note("message start \n/target_branch merge-test\n message end.") @@ -184,7 +206,6 @@ describe 'Merge request > User uses quick actions', :js do context 'when current user can not change target branch' do before do project.add_guest(guest) - sign_out(:user) sign_in(guest) visit project_merge_request_path(project, merge_request) end diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index 96f6df587e1..b3bb8c48b4a 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -14,10 +14,10 @@ feature 'Member autocomplete', :js do shared_examples "open suggestions when typing @" do |resource_name| before do page.within('.new-note') do - if resource_name == 'issue' - find('#note-body').send_keys('@') - else + if resource_name == 'commit' find('#note_note').send_keys('@') + else + find('#note-body').send_keys('@') end end end diff --git a/spec/features/projects/commit/user_comments_on_commit_spec.rb b/spec/features/projects/commit/user_comments_on_commit_spec.rb new file mode 100644 index 00000000000..6397a8ad845 --- /dev/null +++ b/spec/features/projects/commit/user_comments_on_commit_spec.rb @@ -0,0 +1,109 @@ +require "spec_helper" + +describe "User comments on commit", :js do + include Spec::Support::Helpers::Features::NotesHelpers + include RepoHelpers + + let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + let(:comment_text) { "XML attached" } + + before do + sign_in(user) + project.add_developer(user) + + visit(project_commit_path(project, sample_commit.id)) + end + + context "when adding new comment" do + it "adds comment" do + emoji_code = ":+1:" + + page.within(".js-main-target-form") do + expect(page).not_to have_link("Cancel") + + fill_in("note[note]", with: "#{comment_text} #{emoji_code}") + + # Check on `Preview` tab + click_link("Preview") + + expect(find(".js-md-preview")).to have_content(comment_text).and have_css("gl-emoji") + expect(page).not_to have_css(".js-note-text") + + # Check on `Write` tab + click_link("Write") + + expect(page).to have_field("note[note]", with: "#{comment_text} #{emoji_code}") + + # Submit comment from the `Preview` tab to get rid of a separate `it` block + # which would specially tests if everything gets cleared from the note form. + click_link("Preview") + click_button("Comment") + end + + wait_for_requests + + page.within(".note") do + expect(page).to have_content(comment_text).and have_css("gl-emoji") + end + + page.within(".js-main-target-form") do + expect(page).to have_field("note[note]", with: "").and have_no_css(".js-md-preview") + end + end + end + + context "when editing comment" do + before do + add_note(comment_text) + end + + it "edits comment" do + new_comment_text = "+1 Awesome!" + + page.within(".main-notes-list") do + note = find(".note") + note.hover + + note.find(".js-note-edit").click + end + + page.find(".current-note-edit-form textarea") + + page.within(".current-note-edit-form") do + fill_in("note[note]", with: new_comment_text) + click_button("Save comment") + end + + wait_for_requests + + page.within(".note") do + expect(page).to have_content(new_comment_text) + end + end + end + + context "when deleting comment" do + before do + add_note(comment_text) + end + + it "deletes comment" do + page.within(".note") do + expect(page).to have_content(comment_text) + end + + page.within(".main-notes-list") do + note = find(".note") + note.hover + + find(".more-actions").click + find(".more-actions .dropdown-menu li", match: :first) + + accept_confirm { find(".js-note-delete").click } + end + + expect(page).not_to have_css(".note") + end + end +end diff --git a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb index e3f90a78cb5..1828b60fec7 100644 --- a/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb +++ b/spec/features/projects/merge_requests/user_comments_on_diff_spec.rb @@ -91,7 +91,7 @@ describe 'User comments on a diff', :js do # Check the same comments in the side-by-side view. execute_script("window.scrollTo(0,0);") - click_link('Side-by-side') + click_button 'Side-by-side' wait_for_requests @@ -120,7 +120,7 @@ describe 'User comments on a diff', :js do click_button('Comment') end - page.within('.diff-file:nth-of-type(5) .note') do + page.within('.diff-file:nth-of-type(5) .discussion .note') do find('.js-note-edit').click page.within('.current-note-edit-form') do @@ -131,7 +131,7 @@ describe 'User comments on a diff', :js do expect(page).not_to have_button('Save comment', disabled: true) end - page.within('.diff-file:nth-of-type(5) .note') do + page.within('.diff-file:nth-of-type(5) .discussion .note') do expect(page).to have_content('Typo, please fix').and have_no_content('Line is wrong') end end @@ -150,7 +150,7 @@ describe 'User comments on a diff', :js do expect(page).to have_content('1') end - page.within('.diff-file:nth-of-type(5) .note') do + page.within('.diff-file:nth-of-type(5) .discussion .note') do find('.more-actions').click find('.more-actions .dropdown-menu li', match: :first) diff --git a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb b/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb index 2eb652147ce..f90aaba3caf 100644 --- a/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb +++ b/spec/features/projects/merge_requests/user_comments_on_merge_request_spec.rb @@ -16,7 +16,7 @@ describe 'User comments on a merge request', :js do it 'adds a comment' do page.within('.js-main-target-form') do - fill_in(:note_note, with: '# Comment with a header') + fill_in('note[note]', with: '# Comment with a header') click_button('Comment') end @@ -32,7 +32,6 @@ describe 'User comments on a merge request', :js do # Add new comment in background in order to check # if it's going to be loaded automatically for current user. create(:diff_note_on_merge_request, project: project, noteable: merge_request, author: user, note: 'Line is wrong') - # Trigger a refresh of notes. execute_script("$(document).trigger('visibilitychange');") wait_for_requests diff --git a/spec/features/projects/merge_requests/user_views_diffs_spec.rb b/spec/features/projects/merge_requests/user_views_diffs_spec.rb index d36aafdbc54..b1bfe9e5de3 100644 --- a/spec/features/projects/merge_requests/user_views_diffs_spec.rb +++ b/spec/features/projects/merge_requests/user_views_diffs_spec.rb @@ -16,7 +16,7 @@ describe 'User views diffs', :js do it 'unfolds diffs' do first('.js-unfold').click - expect(first('.text-file')).to have_content('.bundle') + expect(find('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"] .text-file')).to have_content('.bundle') end end @@ -36,7 +36,7 @@ describe 'User views diffs', :js do context 'when in the side-by-side view' do before do - click_link('Side-by-side') + click_button 'Side-by-side' wait_for_requests end @@ -45,6 +45,14 @@ describe 'User views diffs', :js do expect(page).to have_css('.parallel') end + it 'toggles container class' do + expect(page).not_to have_css('.content-wrapper > .container-fluid.container-limited') + + click_link 'Commits' + + expect(page).to have_css('.content-wrapper > .container-fluid.container-limited') + end + include_examples 'unfold diffs' end end diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb index 7f547a4ca1f..84ec32b3fac 100644 --- a/spec/features/projects/view_on_env_spec.rb +++ b/spec/features/projects/view_on_env_spec.rb @@ -59,7 +59,9 @@ describe 'View on environment', :js do it 'has a "View on env" button' do within '.diffs' do - expect(page).to have_link('View on feature.review.example.com', href: 'http://feature.review.example.com/ruby/feature') + text = 'View on feature.review.example.com' + url = 'http://feature.review.example.com/ruby/feature' + expect(page).to have_selector("a[data-original-title='#{text}'][href='#{url}']") end end end diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb index f1ae2c7ab65..232f35c86f9 100644 --- a/spec/finders/notes_finder_spec.rb +++ b/spec/finders/notes_finder_spec.rb @@ -133,7 +133,7 @@ describe NotesFinder do it 'raises an exception for an invalid target_type' do params[:target_type] = 'invalid' - expect { described_class.new(project, user, params).execute }.to raise_error('invalid target_type') + expect { described_class.new(project, user, params).execute }.to raise_error("invalid target_type '#{params[:target_type]}'") end it 'filters out old notes' do diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json index ee5588fa6c6..38ce92a5dc7 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json @@ -109,6 +109,7 @@ "ff_only_enabled": { "type": ["boolean", false] }, "should_be_rebased": { "type": "boolean" }, "create_note_path": { "type": ["string", "null"] }, + "preview_note_path": { "type": ["string", "null"] }, "rebase_commit_sha": { "type": ["string", "null"] }, "rebase_in_progress": { "type": "boolean" }, "can_push_to_source_branch": { "type": "boolean" }, diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index ff6020c8fdd..ada26b37f4a 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, no-unused-vars, quotes, prefer-template, max-len */ +/* eslint-disable no-var, one-var, one-var-declaration-per-line, no-unused-expressions, no-unused-vars, prefer-template, max-len */ import $ from 'jquery'; import Cookies from 'js-cookie'; @@ -21,20 +21,21 @@ import '~/lib/utils/common_utils'; return setTimeout(function() { assertFn(); return done(); - // Maybe jasmine.clock here? + // Maybe jasmine.clock here? }, 333); }; describe('AwardsHandler', function() { - preloadFixtures('merge_requests/diff_comment.html.raw'); + preloadFixtures('snippets/show.html.raw'); beforeEach(function(done) { - loadFixtures('merge_requests/diff_comment.html.raw'); - $('body').attr('data-page', 'projects:merge_requests:show'); - loadAwardsHandler(true).then((obj) => { - awardsHandler = obj; - spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb()); - done(); - }).catch(fail); + loadFixtures('snippets/show.html.raw'); + loadAwardsHandler(true) + .then(obj => { + awardsHandler = obj; + spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb()); + done(); + }) + .catch(fail); let isEmojiMenuBuilt = false; openAndWaitForEmojiMenu = function() { @@ -42,7 +43,9 @@ import '~/lib/utils/common_utils'; if (isEmojiMenuBuilt) { resolve(); } else { - $('.js-add-award').eq(0).click(); + $('.js-add-award') + .eq(0) + .click(); const $menu = $('.emoji-menu'); $menu.one('build-emoji-menu-finish', () => { isEmojiMenuBuilt = true; @@ -63,7 +66,9 @@ import '~/lib/utils/common_utils'; }); describe('::showEmojiMenu', function() { it('should show emoji menu when Add emoji button clicked', function(done) { - $('.js-add-award').eq(0).click(); + $('.js-add-award') + .eq(0) + .click(); return lazyAssert(done, function() { var $emojiMenu; $emojiMenu = $('.emoji-menu'); @@ -81,7 +86,9 @@ import '~/lib/utils/common_utils'; }); }); it('should remove emoji menu when body is clicked', function(done) { - $('.js-add-award').eq(0).click(); + $('.js-add-award') + .eq(0) + .click(); return lazyAssert(done, function() { var $emojiMenu; $emojiMenu = $('.emoji-menu'); @@ -92,7 +99,9 @@ import '~/lib/utils/common_utils'; }); }); it('should not remove emoji menu when search is clicked', function(done) { - $('.js-add-award').eq(0).click(); + $('.js-add-award') + .eq(0) + .click(); return lazyAssert(done, function() { var $emojiMenu; $emojiMenu = $('.emoji-menu'); @@ -103,6 +112,7 @@ import '~/lib/utils/common_utils'; }); }); }); + describe('::addAwardToEmojiBar', function() { it('should add emoji to votes block', function() { var $emojiButton, $votesBlock; @@ -139,7 +149,9 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); $thumbsUpEmoji.attr('data-title', 'sam'); awardsHandler.userAuthored($thumbsUpEmoji); - return expect($thumbsUpEmoji.data("originalTitle")).toBe("You cannot vote on your own issue, MR and note"); + return expect($thumbsUpEmoji.data('originalTitle')).toBe( + 'You cannot vote on your own issue, MR and note', + ); }); it('should restore tooltip back to initial vote list', function() { var $thumbsUpEmoji, $votesBlock; @@ -150,12 +162,14 @@ import '~/lib/utils/common_utils'; awardsHandler.userAuthored($thumbsUpEmoji); jasmine.clock().tick(2801); jasmine.clock().uninstall(); - return expect($thumbsUpEmoji.data("originalTitle")).toBe("sam"); + return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam'); }); }); describe('::getAwardUrl', function() { return it('returns the url for request', function() { - return expect(awardsHandler.getAwardUrl()).toBe('http://test.host/frontend-fixtures/merge-requests-project/merge_requests/1/toggle_award_emoji'); + return expect(awardsHandler.getAwardUrl()).toBe( + 'http://test.host/snippets/1/toggle_award_emoji', + ); }); }); describe('::addAward and ::checkMutuality', function() { @@ -195,7 +209,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.attr('data-title', 'sam, jerry, max, and andy'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("originalTitle")).toBe('You, sam, jerry, max, and andy'); + return expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy'); }); return it('handles the special case where "You" is not cleanly comma seperated', function() { var $thumbsUpEmoji, $votesBlock, awardUrl; @@ -205,7 +219,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.attr('data-title', 'sam'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("originalTitle")).toBe('You and sam'); + return expect($thumbsUpEmoji.data('originalTitle')).toBe('You and sam'); }); }); describe('::removeYouToUserList', function() { @@ -218,7 +232,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.addClass('active'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("originalTitle")).toBe('sam, jerry, max, and andy'); + return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy'); }); return it('handles the special case where "You" is not cleanly comma seperated', function() { var $thumbsUpEmoji, $votesBlock, awardUrl; @@ -229,7 +243,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.addClass('active'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("originalTitle")).toBe('sam'); + return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam'); }); }); describe('::searchEmojis', () => { @@ -245,7 +259,7 @@ import '~/lib/utils/common_utils'; expect($('.js-emoji-menu-search').val()).toBe('ali'); }) .then(done) - .catch((err) => { + .catch(err => { done.fail(`Failed to open and build emoji menu: ${err.message}`); }); }); @@ -263,7 +277,7 @@ import '~/lib/utils/common_utils'; expect($('.js-emoji-menu-search').val()).toBe(''); }) .then(done) - .catch((err) => { + .catch(err => { done.fail(`Failed to open and build emoji menu: ${err.message}`); }); }); @@ -272,37 +286,40 @@ import '~/lib/utils/common_utils'; describe('emoji menu', function() { const emojiSelector = '[data-name="sunglasses"]'; const openEmojiMenuAndAddEmoji = function() { - return openAndWaitForEmojiMenu() - .then(() => { - const $menu = $('.emoji-menu'); - const $block = $('.js-awards-block'); - const $emoji = $menu.find('.emoji-menu-list:not(.frequent-emojis) ' + emojiSelector); + return openAndWaitForEmojiMenu().then(() => { + const $menu = $('.emoji-menu'); + const $block = $('.js-awards-block'); + const $emoji = $menu.find('.emoji-menu-list:not(.frequent-emojis) ' + emojiSelector); - expect($emoji.length).toBe(1); - expect($block.find(emojiSelector).length).toBe(0); - $emoji.click(); - expect($menu.hasClass('.is-visible')).toBe(false); - expect($block.find(emojiSelector).length).toBe(1); - }); + expect($emoji.length).toBe(1); + expect($block.find(emojiSelector).length).toBe(0); + $emoji.click(); + expect($menu.hasClass('.is-visible')).toBe(false); + expect($block.find(emojiSelector).length).toBe(1); + }); }; it('should add selected emoji to awards block', function(done) { return openEmojiMenuAndAddEmoji() .then(done) - .catch((err) => { + .catch(err => { done.fail(`Failed to open and build emoji menu: ${err.message}`); }); }); it('should remove already selected emoji', function(done) { return openEmojiMenuAndAddEmoji() .then(() => { - $('.js-add-award').eq(0).click(); + $('.js-add-award') + .eq(0) + .click(); const $block = $('.js-awards-block'); - const $emoji = $('.emoji-menu').find(`.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`); + const $emoji = $('.emoji-menu').find( + `.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`, + ); $emoji.click(); expect($block.find(emojiSelector).length).toBe(0); }) .then(done) - .catch((err) => { + .catch(err => { done.fail(`Failed to open and build emoji menu: ${err.message}`); }); }); @@ -318,12 +335,12 @@ import '~/lib/utils/common_utils'; return openAndWaitForEmojiMenu() .then(() => { const emojiMenu = document.querySelector('.emoji-menu'); - Array.prototype.forEach.call(emojiMenu.querySelectorAll('.emoji-menu-title'), (title) => { + Array.prototype.forEach.call(emojiMenu.querySelectorAll('.emoji-menu-title'), title => { expect(title.textContent.trim().toLowerCase()).not.toBe('frequently used'); }); }) .then(done) - .catch((err) => { + .catch(err => { done.fail(`Failed to open and build emoji menu: ${err.message}`); }); }); @@ -334,14 +351,15 @@ import '~/lib/utils/common_utils'; return openAndWaitForEmojiMenu() .then(() => { const emojiMenu = document.querySelector('.emoji-menu'); - const hasFrequentlyUsedHeading = Array.prototype.some.call(emojiMenu.querySelectorAll('.emoji-menu-title'), title => - title.textContent.trim().toLowerCase() === 'frequently used' + const hasFrequentlyUsedHeading = Array.prototype.some.call( + emojiMenu.querySelectorAll('.emoji-menu-title'), + title => title.textContent.trim().toLowerCase() === 'frequently used', ); expect(hasFrequentlyUsedHeading).toBe(true); }) .then(done) - .catch((err) => { + .catch(err => { done.fail(`Failed to open and build emoji menu: ${err.message}`); }); }); @@ -361,4 +379,4 @@ import '~/lib/utils/common_utils'; }); }); }); -}).call(window); +}.call(window)); diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js index d03836d10f9..d8aa5c636da 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -4,12 +4,11 @@ import '~/behaviors/quick_submit'; describe('Quick Submit behavior', function () { const keydownEvent = (options = { keyCode: 13, metaKey: true }) => $.Event('keydown', options); - preloadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + preloadFixtures('snippets/show.html.raw'); beforeEach(() => { - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - $('body').attr('data-page', 'projects:merge_requests:show'); - $('form').submit((e) => { + loadFixtures('snippets/show.html.raw'); + $('form').submit(e => { // Prevent a form submit from moving us off the testing page e.preventDefault(); }); @@ -26,24 +25,30 @@ describe('Quick Submit behavior', function () { }); it('does not respond to other keyCodes', () => { - this.textarea.trigger(keydownEvent({ - keyCode: 32, - })); + this.textarea.trigger( + keydownEvent({ + keyCode: 32, + }), + ); expect(this.spies.submit).not.toHaveBeenTriggered(); }); it('does not respond to Enter alone', () => { - this.textarea.trigger(keydownEvent({ - ctrlKey: false, - metaKey: false, - })); + this.textarea.trigger( + keydownEvent({ + ctrlKey: false, + metaKey: false, + }), + ); expect(this.spies.submit).not.toHaveBeenTriggered(); }); it('does not respond to repeated events', () => { - this.textarea.trigger(keydownEvent({ - repeat: true, - })); + this.textarea.trigger( + keydownEvent({ + repeat: true, + }), + ); expect(this.spies.submit).not.toHaveBeenTriggered(); }); @@ -83,15 +88,21 @@ describe('Quick Submit behavior', function () { }); it('excludes other modifier keys', () => { - this.textarea.trigger(keydownEvent({ - altKey: true, - })); - this.textarea.trigger(keydownEvent({ - ctrlKey: true, - })); - this.textarea.trigger(keydownEvent({ - shiftKey: true, - })); + this.textarea.trigger( + keydownEvent({ + altKey: true, + }), + ); + this.textarea.trigger( + keydownEvent({ + ctrlKey: true, + }), + ); + this.textarea.trigger( + keydownEvent({ + shiftKey: true, + }), + ); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); }); @@ -102,15 +113,21 @@ describe('Quick Submit behavior', function () { }); it('excludes other modifier keys', () => { - this.textarea.trigger(keydownEvent({ - altKey: true, - })); - this.textarea.trigger(keydownEvent({ - metaKey: true, - })); - this.textarea.trigger(keydownEvent({ - shiftKey: true, - })); + this.textarea.trigger( + keydownEvent({ + altKey: true, + }), + ); + this.textarea.trigger( + keydownEvent({ + metaKey: true, + }), + ); + this.textarea.trigger( + keydownEvent({ + shiftKey: true, + }), + ); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); } diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/app_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/changed_files_dropdown_spec.js b/spec/javascripts/diffs/components/changed_files_dropdown_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/changed_files_dropdown_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/changed_files_spec.js b/spec/javascripts/diffs/components/changed_files_spec.js new file mode 100644 index 00000000000..2d57af6137c --- /dev/null +++ b/spec/javascripts/diffs/components/changed_files_spec.js @@ -0,0 +1,100 @@ +import Vue from 'vue'; +import $ from 'jquery'; +import { mountComponentWithStore } from 'spec/helpers'; +import store from '~/diffs/store'; +import ChangedFiles from '~/diffs/components/changed_files.vue'; + +describe('ChangedFiles', () => { + const Component = Vue.extend(ChangedFiles); + const createComponent = props => mountComponentWithStore(Component, { props, store }); + let vm; + + beforeEach(() => { + setFixtures(` +
+
+ `); + const props = { + diffFiles: [ + { + addedLines: 10, + removedLines: 20, + blob: { + path: 'some/code.txt', + }, + filePath: 'some/code.txt', + }, + ], + }; + vm = createComponent(props); + }); + + describe('with single file added', () => { + it('shows files changes', () => { + expect(vm.$el).toContainText('1 changed file'); + }); + + it('shows file additions and deletions', () => { + expect(vm.$el).toContainText('10 additions'); + expect(vm.$el).toContainText('20 deletions'); + }); + }); + + describe('template', () => { + describe('diff view mode buttons', () => { + let inlineButton; + let parallelButton; + + beforeEach(() => { + inlineButton = vm.$el.querySelector('.js-inline-diff-button'); + parallelButton = vm.$el.querySelector('.js-parallel-diff-button'); + }); + + it('should have Inline and Side-by-side buttons', () => { + expect(inlineButton).toBeDefined(); + expect(parallelButton).toBeDefined(); + }); + + it('should add active class to Inline button', done => { + vm.$store.state.diffs.diffViewType = 'inline'; + + vm.$nextTick(() => { + expect(inlineButton.classList.contains('active')).toEqual(true); + expect(parallelButton.classList.contains('active')).toEqual(false); + + done(); + }); + }); + + it('should toggle active state of buttons when diff view type changed', done => { + vm.$store.state.diffs.diffViewType = 'parallel'; + + vm.$nextTick(() => { + expect(inlineButton.classList.contains('active')).toEqual(false); + expect(parallelButton.classList.contains('active')).toEqual(true); + + done(); + }); + }); + + describe('clicking them', () => { + it('should toggle the diff view type', done => { + $(parallelButton).click(); + + vm.$nextTick(() => { + expect(inlineButton.classList.contains('active')).toEqual(false); + expect(parallelButton.classList.contains('active')).toEqual(true); + + $(inlineButton).click(); + + vm.$nextTick(() => { + expect(inlineButton.classList.contains('active')).toEqual(true); + expect(parallelButton.classList.contains('active')).toEqual(false); + done(); + }); + }); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js b/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/compare_versions_spec.js b/spec/javascripts/diffs/components/compare_versions_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/compare_versions_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/diff_content_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/diff_discussions_spec.js b/spec/javascripts/diffs/components/diff_discussions_spec.js new file mode 100644 index 00000000000..270f363825f --- /dev/null +++ b/spec/javascripts/diffs/components/diff_discussions_spec.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import DiffDiscussions from '~/diffs/components/diff_discussions.vue'; +import store from '~/mr_notes/stores'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import discussionsMockData from '../mock_data/diff_discussions'; + +describe('DiffDiscussions', () => { + let component; + const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)]; + + beforeEach(() => { + component = createComponentWithStore(Vue.extend(DiffDiscussions), store, { + discussions: getDiscussionsMockData(), + }).$mount(); + }); + + describe('template', () => { + it('should have notes list', () => { + const { $el } = component; + + expect($el.querySelectorAll('.discussion .note.timeline-entry').length).toEqual(5); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js new file mode 100644 index 00000000000..d0f1700bee6 --- /dev/null +++ b/spec/javascripts/diffs/components/diff_file_header_spec.js @@ -0,0 +1,433 @@ +import Vue from 'vue'; +import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +const discussionFixture = 'merge_requests/diff_discussion.json'; + +describe('diff_file_header', () => { + let vm; + let props; + const Component = Vue.extend(DiffFileHeader); + + beforeEach(() => { + const diffDiscussionMock = getJSONFixture(discussionFixture)[0]; + const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file, { deep: true }); + props = { + diffFile, + currentUser: {}, + }; + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('computed', () => { + describe('icon', () => { + beforeEach(() => { + props.diffFile.blob.icon = 'dummy icon'; + }); + + it('returns the blob icon for files', () => { + props.diffFile.submodule = false; + + vm = mountComponent(Component, props); + + expect(vm.icon).toBe(props.diffFile.blob.icon); + }); + + it('returns the archive icon for submodules', () => { + props.diffFile.submodule = true; + + vm = mountComponent(Component, props); + + expect(vm.icon).toBe('archive'); + }); + }); + + describe('titleLink', () => { + beforeEach(() => { + Object.assign(props.diffFile, { + fileHash: 'badc0ffee', + submoduleLink: 'link://to/submodule', + submoduleTreeUrl: 'some://tree/url', + }); + }); + + it('returns the fileHash for files', () => { + props.diffFile.submodule = false; + + vm = mountComponent(Component, props); + + expect(vm.titleLink).toBe(`#${props.diffFile.fileHash}`); + }); + + it('returns the submoduleTreeUrl for submodules', () => { + props.diffFile.submodule = true; + + vm = mountComponent(Component, props); + + expect(vm.titleLink).toBe(props.diffFile.submoduleTreeUrl); + }); + + it('returns the submoduleLink for submodules without submoduleTreeUrl', () => { + Object.assign(props.diffFile, { + submodule: true, + submoduleTreeUrl: null, + }); + + vm = mountComponent(Component, props); + + expect(vm.titleLink).toBe(props.diffFile.submoduleLink); + }); + }); + + describe('filePath', () => { + beforeEach(() => { + Object.assign(props.diffFile, { + blob: { id: 'b10b1db10b1d' }, + filePath: 'path/to/file', + }); + }); + + it('returns the filePath for files', () => { + props.diffFile.submodule = false; + + vm = mountComponent(Component, props); + + expect(vm.filePath).toBe(props.diffFile.filePath); + }); + + it('appends the truncated blob id for submodules', () => { + props.diffFile.submodule = true; + + vm = mountComponent(Component, props); + + expect(vm.filePath).toBe( + `${props.diffFile.filePath} @ ${props.diffFile.blob.id.substr(0, 8)}`, + ); + }); + }); + + describe('titleTag', () => { + it('returns a link tag if fileHash is set', () => { + props.diffFile.fileHash = 'some hash'; + + vm = mountComponent(Component, props); + + expect(vm.titleTag).toBe('a'); + }); + + it('returns a span tag if fileHash is not set', () => { + props.diffFile.fileHash = null; + + vm = mountComponent(Component, props); + + expect(vm.titleTag).toBe('span'); + }); + }); + + describe('isUsingLfs', () => { + beforeEach(() => { + Object.assign(props.diffFile, { + storedExternally: true, + externalStorage: 'lfs', + }); + }); + + it('returns true if file is stored in LFS', () => { + vm = mountComponent(Component, props); + + expect(vm.isUsingLfs).toBe(true); + }); + + it('returns false if file is not stored externally', () => { + props.diffFile.storedExternally = false; + + vm = mountComponent(Component, props); + + expect(vm.isUsingLfs).toBe(false); + }); + + it('returns false if file is not stored in LFS', () => { + props.diffFile.externalStorage = 'not lfs'; + + vm = mountComponent(Component, props); + + expect(vm.isUsingLfs).toBe(false); + }); + }); + + describe('collapseIcon', () => { + it('returns chevron-down if the diff is expanded', () => { + props.expanded = true; + + vm = mountComponent(Component, props); + + expect(vm.collapseIcon).toBe('chevron-down'); + }); + + it('returns chevron-right if the diff is collapsed', () => { + props.expanded = false; + + vm = mountComponent(Component, props); + + expect(vm.collapseIcon).toBe('chevron-right'); + }); + }); + + describe('isDiscussionsExpanded', () => { + beforeEach(() => { + Object.assign(props, { + discussionsExpanded: true, + expanded: true, + }); + }); + + it('returns true if diff and discussion are expanded', () => { + vm = mountComponent(Component, props); + + expect(vm.isDiscussionsExpanded).toBe(true); + }); + + it('returns false if discussion is collapsed', () => { + props.discussionsExpanded = false; + + vm = mountComponent(Component, props); + + expect(vm.isDiscussionsExpanded).toBe(false); + }); + + it('returns false if diff is collapsed', () => { + props.expanded = false; + + vm = mountComponent(Component, props); + + expect(vm.isDiscussionsExpanded).toBe(false); + }); + }); + + describe('viewFileButtonText', () => { + it('contains the truncated content SHA', () => { + const dummySha = 'deebd00f is no SHA'; + props.diffFile.contentSha = dummySha; + + vm = mountComponent(Component, props); + + expect(vm.viewFileButtonText).not.toContain(dummySha); + expect(vm.viewFileButtonText).toContain(dummySha.substr(0, 8)); + }); + }); + + describe('viewReplacedFileButtonText', () => { + it('contains the truncated base SHA', () => { + const dummySha = 'deadabba sings no more'; + props.diffFile.diffRefs.baseSha = dummySha; + + vm = mountComponent(Component, props); + + expect(vm.viewReplacedFileButtonText).not.toContain(dummySha); + expect(vm.viewReplacedFileButtonText).toContain(dummySha.substr(0, 8)); + }); + }); + }); + + describe('methods', () => { + describe('handleToggle', () => { + beforeEach(() => { + spyOn(vm, '$emit').and.stub(); + }); + + it('emits toggleFile if checkTarget is false', () => { + vm.handleToggle(null, false); + + expect(vm.$emit).toHaveBeenCalledWith('toggleFile'); + }); + + it('emits toggleFile if checkTarget is true and event target is header', () => { + vm.handleToggle({ target: vm.$refs.header }, true); + + expect(vm.$emit).toHaveBeenCalledWith('toggleFile'); + }); + + it('does not emit toggleFile if checkTarget is true and event target is not header', () => { + vm.handleToggle({ target: 'not header' }, true); + + expect(vm.$emit).not.toHaveBeenCalled(); + }); + }); + }); + + describe('template', () => { + describe('collapse toggle', () => { + const collapseToggle = () => vm.$el.querySelector('.diff-toggle-caret'); + + it('is visible if collapsible is true', () => { + props.collapsible = true; + + vm = mountComponent(Component, props); + + expect(collapseToggle()).not.toBe(null); + }); + + it('is hidden if collapsible is false', () => { + props.collapsible = false; + + vm = mountComponent(Component, props); + + expect(collapseToggle()).toBe(null); + }); + }); + + it('displays an icon in the title', () => { + vm = mountComponent(Component, props); + + const icon = vm.$el.querySelector(`i[class="fa fa-fw fa-${vm.icon}"]`); + expect(icon).not.toBe(null); + }); + + describe('file paths', () => { + const filePaths = () => vm.$el.querySelectorAll('.file-title-name'); + + it('displays the path of a added file', () => { + props.diffFile.renamedFile = false; + + vm = mountComponent(Component, props); + + expect(filePaths()).toHaveLength(1); + expect(filePaths()[0]).toHaveText(props.diffFile.filePath); + }); + + it('displays path for deleted file', () => { + props.diffFile.renamedFile = false; + props.diffFile.deletedFile = true; + + vm = mountComponent(Component, props); + + expect(filePaths()).toHaveLength(1); + expect(filePaths()[0]).toHaveText(`${props.diffFile.filePath} deleted`); + }); + + it('displays old and new path if the file was renamed', () => { + props.diffFile.renamedFile = true; + + vm = mountComponent(Component, props); + + expect(filePaths()).toHaveLength(2); + expect(filePaths()[0]).toHaveText(props.diffFile.oldPath); + expect(filePaths()[1]).toHaveText(props.diffFile.newPath); + }); + }); + + it('displays a copy to clipboard button', () => { + vm = mountComponent(Component, props); + + const button = vm.$el.querySelector('.btn-clipboard'); + expect(button).not.toBe(null); + expect(button.dataset.clipboardText).toBe(props.diffFile.filePath); + }); + + describe('file mode', () => { + it('it displays old and new file mode if it changed', () => { + props.diffFile.modeChanged = true; + + vm = mountComponent(Component, props); + + const { fileMode } = vm.$refs; + expect(fileMode).not.toBe(undefined); + expect(fileMode).toContainText(props.diffFile.aMode); + expect(fileMode).toContainText(props.diffFile.bMode); + }); + + it('does not display the file mode if it has not changed', () => { + props.diffFile.modeChanged = false; + + vm = mountComponent(Component, props); + + const { fileMode } = vm.$refs; + expect(fileMode).toBe(undefined); + }); + }); + + describe('LFS label', () => { + const lfsLabel = () => vm.$el.querySelector('.label-lfs'); + + it('displays the LFS label for files stored in LFS', () => { + Object.assign(props.diffFile, { + storedExternally: true, + externalStorage: 'lfs', + }); + + vm = mountComponent(Component, props); + + expect(lfsLabel()).not.toBe(null); + expect(lfsLabel()).toHaveText('LFS'); + }); + + it('does not display the LFS label for files stored in repository', () => { + props.diffFile.storedExternally = false; + + vm = mountComponent(Component, props); + + expect(lfsLabel()).toBe(null); + }); + }); + + describe('edit button', () => { + it('should not render edit button if addMergeRequestButtons is not true', () => { + vm = mountComponent(Component, props); + + expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null); + }); + + it('should show edit button when file is editable', () => { + props.addMergeRequestButtons = true; + props.diffFile.editPath = '/'; + vm = mountComponent(Component, props); + + expect(vm.$el.querySelector('.js-edit-blob')).toContainText('Edit'); + }); + + it('should not show edit button when file is deleted', () => { + props.addMergeRequestButtons = true; + props.diffFile.deletedFile = true; + props.diffFile.editPath = '/'; + vm = mountComponent(Component, props); + + expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null); + }); + }); + + describe('addMergeRequestButtons', () => { + beforeEach(() => { + props.addMergeRequestButtons = true; + props.diffFile.editPath = ''; + }); + + describe('view on environment button', () => { + const url = 'some.external.url/'; + const title = 'url.title'; + + it('displays link to external url', () => { + props.diffFile.externalUrl = url; + props.diffFile.formattedExternalUrl = title; + + vm = mountComponent(Component, props); + + expect(vm.$el.querySelector(`a[href="${url}"]`)).not.toBe(null); + expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).not.toBe(null); + }); + + it('hides link if no external url', () => { + props.diffFile.externalUrl = ''; + props.diffFile.formattedExternalUrl = title; + + vm = mountComponent(Component, props); + + expect(vm.$el.querySelector(`a[data-original-title="View on ${title}"]`)).toBe(null); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js new file mode 100644 index 00000000000..1c1edfac68c --- /dev/null +++ b/spec/javascripts/diffs/components/diff_file_spec.js @@ -0,0 +1,88 @@ +import Vue from 'vue'; +import DiffFileComponent from '~/diffs/components/diff_file.vue'; +import store from '~/mr_notes/stores'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import diffFileMockData from '../mock_data/diff_file'; + +describe('DiffFile', () => { + let vm; + const getDiffFileMock = () => Object.assign({}, diffFileMockData); + + beforeEach(() => { + vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, { + file: getDiffFileMock(), + currentUser: {}, + }).$mount(); + }); + + describe('template', () => { + it('should render component with file header, file content components', () => { + const el = vm.$el; + const { fileHash, filePath } = diffFileMockData; + + expect(el.id).toEqual(fileHash); + expect(el.classList.contains('diff-file')).toEqual(true); + expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0); + expect(el.querySelector('.js-file-title')).toBeDefined(); + expect(el.querySelector('.file-title-name').innerText.indexOf(filePath) > -1).toEqual(true); + expect(el.querySelector('.js-syntax-highlight')).toBeDefined(); + expect(el.querySelectorAll('.line_content').length > 5).toEqual(true); + }); + + describe('collapsed', () => { + it('should not have file content', done => { + expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(0); + expect(vm.file.collapsed).toEqual(false); + vm.file.collapsed = true; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.diff-content.hidden').length).toEqual(1); + + done(); + }); + }); + + it('should have collapsed text and link', done => { + vm.file.collapsed = true; + + vm.$nextTick(() => { + expect(vm.$el.innerText).toContain('This diff is collapsed'); + expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1); + + done(); + }); + }); + + it('should have loading icon while loading a collapsed diffs', done => { + vm.file.collapsed = true; + vm.isLoadingCollapsedDiff = true; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.diff-content.loading').length).toEqual(1); + + done(); + }); + }); + }); + }); + + describe('too large diff', () => { + it('should have too large warning and blob link', done => { + const BLOB_LINK = '/file/view/path'; + vm.file.tooLarge = true; + vm.file.viewPath = BLOB_LINK; + + vm.$nextTick(() => { + expect(vm.$el.innerText).toContain( + 'This source diff could not be displayed because it is too large', + ); + expect(vm.$el.querySelector('.js-too-large-diff')).toBeDefined(); + expect(vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK) > -1).toEqual( + true, + ); + + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js new file mode 100644 index 00000000000..0085a16815a --- /dev/null +++ b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js @@ -0,0 +1,115 @@ +import Vue from 'vue'; +import DiffGutterAvatarsComponent from '~/diffs/components/diff_gutter_avatars.vue'; +import { COUNT_OF_AVATARS_IN_GUTTER } from '~/diffs/constants'; +import store from '~/mr_notes/stores'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import discussionsMockData from '../mock_data/diff_discussions'; + +describe('DiffGutterAvatars', () => { + let component; + const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)]; + + beforeEach(() => { + component = createComponentWithStore(Vue.extend(DiffGutterAvatarsComponent), store, { + discussions: getDiscussionsMockData(), + }).$mount(); + }); + + describe('computed', () => { + describe('discussionsExpanded', () => { + it('should return true when all discussions are expanded', () => { + expect(component.discussionsExpanded).toEqual(true); + }); + + it('should return false when all discussions are not expanded', () => { + component.discussions[0].expanded = false; + expect(component.discussionsExpanded).toEqual(false); + }); + }); + + describe('allDiscussions', () => { + it('should return an array of notes', () => { + expect(component.allDiscussions).toEqual([...component.discussions[0].notes]); + }); + }); + + describe('notesInGutter', () => { + it('should return a subset of discussions to show in gutter', () => { + expect(component.notesInGutter.length).toEqual(COUNT_OF_AVATARS_IN_GUTTER); + expect(component.notesInGutter[0]).toEqual({ + note: component.discussions[0].notes[0].note, + author: component.discussions[0].notes[0].author, + }); + }); + }); + + describe('moreCount', () => { + it('should return count of remaining discussions from gutter', () => { + expect(component.moreCount).toEqual(2); + }); + }); + + describe('moreText', () => { + it('should return proper text if moreCount > 0', () => { + expect(component.moreText).toEqual('2 more comments'); + }); + + it('should return empty string if there is no discussion', () => { + component.discussions = []; + expect(component.moreText).toEqual(''); + }); + }); + }); + + describe('methods', () => { + describe('getTooltipText', () => { + it('should return original comment if it is shorter than max length', () => { + const note = component.discussions[0].notes[0]; + + expect(component.getTooltipText(note)).toEqual('Administrator: comment 1'); + }); + + it('should return truncated version of comment', () => { + const note = component.discussions[0].notes[1]; + + expect(component.getTooltipText(note)).toEqual('Fatih Acet: comment 2 is r...'); + }); + }); + + describe('toggleDiscussions', () => { + it('should toggle all discussions', () => { + expect(component.discussions[0].expanded).toEqual(true); + + component.$store.dispatch('setInitialNotes', getDiscussionsMockData()); + component.discussions = component.$store.state.notes.discussions; + component.toggleDiscussions(); + + expect(component.discussions[0].expanded).toEqual(false); + component.$store.dispatch('setInitialNotes', []); + }); + }); + }); + + describe('template', () => { + const buttonSelector = '.js-diff-comment-button'; + const svgSelector = `${buttonSelector} svg`; + const avatarSelector = '.js-diff-comment-avatar'; + const plusCountSelector = '.js-diff-comment-plus'; + + it('should have button to collapse discussions when the discussions expanded', () => { + expect(component.$el.querySelector(buttonSelector)).toBeDefined(); + expect(component.$el.querySelector(svgSelector)).toBeDefined(); + }); + + it('should have user avatars when discussions collapsed', () => { + component.discussions[0].expanded = false; + + Vue.nextTick(() => { + expect(component.$el.querySelector(buttonSelector)).toBeNull(); + expect(component.$el.querySelectorAll(avatarSelector).length).toEqual(4); + expect(component.$el.querySelector(plusCountSelector)).toBeDefined(); + expect(component.$el.querySelector(plusCountSelector).textContent).toEqual('+2'); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js new file mode 100644 index 00000000000..312a684f4d2 --- /dev/null +++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js @@ -0,0 +1,153 @@ +import Vue from 'vue'; +import DiffLineGutterContent from '~/diffs/components/diff_line_gutter_content.vue'; +import store from '~/mr_notes/stores'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { + MATCH_LINE_TYPE, + CONTEXT_LINE_TYPE, + OLD_NO_NEW_LINE_TYPE, + NEW_NO_NEW_LINE_TYPE, +} from '~/diffs/constants'; +import discussionsMockData from '../mock_data/diff_discussions'; +import diffFileMockData from '../mock_data/diff_file'; + +describe('DiffLineGutterContent', () => { + const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)]; + const getDiffFileMock = () => Object.assign({}, diffFileMockData); + const createComponent = (options = {}) => { + const cmp = Vue.extend(DiffLineGutterContent); + const props = Object.assign({}, options); + props.fileHash = getDiffFileMock().fileHash; + props.contextLinesPath = '/context/lines/path'; + + return createComponentWithStore(cmp, store, props).$mount(); + }; + const setDiscussions = component => { + component.$store.dispatch('setInitialNotes', getDiscussionsMockData()); + }; + + const resetDiscussions = component => { + component.$store.dispatch('setInitialNotes', []); + }; + + describe('computed', () => { + describe('isMatchLine', () => { + it('should return true for match line type', () => { + const component = createComponent({ lineType: MATCH_LINE_TYPE }); + expect(component.isMatchLine).toEqual(true); + }); + + it('should return false for non-match line type', () => { + const component = createComponent({ lineType: CONTEXT_LINE_TYPE }); + expect(component.isMatchLine).toEqual(false); + }); + }); + + describe('isContextLine', () => { + it('should return true for context line type', () => { + const component = createComponent({ lineType: CONTEXT_LINE_TYPE }); + expect(component.isContextLine).toEqual(true); + }); + + it('should return false for non-context line type', () => { + const component = createComponent({ lineType: MATCH_LINE_TYPE }); + expect(component.isContextLine).toEqual(false); + }); + }); + + describe('isMetaLine', () => { + it('should return true for meta line type', () => { + const component = createComponent({ lineType: NEW_NO_NEW_LINE_TYPE }); + expect(component.isMetaLine).toEqual(true); + + const component2 = createComponent({ lineType: OLD_NO_NEW_LINE_TYPE }); + expect(component2.isMetaLine).toEqual(true); + }); + + it('should return false for non-meta line type', () => { + const component = createComponent({ lineType: MATCH_LINE_TYPE }); + expect(component.isMetaLine).toEqual(false); + }); + }); + + describe('lineHref', () => { + it('should prepend # to lineCode', () => { + const lineCode = 'LC_42'; + const component = createComponent({ lineCode }); + expect(component.lineHref).toEqual(`#${lineCode}`); + }); + + it('should return # if there is no lineCode', () => { + const component = createComponent({ lineCode: null }); + expect(component.lineHref).toEqual('#'); + }); + }); + + describe('discussions, hasDiscussions, shouldShowAvatarsOnGutter', () => { + it('should return empty array when there is no discussion', () => { + const component = createComponent({ lineCode: 'LC_42' }); + expect(component.discussions).toEqual([]); + expect(component.hasDiscussions).toEqual(false); + expect(component.shouldShowAvatarsOnGutter).toEqual(false); + }); + + it('should return discussions for the given lineCode', () => { + const lineCode = getDiffFileMock().highlightedDiffLines[1].lineCode; + const component = createComponent({ lineCode, showCommentButton: true }); + + setDiscussions(component); + + expect(component.discussions).toEqual(getDiscussionsMockData()); + expect(component.hasDiscussions).toEqual(true); + expect(component.shouldShowAvatarsOnGutter).toEqual(true); + + resetDiscussions(component); + }); + }); + }); + + describe('template', () => { + it('should render three dots for context lines', () => { + const component = createComponent({ + lineType: MATCH_LINE_TYPE, + }); + + expect(component.$el.querySelector('span').classList.contains('context-cell')).toEqual(true); + expect(component.$el.innerText).toEqual('...'); + }); + + it('should render comment button', () => { + const component = createComponent({ + showCommentButton: true, + }); + Object.defineProperty(component, 'isLoggedIn', { + get() { + return true; + }, + }); + + expect(component.$el.querySelector('.js-add-diff-note-button')).toBeDefined(); + }); + + it('should render line link', () => { + const lineNumber = 42; + const lineCode = `LC_${lineNumber}`; + const component = createComponent({ lineNumber, lineCode }); + const link = component.$el.querySelector('a'); + + expect(link.href.indexOf(`#${lineCode}`) > -1).toEqual(true); + expect(link.dataset.linenumber).toEqual(lineNumber.toString()); + }); + + it('should render user avatars', () => { + const component = createComponent({ + showCommentButton: true, + lineCode: getDiffFileMock().highlightedDiffLines[1].lineCode, + }); + + setDiscussions(component); + expect(component.$el.querySelector('.diff-comment-avatar-holders')).toBeDefined(); + resetDiscussions(component); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js new file mode 100644 index 00000000000..724d1948214 --- /dev/null +++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js @@ -0,0 +1,68 @@ +import Vue from 'vue'; +import DiffLineNoteForm from '~/diffs/components/diff_line_note_form.vue'; +import store from '~/mr_notes/stores'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import diffFileMockData from '../mock_data/diff_file'; + +describe('DiffLineNoteForm', () => { + let component; + let diffFile; + let diffLines; + const getDiffFileMock = () => Object.assign({}, diffFileMockData); + + beforeEach(() => { + diffFile = getDiffFileMock(); + diffLines = diffFile.highlightedDiffLines; + + component = createComponentWithStore(Vue.extend(DiffLineNoteForm), store, { + diffFile, + diffLines, + line: diffLines[0], + noteTargetLine: diffLines[0], + }).$mount(); + }); + + describe('methods', () => { + describe('handleCancelCommentForm', () => { + it('should call cancelCommentForm with lineCode', () => { + spyOn(component, 'cancelCommentForm'); + component.handleCancelCommentForm(); + + expect(component.cancelCommentForm).toHaveBeenCalledWith({ + lineCode: diffLines[0].lineCode, + }); + }); + }); + + describe('saveNoteForm', () => { + it('should call saveNote action with proper params', done => { + let isPromiseCalled = false; + const formDataSpy = spyOnDependency(DiffLineNoteForm, 'getNoteFormData').and.returnValue({ + postData: 1, + }); + const saveNoteSpy = spyOn(component, 'saveNote').and.returnValue( + new Promise(() => { + isPromiseCalled = true; + done(); + }), + ); + + component.handleSaveNote('note body'); + + expect(formDataSpy).toHaveBeenCalled(); + expect(saveNoteSpy).toHaveBeenCalled(); + expect(isPromiseCalled).toEqual(true); + }); + }); + }); + + describe('template', () => { + it('should have note form', () => { + const { $el } = component; + + expect($el.querySelector('.js-vue-textarea')).toBeDefined(); + expect($el.querySelector('.js-vue-issue-save')).toBeDefined(); + expect($el.querySelector('.js-vue-markdown-field')).toBeDefined(); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/edit_button_spec.js b/spec/javascripts/diffs/components/edit_button_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/edit_button_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/hidden_files_warning_spec.js b/spec/javascripts/diffs/components/hidden_files_warning_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/hidden_files_warning_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js new file mode 100644 index 00000000000..0d5a3576204 --- /dev/null +++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js @@ -0,0 +1,111 @@ +import Vue from 'vue'; +import InlineDiffView from '~/diffs/components/inline_diff_view.vue'; +import store from '~/mr_notes/stores'; +import * as constants from '~/diffs/constants'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import diffFileMockData from '../mock_data/diff_file'; +import discussionsMockData from '../mock_data/diff_discussions'; + +describe('InlineDiffView', () => { + let component; + const getDiffFileMock = () => Object.assign({}, diffFileMockData); + const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)]; + + beforeEach(() => { + const diffFile = getDiffFileMock(); + + component = createComponentWithStore(Vue.extend(InlineDiffView), store, { + diffFile, + diffLines: diffFile.highlightedDiffLines, + }).$mount(); + }); + + describe('methods', () => { + describe('handleMouse', () => { + it('should set hoveredLineCode', () => { + expect(component.hoveredLineCode).toEqual(null); + + component.handleMouse('lineCode1', true); + expect(component.hoveredLineCode).toEqual('lineCode1'); + + component.handleMouse('lineCode1', false); + expect(component.hoveredLineCode).toEqual(null); + }); + }); + + describe('getLineClass', () => { + it('should return line class object', () => { + const { LINE_HOVER_CLASS_NAME, LINE_UNFOLD_CLASS_NAME } = constants; + const { MATCH_LINE_TYPE, NEW_LINE_TYPE } = constants; + + expect(component.getLineClass(component.diffLines[0])).toEqual({ + [NEW_LINE_TYPE]: NEW_LINE_TYPE, + [LINE_UNFOLD_CLASS_NAME]: false, + [LINE_HOVER_CLASS_NAME]: false, + }); + + component.handleMouse(component.diffLines[0].lineCode, true); + Object.defineProperty(component, 'isLoggedIn', { + get() { + return true; + }, + }); + + expect(component.getLineClass(component.diffLines[0])).toEqual({ + [NEW_LINE_TYPE]: NEW_LINE_TYPE, + [LINE_UNFOLD_CLASS_NAME]: false, + [LINE_HOVER_CLASS_NAME]: true, + }); + + expect(component.getLineClass(component.diffLines[5])).toEqual({ + [MATCH_LINE_TYPE]: MATCH_LINE_TYPE, + [LINE_UNFOLD_CLASS_NAME]: true, + [LINE_HOVER_CLASS_NAME]: false, + }); + }); + }); + }); + + describe('template', () => { + it('should have rendered diff lines', () => { + const el = component.$el; + + expect(el.querySelectorAll('tr.line_holder').length).toEqual(6); + expect(el.querySelectorAll('tr.line_holder.new').length).toEqual(2); + expect(el.querySelectorAll('tr.line_holder.match').length).toEqual(1); + expect(el.textContent.indexOf('Bad dates') > -1).toEqual(true); + }); + + it('should render discussions', done => { + const el = component.$el; + component.$store.dispatch('setInitialNotes', getDiscussionsMockData()); + + Vue.nextTick(() => { + expect(el.querySelectorAll('.notes_holder').length).toEqual(1); + expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5); + expect(el.innerText.indexOf('comment 5') > -1).toEqual(true); + component.$store.dispatch('setInitialNotes', []); + + done(); + }); + }); + + it('should render new discussion forms', done => { + const el = component.$el; + const lines = getDiffFileMock().highlightedDiffLines; + + component.handleShowCommentForm({ lineCode: lines[0].lineCode }); + component.handleShowCommentForm({ lineCode: lines[1].lineCode }); + + Vue.nextTick(() => { + expect(el.querySelectorAll('.js-vue-markdown-field').length).toEqual(2); + expect(el.querySelectorAll('tr')[1].classList.contains('notes_holder')).toEqual(true); + expect(el.querySelectorAll('tr')[3].classList.contains('notes_holder')).toEqual(true); + + store.state.diffs.diffLineCommentForms = {}; + + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/components/no_changes_spec.js b/spec/javascripts/diffs/components/no_changes_spec.js new file mode 100644 index 00000000000..7237274eb43 --- /dev/null +++ b/spec/javascripts/diffs/components/no_changes_spec.js @@ -0,0 +1 @@ +// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 diff --git a/spec/javascripts/diffs/components/parallel_diff_view_spec.js b/spec/javascripts/diffs/components/parallel_diff_view_spec.js new file mode 100644 index 00000000000..cab533217c0 --- /dev/null +++ b/spec/javascripts/diffs/components/parallel_diff_view_spec.js @@ -0,0 +1,224 @@ +import Vue from 'vue'; +import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue'; +import store from '~/mr_notes/stores'; +import * as constants from '~/diffs/constants'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import diffFileMockData from '../mock_data/diff_file'; +import discussionsMockData from '../mock_data/diff_discussions'; + +describe('ParallelDiffView', () => { + let component; + const getDiffFileMock = () => Object.assign({}, diffFileMockData); + const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)]; + + beforeEach(() => { + const diffFile = getDiffFileMock(); + + component = createComponentWithStore(Vue.extend(ParallelDiffView), store, { + diffFile, + diffLines: diffFile.parallelDiffLines, + }).$mount(); + }); + + describe('computed', () => { + describe('parallelDiffLines', () => { + it('should normalize lines for empty cells', () => { + expect(component.parallelDiffLines[0].left.type).toEqual(constants.EMPTY_CELL_TYPE); + expect(component.parallelDiffLines[1].left.type).toEqual(constants.EMPTY_CELL_TYPE); + }); + }); + }); + + describe('methods', () => { + describe('hasDiscussion', () => { + it('it should return true if there is a discussion either for left or right section', () => { + Object.defineProperty(component, 'discussionsByLineCode', { + get() { + return { line_42: true }; + }, + }); + + expect(component.hasDiscussion({ left: {}, right: {} })).toEqual(undefined); + expect(component.hasDiscussion({ left: { lineCode: 'line_42' }, right: {} })).toEqual(true); + expect(component.hasDiscussion({ left: {}, right: { lineCode: 'line_42' } })).toEqual(true); + }); + }); + + describe('getClassName', () => { + it('should return line class object', () => { + const { LINE_HOVER_CLASS_NAME, LINE_UNFOLD_CLASS_NAME } = constants; + const { MATCH_LINE_TYPE, NEW_LINE_TYPE, LINE_POSITION_RIGHT } = constants; + + expect(component.getClassName(component.diffLines[1], LINE_POSITION_RIGHT)).toEqual({ + [NEW_LINE_TYPE]: NEW_LINE_TYPE, + [LINE_UNFOLD_CLASS_NAME]: false, + [LINE_HOVER_CLASS_NAME]: false, + }); + + const eventMock = { + target: component.$refs.rightLines[1], + }; + + component.handleMouse(eventMock, component.diffLines[1], true); + Object.defineProperty(component, 'isLoggedIn', { + get() { + return true; + }, + }); + + expect(component.getClassName(component.diffLines[1], LINE_POSITION_RIGHT)).toEqual({ + [NEW_LINE_TYPE]: NEW_LINE_TYPE, + [LINE_UNFOLD_CLASS_NAME]: false, + [LINE_HOVER_CLASS_NAME]: true, + }); + + expect(component.getClassName(component.diffLines[5], LINE_POSITION_RIGHT)).toEqual({ + [MATCH_LINE_TYPE]: MATCH_LINE_TYPE, + [LINE_UNFOLD_CLASS_NAME]: true, + [LINE_HOVER_CLASS_NAME]: false, + }); + }); + }); + + describe('handleMouse', () => { + it('should set hovered line code and line section to null when isHover is false', () => { + const rightLineEventMock = { target: component.$refs.rightLines[1] }; + expect(component.hoveredLineCode).toEqual(null); + expect(component.hoveredSection).toEqual(null); + + component.handleMouse(rightLineEventMock, null, false); + expect(component.hoveredLineCode).toEqual(null); + expect(component.hoveredSection).toEqual(null); + }); + + it('should set hovered line code and line section for right section', () => { + const rightLineEventMock = { target: component.$refs.rightLines[1] }; + component.handleMouse(rightLineEventMock, component.diffLines[1], true); + expect(component.hoveredLineCode).toEqual(component.diffLines[1].right.lineCode); + expect(component.hoveredSection).toEqual(constants.LINE_POSITION_RIGHT); + }); + + it('should set hovered line code and line section for left section', () => { + const leftLineEventMock = { target: component.$refs.leftLines[2] }; + component.handleMouse(leftLineEventMock, component.diffLines[2], true); + expect(component.hoveredLineCode).toEqual(component.diffLines[2].left.lineCode); + expect(component.hoveredSection).toEqual(constants.LINE_POSITION_LEFT); + }); + }); + + describe('shouldRenderDiscussions', () => { + it('should return true if there is a discussion on left side and it is expanded', () => { + const line = { left: { lineCode: 'lineCode1' } }; + spyOn(component, 'isDiscussionExpanded').and.returnValue(true); + Object.defineProperty(component, 'discussionsByLineCode', { + get() { + return { + [line.left.lineCode]: true, + }; + }, + }); + + expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_LEFT)).toEqual(true); + expect(component.isDiscussionExpanded).toHaveBeenCalledWith(line.left.lineCode); + }); + + it('should return false if there is a discussion on left side but it is collapsed', () => { + const line = { left: { lineCode: 'lineCode1' } }; + spyOn(component, 'isDiscussionExpanded').and.returnValue(false); + Object.defineProperty(component, 'discussionsByLineCode', { + get() { + return { + [line.left.lineCode]: true, + }; + }, + }); + + expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_LEFT)).toEqual( + false, + ); + }); + + it('should return false for discussions on the right side if there is no line type', () => { + const CUSTOM_RIGHT_LINE_TYPE = 'CUSTOM_RIGHT_LINE_TYPE'; + const line = { right: { lineCode: 'lineCode1', type: CUSTOM_RIGHT_LINE_TYPE } }; + spyOn(component, 'isDiscussionExpanded').and.returnValue(true); + Object.defineProperty(component, 'discussionsByLineCode', { + get() { + return { + [line.right.lineCode]: true, + }; + }, + }); + + expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_RIGHT)).toEqual( + CUSTOM_RIGHT_LINE_TYPE, + ); + }); + }); + + describe('hasAnyExpandedDiscussion', () => { + const LINE_CODE_LEFT = 'LINE_CODE_LEFT'; + const LINE_CODE_RIGHT = 'LINE_CODE_RIGHT'; + + it('should return true if there is a discussion either on the left or the right side', () => { + const mockLineOne = { + right: { lineCode: LINE_CODE_RIGHT }, + left: {}, + }; + const mockLineTwo = { + left: { lineCode: LINE_CODE_LEFT }, + right: {}, + }; + + spyOn(component, 'isDiscussionExpanded').and.callFake(lc => lc === LINE_CODE_RIGHT); + expect(component.hasAnyExpandedDiscussion(mockLineOne)).toEqual(true); + expect(component.hasAnyExpandedDiscussion(mockLineTwo)).toEqual(false); + }); + }); + }); + + describe('template', () => { + it('should have rendered diff lines', () => { + const el = component.$el; + + expect(el.querySelectorAll('tr.line_holder.parallel').length).toEqual(6); + expect(el.querySelectorAll('td.empty-cell').length).toEqual(4); + expect(el.querySelectorAll('td.line_content.parallel.right-side').length).toEqual(6); + expect(el.querySelectorAll('td.line_content.parallel.left-side').length).toEqual(6); + expect(el.querySelectorAll('td.match').length).toEqual(4); + expect(el.textContent.indexOf('Bad dates') > -1).toEqual(true); + }); + + it('should render discussions', done => { + const el = component.$el; + component.$store.dispatch('setInitialNotes', getDiscussionsMockData()); + + Vue.nextTick(() => { + expect(el.querySelectorAll('.notes_holder').length).toEqual(1); + expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5); + expect(el.innerText.indexOf('comment 5') > -1).toEqual(true); + component.$store.dispatch('setInitialNotes', []); + + done(); + }); + }); + + it('should render new discussion forms', done => { + const el = component.$el; + const lines = getDiffFileMock().parallelDiffLines; + + component.handleShowCommentForm({ lineCode: lines[0].lineCode }); + component.handleShowCommentForm({ lineCode: lines[1].lineCode }); + + Vue.nextTick(() => { + expect(el.querySelectorAll('.js-vue-markdown-field').length).toEqual(2); + expect(el.querySelectorAll('tr')[1].classList.contains('notes_holder')).toEqual(true); + expect(el.querySelectorAll('tr')[3].classList.contains('notes_holder')).toEqual(true); + + store.state.diffs.diffLineCommentForms = {}; + + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js new file mode 100644 index 00000000000..41d0dfd8939 --- /dev/null +++ b/spec/javascripts/diffs/mock_data/diff_discussions.js @@ -0,0 +1,496 @@ +export default { + id: '6b232e05bea388c6b043ccc243ba505faac04ea8', + reply_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', + position: { + formatter: { + old_line: null, + new_line: 2, + old_path: 'CHANGELOG', + new_path: 'CHANGELOG', + base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', + start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', + head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + }, + }, + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + expanded: true, + notes: [ + { + id: 1749, + type: 'DiffNote', + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-04-03T21:06:21.521Z', + updated_at: '2018-04-08T08:50:41.762Z', + system: false, + noteable_id: 51, + noteable_type: 'MergeRequest', + noteable_iid: 20, + human_access: 'Owner', + note: 'comment 1', + note_html: '

comment 1

', + last_edited_at: '2018-04-08T08:50:41.762Z', + last_edited_by: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + current_user: { + can_edit: true, + can_award_emoji: true, + }, + resolved: false, + resolvable: true, + resolved_by: null, + discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', + emoji_awardable: true, + award_emoji: [], + toggle_award_path: '/gitlab-org/gitlab-test/notes/1749/toggle_award_emoji', + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1749&user_id=1', + path: '/gitlab-org/gitlab-test/notes/1749', + noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1749', + resolve_path: + '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', + resolve_with_issue_path: + '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', + }, + { + id: 1753, + type: 'DiffNote', + attachment: null, + author: { + id: 1, + name: 'Fatih Acet', + username: 'fatihacet', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/fatihacevt', + }, + created_at: '2018-04-08T08:49:35.804Z', + updated_at: '2018-04-08T08:50:45.915Z', + system: false, + noteable_id: 51, + noteable_type: 'MergeRequest', + noteable_iid: 20, + human_access: 'Owner', + note: 'comment 2 is really long one', + note_html: '

comment 2 is really long one

', + last_edited_at: '2018-04-08T08:50:45.915Z', + last_edited_by: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + current_user: { + can_edit: true, + can_award_emoji: true, + }, + resolved: false, + resolvable: true, + resolved_by: null, + discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', + emoji_awardable: true, + award_emoji: [], + toggle_award_path: '/gitlab-org/gitlab-test/notes/1753/toggle_award_emoji', + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1753&user_id=1', + path: '/gitlab-org/gitlab-test/notes/1753', + noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1753', + resolve_path: + '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', + resolve_with_issue_path: + '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', + }, + { + id: 1754, + type: 'DiffNote', + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-04-08T08:50:48.294Z', + updated_at: '2018-04-08T08:50:48.294Z', + system: false, + noteable_id: 51, + noteable_type: 'MergeRequest', + noteable_iid: 20, + human_access: 'Owner', + note: 'comment 3', + note_html: '

comment 3

', + current_user: { + can_edit: true, + can_award_emoji: true, + }, + resolved: false, + resolvable: true, + resolved_by: null, + discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', + emoji_awardable: true, + award_emoji: [], + toggle_award_path: '/gitlab-org/gitlab-test/notes/1754/toggle_award_emoji', + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1754&user_id=1', + path: '/gitlab-org/gitlab-test/notes/1754', + noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1754', + resolve_path: + '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', + resolve_with_issue_path: + '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', + }, + { + id: 1755, + type: 'DiffNote', + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-04-08T08:50:50.911Z', + updated_at: '2018-04-08T08:50:50.911Z', + system: false, + noteable_id: 51, + noteable_type: 'MergeRequest', + noteable_iid: 20, + human_access: 'Owner', + note: 'comment 4', + note_html: '

comment 4

', + current_user: { + can_edit: true, + can_award_emoji: true, + }, + resolved: false, + resolvable: true, + resolved_by: null, + discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', + emoji_awardable: true, + award_emoji: [], + toggle_award_path: '/gitlab-org/gitlab-test/notes/1755/toggle_award_emoji', + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1755&user_id=1', + path: '/gitlab-org/gitlab-test/notes/1755', + noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1755', + resolve_path: + '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', + resolve_with_issue_path: + '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', + }, + { + id: 1756, + type: 'DiffNote', + attachment: null, + author: { + id: 1, + name: 'Administrator', + username: 'root', + state: 'active', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + path: '/root', + }, + created_at: '2018-04-08T08:50:53.895Z', + updated_at: '2018-04-08T08:50:53.895Z', + system: false, + noteable_id: 51, + noteable_type: 'MergeRequest', + noteable_iid: 20, + human_access: 'Owner', + note: 'comment 5', + note_html: '

comment 5

', + current_user: { + can_edit: true, + can_award_emoji: true, + }, + resolved: false, + resolvable: true, + resolved_by: null, + discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8', + emoji_awardable: true, + award_emoji: [], + toggle_award_path: '/gitlab-org/gitlab-test/notes/1756/toggle_award_emoji', + report_abuse_path: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1756&user_id=1', + path: '/gitlab-org/gitlab-test/notes/1756', + noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1756', + resolve_path: + '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', + resolve_with_issue_path: + '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', + }, + ], + individual_note: false, + resolvable: true, + resolved: false, + resolve_path: + '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve', + resolve_with_issue_path: + '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20', + diff_file: { + submodule: false, + submodule_link: null, + blob: { + id: '9e10516ca50788acf18c518a231914a21e5f16f7', + path: 'CHANGELOG', + name: 'CHANGELOG', + mode: '100644', + readable_text: true, + icon: 'file-text-o', + }, + blob_path: 'CHANGELOG', + blob_name: 'CHANGELOG', + blob_icon: '', + file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a', + file_path: 'CHANGELOG', + new_file: false, + deleted_file: false, + renamed_file: false, + old_path: 'CHANGELOG', + new_path: 'CHANGELOG', + mode_changed: false, + a_mode: '100644', + b_mode: '100644', + text: true, + added_lines: 2, + removed_lines: 0, + diff_refs: { + base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', + start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', + head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + }, + content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + stored_externally: null, + external_storage: null, + old_path_html: ['CHANGELOG', 'CHANGELOG'], + new_path_html: 'CHANGELOG', + context_lines_path: + '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff', + highlighted_diff_lines: [ + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + old_line: null, + new_line: 1, + text: ' - Bad dates\n', + rich_text: ' - Bad dates\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + old_line: null, + new_line: 2, + text: '\n', + rich_text: '\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + text: 'v6.8.0\n', + rich_text: 'v6.8.0\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + text: '\n', + rich_text: '\n', + meta_data: null, + }, + { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + text: ' v6.7.0\n', + rich_text: ' v6.7.0\n', + meta_data: null, + }, + { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + ], + parallel_diff_lines: [ + { + left: null, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + old_line: null, + new_line: 1, + text: ' - Bad dates\n', + rich_text: ' - Bad dates\n', + meta_data: null, + }, + }, + { + left: null, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + old_line: null, + new_line: 2, + text: '\n', + rich_text: '\n', + meta_data: null, + }, + }, + { + left: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + text: 'v6.8.0\n', + rich_text: 'v6.8.0\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + old_line: 1, + new_line: 3, + text: 'v6.8.0\n', + rich_text: 'v6.8.0\n', + meta_data: null, + }, + }, + { + left: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + text: '\n', + rich_text: '\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + old_line: 2, + new_line: 4, + text: '\n', + rich_text: '\n', + meta_data: null, + }, + }, + { + left: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + text: ' v6.7.0\n', + rich_text: ' v6.7.0\n', + meta_data: null, + }, + right: { + line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + old_line: 3, + new_line: 5, + text: ' v6.7.0\n', + rich_text: ' v6.7.0\n', + meta_data: null, + }, + }, + { + left: { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + right: { + line_code: null, + type: 'match', + old_line: null, + new_line: null, + text: '', + rich_text: '', + meta_data: { + old_pos: 3, + new_pos: 5, + }, + }, + }, + ], + }, + diff_discussion: true, + truncated_diff_lines: + '\n\n \n\n\n1\n\n - Bad dates\n\n\n\n\n \n\n\n2\n\n\n\n\n', + image_diff_html: + '
\n
\n
\n
\nCHANGELOG\n
\n

\n22.3 KB\n|\nW:\n\n|\nH:\n\n

\n
\n
\n
\nCHANGELOG\n
\n\n

\n22.3 KB\n|\nW:\n\n|\nH:\n\n

\n
\n
\n
\n
\n
\nCHANGELOG\n
\n
\n
\nCHANGELOG\n
\n\n
\n\n\n\n\n
\n
\n
\n
\n
\nCHANGELOG\n
\n
\nCHANGELOG\n
\n\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
    \n
  • 2-up
  • \n
  • Swipe
  • \n
  • Onion skin
  • \n
\n
\n', +}; diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js new file mode 100644 index 00000000000..d3bf9525924 --- /dev/null +++ b/spec/javascripts/diffs/mock_data/diff_file.js @@ -0,0 +1,220 @@ +export default { + submodule: false, + submoduleLink: null, + blob: { + id: '9e10516ca50788acf18c518a231914a21e5f16f7', + path: 'CHANGELOG', + name: 'CHANGELOG', + mode: '100644', + readableText: true, + icon: 'file-text-o', + }, + blobPath: 'CHANGELOG', + blobName: 'CHANGELOG', + blobIcon: '', + fileHash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a', + filePath: 'CHANGELOG', + newFile: false, + deletedFile: false, + renamedFile: false, + oldPath: 'CHANGELOG', + newPath: 'CHANGELOG', + modeChanged: false, + aMode: '100644', + bMode: '100644', + text: true, + addedLines: 2, + removedLines: 0, + diffRefs: { + baseSha: 'e63f41fe459e62e1228fcef60d7189127aeba95a', + startSha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962', + headSha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + }, + contentSha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', + storedExternally: null, + externalStorage: null, + oldPathHtml: ['CHANGELOG', 'CHANGELOG'], + newPathHtml: 'CHANGELOG', + editPath: '/gitlab-org/gitlab-test/edit/spooky-stuff/CHANGELOG', + viewPath: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG', + replacedViewPath: null, + collapsed: false, + tooLarge: false, + contextLinesPath: + '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff', + highlightedDiffLines: [ + { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + oldLine: null, + newLine: 1, + text: '+ - Bad dates\n', + richText: '+ - Bad dates\n', + metaData: null, + }, + { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + oldLine: null, + newLine: 2, + text: '+\n', + richText: '+\n', + metaData: null, + }, + { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + oldLine: 1, + newLine: 3, + text: ' v6.8.0\n', + richText: ' v6.8.0\n', + metaData: null, + }, + { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + oldLine: 2, + newLine: 4, + text: ' \n', + richText: ' \n', + metaData: null, + }, + { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + oldLine: 3, + newLine: 5, + text: ' v6.7.0\n', + richText: ' v6.7.0\n', + metaData: null, + }, + { + lineCode: null, + type: 'match', + oldLine: null, + newLine: null, + text: '', + richText: '', + metaData: { + oldPos: 3, + newPos: 5, + }, + }, + ], + parallelDiffLines: [ + { + left: { + type: 'empty-cell', + }, + right: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1', + type: 'new', + oldLine: null, + newLine: 1, + text: '+ - Bad dates\n', + richText: ' - Bad dates\n', + metaData: null, + }, + }, + { + left: { + type: 'empty-cell', + }, + right: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2', + type: 'new', + oldLine: null, + newLine: 2, + text: '+\n', + richText: '\n', + metaData: null, + }, + }, + { + left: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + oldLine: 1, + newLine: 3, + text: ' v6.8.0\n', + richText: 'v6.8.0\n', + metaData: null, + }, + right: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + type: null, + oldLine: 1, + newLine: 3, + text: ' v6.8.0\n', + richText: 'v6.8.0\n', + metaData: null, + }, + }, + { + left: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + oldLine: 2, + newLine: 4, + text: ' \n', + richText: '\n', + metaData: null, + }, + right: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4', + type: null, + oldLine: 2, + newLine: 4, + text: ' \n', + richText: '\n', + metaData: null, + }, + }, + { + left: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + oldLine: 3, + newLine: 5, + text: ' v6.7.0\n', + richText: 'v6.7.0\n', + metaData: null, + }, + right: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5', + type: null, + oldLine: 3, + newLine: 5, + text: ' v6.7.0\n', + richText: 'v6.7.0\n', + metaData: null, + }, + }, + { + left: { + lineCode: null, + type: 'match', + oldLine: null, + newLine: null, + text: '', + richText: '', + metaData: { + oldPos: 3, + newPos: 5, + }, + }, + right: { + lineCode: null, + type: 'match', + oldLine: null, + newLine: null, + text: '', + richText: '', + metaData: { + oldPos: 3, + newPos: 5, + }, + }, + }, + ], +}; diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js new file mode 100644 index 00000000000..e61780c9928 --- /dev/null +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -0,0 +1,210 @@ +import MockAdapter from 'axios-mock-adapter'; +import Cookies from 'js-cookie'; +import { + DIFF_VIEW_COOKIE_NAME, + INLINE_DIFF_VIEW_TYPE, + PARALLEL_DIFF_VIEW_TYPE, +} from '~/diffs/constants'; +import store from '~/diffs/store'; +import * as actions from '~/diffs/store/actions'; +import * as types from '~/diffs/store/mutation_types'; +import axios from '~/lib/utils/axios_utils'; +import testAction from '../../helpers/vuex_action_helper'; + +describe('DiffsStoreActions', () => { + describe('setEndpoint', () => { + it('should set given endpoint', done => { + const endpoint = '/diffs/set/endpoint'; + + testAction( + actions.setEndpoint, + endpoint, + { endpoint: '' }, + [{ type: types.SET_ENDPOINT, payload: endpoint }], + [], + done, + ); + }); + }); + + describe('setLoadingState', () => { + it('should set loading state', done => { + expect(store.state.diffs.isLoading).toEqual(true); + const loadingState = false; + + testAction( + actions.setLoadingState, + loadingState, + {}, + [{ type: types.SET_LOADING, payload: loadingState }], + [], + done, + ); + }); + }); + + describe('fetchDiffFiles', () => { + it('should fetch diff files', done => { + const endpoint = '/fetch/diff/files'; + const mock = new MockAdapter(axios); + const res = { diff_files: 1, merge_request_diffs: [] }; + mock.onGet(endpoint).reply(200, res); + + testAction( + actions.fetchDiffFiles, + {}, + { endpoint }, + [ + { type: types.SET_LOADING, payload: true }, + { type: types.SET_LOADING, payload: false }, + { type: types.SET_MERGE_REQUEST_DIFFS, payload: res.merge_request_diffs }, + { type: types.SET_DIFF_DATA, payload: res }, + ], + [], + () => { + mock.restore(); + done(); + }, + ); + }); + }); + + describe('setInlineDiffViewType', () => { + it('should set diff view type to inline and also set the cookie properly', done => { + testAction( + actions.setInlineDiffViewType, + null, + {}, + [{ type: types.SET_DIFF_VIEW_TYPE, payload: INLINE_DIFF_VIEW_TYPE }], + [], + () => { + setTimeout(() => { + expect(Cookies.get('diff_view')).toEqual(INLINE_DIFF_VIEW_TYPE); + done(); + }, 0); + }, + ); + }); + }); + + describe('setParallelDiffViewType', () => { + it('should set diff view type to parallel and also set the cookie properly', done => { + testAction( + actions.setParallelDiffViewType, + null, + {}, + [{ type: types.SET_DIFF_VIEW_TYPE, payload: PARALLEL_DIFF_VIEW_TYPE }], + [], + () => { + setTimeout(() => { + expect(Cookies.get(DIFF_VIEW_COOKIE_NAME)).toEqual(PARALLEL_DIFF_VIEW_TYPE); + done(); + }, 0); + }, + ); + }); + }); + + describe('showCommentForm', () => { + it('should call mutation to show comment form', done => { + const payload = { lineCode: 'lineCode' }; + + testAction( + actions.showCommentForm, + payload, + {}, + [{ type: types.ADD_COMMENT_FORM_LINE, payload }], + [], + done, + ); + }); + }); + + describe('cancelCommentForm', () => { + it('should call mutation to cancel comment form', done => { + const payload = { lineCode: 'lineCode' }; + + testAction( + actions.cancelCommentForm, + payload, + {}, + [{ type: types.REMOVE_COMMENT_FORM_LINE, payload }], + [], + done, + ); + }); + }); + + describe('loadMoreLines', () => { + it('should call mutation to show comment form', done => { + const endpoint = '/diffs/load/more/lines'; + const params = { since: 6, to: 26 }; + const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 }; + const fileHash = 'ff9200'; + const options = { endpoint, params, lineNumbers, fileHash }; + const mock = new MockAdapter(axios); + const contextLines = { contextLines: [{ lineCode: 6 }] }; + mock.onGet(endpoint).reply(200, contextLines); + + testAction( + actions.loadMoreLines, + options, + {}, + [ + { + type: types.ADD_CONTEXT_LINES, + payload: { lineNumbers, contextLines, params, fileHash }, + }, + ], + [], + () => { + mock.restore(); + done(); + }, + ); + }); + }); + + describe('loadCollapsedDiff', () => { + it('should fetch data and call mutation with response and the give parameter', done => { + const file = { hash: 123, loadCollapsedDiffUrl: '/load/collapsed/diff/url' }; + const data = { hash: 123, parallelDiffLines: [{ lineCode: 1 }] }; + const mock = new MockAdapter(axios); + mock.onGet(file.loadCollapsedDiffUrl).reply(200, data); + + testAction( + actions.loadCollapsedDiff, + file, + {}, + [ + { + type: types.ADD_COLLAPSED_DIFFS, + payload: { file, data }, + }, + ], + [], + () => { + mock.restore(); + done(); + }, + ); + }); + }); + + describe('expandAllFiles', () => { + it('should change the collapsed prop from the diffFiles', done => { + testAction( + actions.expandAllFiles, + null, + {}, + [ + { + type: types.EXPAND_ALL_FILES, + }, + ], + [], + done, + ); + }); + }); +}); diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js new file mode 100644 index 00000000000..7945ddea911 --- /dev/null +++ b/spec/javascripts/diffs/store/getters_spec.js @@ -0,0 +1,24 @@ +import getters from '~/diffs/store/getters'; +import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; + +describe('DiffsStoreGetters', () => { + describe('isParallelView', () => { + it('should return true if view set to parallel view', () => { + expect(getters.isParallelView({ diffViewType: PARALLEL_DIFF_VIEW_TYPE })).toBeTruthy(); + }); + + it('should return false if view not to parallel view', () => { + expect(getters.isParallelView({ diffViewType: 'foo' })).toBeFalsy(); + }); + }); + + describe('isInlineView', () => { + it('should return true if view set to inline view', () => { + expect(getters.isInlineView({ diffViewType: INLINE_DIFF_VIEW_TYPE })).toBeTruthy(); + }); + + it('should return false if view not to inline view', () => { + expect(getters.isInlineView({ diffViewType: PARALLEL_DIFF_VIEW_TYPE })).toBeFalsy(); + }); + }); +}); diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js new file mode 100644 index 00000000000..5f1a6e9def7 --- /dev/null +++ b/spec/javascripts/diffs/store/mutations_spec.js @@ -0,0 +1,147 @@ +import mutations from '~/diffs/store/mutations'; +import * as types from '~/diffs/store/mutation_types'; +import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; + +describe('DiffsStoreMutations', () => { + describe('SET_ENDPOINT', () => { + it('should set endpoint', () => { + const state = {}; + const endpoint = '/diffs/endpoint'; + + mutations[types.SET_ENDPOINT](state, endpoint); + expect(state.endpoint).toEqual(endpoint); + }); + }); + + describe('SET_LOADING', () => { + it('should set loading state', () => { + const state = {}; + + mutations[types.SET_LOADING](state, false); + expect(state.isLoading).toEqual(false); + }); + }); + + describe('SET_DIFF_FILES', () => { + it('should set diff files to state', () => { + const filePath = '/first-diff-file-path'; + const state = {}; + const diffFiles = { + a_mode: 1, + highlighted_diff_lines: [{ file_path: filePath }], + }; + + mutations[types.SET_DIFF_FILES](state, diffFiles); + expect(state.diffFiles.aMode).toEqual(1); + expect(state.diffFiles.highlightedDiffLines[0].filePath).toEqual(filePath); + }); + }); + + describe('SET_DIFF_VIEW_TYPE', () => { + it('should set diff view type properly', () => { + const state = {}; + + mutations[types.SET_DIFF_VIEW_TYPE](state, INLINE_DIFF_VIEW_TYPE); + expect(state.diffViewType).toEqual(INLINE_DIFF_VIEW_TYPE); + }); + }); + + describe('ADD_COMMENT_FORM_LINE', () => { + it('should set a truthy reference for the given line code in diffLineCommentForms', () => { + const state = { diffLineCommentForms: {} }; + const lineCode = 'FDE'; + + mutations[types.ADD_COMMENT_FORM_LINE](state, { lineCode }); + expect(state.diffLineCommentForms[lineCode]).toBeTruthy(); + }); + }); + + describe('REMOVE_COMMENT_FORM_LINE', () => { + it('should remove given reference from diffLineCommentForms', () => { + const state = { diffLineCommentForms: {} }; + const lineCode = 'FDE'; + + mutations[types.ADD_COMMENT_FORM_LINE](state, { lineCode }); + expect(state.diffLineCommentForms[lineCode]).toBeTruthy(); + + mutations[types.REMOVE_COMMENT_FORM_LINE](state, { lineCode }); + expect(state.diffLineCommentForms[lineCode]).toBeUndefined(); + }); + }); + + describe('EXPAND_ALL_FILES', () => { + it('should change the collapsed prop from diffFiles', () => { + const diffFile = { + collapsed: true, + }; + const state = { expandAllFiles: true, diffFiles: [diffFile] }; + + mutations[types.EXPAND_ALL_FILES](state); + expect(state.diffFiles[0].collapsed).toEqual(false); + }); + }); + + describe('ADD_CONTEXT_LINES', () => { + it('should call utils.addContextLines with proper params', () => { + const options = { + lineNumbers: { oldLineNumber: 1, newLineNumber: 2 }, + contextLines: [{ oldLine: 1 }], + fileHash: 'ff9200', + params: { + bottom: true, + }, + }; + const diffFile = { + fileHash: options.fileHash, + highlightedDiffLines: [], + parallelDiffLines: [], + }; + const state = { diffFiles: [diffFile] }; + const lines = [{ oldLine: 1 }]; + + const findDiffFileSpy = spyOnDependency(mutations, 'findDiffFile').and.returnValue(diffFile); + const removeMatchLineSpy = spyOnDependency(mutations, 'removeMatchLine'); + const lineRefSpy = spyOnDependency(mutations, 'addLineReferences').and.returnValue(lines); + const addContextLinesSpy = spyOnDependency(mutations, 'addContextLines'); + + mutations[types.ADD_CONTEXT_LINES](state, options); + + expect(findDiffFileSpy).toHaveBeenCalledWith(state.diffFiles, options.fileHash); + expect(removeMatchLineSpy).toHaveBeenCalledWith( + diffFile, + options.lineNumbers, + options.params.bottom, + ); + expect(lineRefSpy).toHaveBeenCalledWith( + options.contextLines, + options.lineNumbers, + options.params.bottom, + ); + expect(addContextLinesSpy).toHaveBeenCalledWith({ + inlineLines: diffFile.highlightedDiffLines, + parallelLines: diffFile.parallelDiffLines, + contextLines: options.contextLines, + bottom: options.params.bottom, + lineNumbers: options.lineNumbers, + }); + }); + }); + + describe('ADD_COLLAPSED_DIFFS', () => { + it('should update the state with the given data for the given file hash', () => { + const spy = spyOnDependency(mutations, 'convertObjectPropsToCamelCase').and.callThrough(); + + const fileHash = 123; + const state = { diffFiles: [{}, { fileHash, existingField: 0 }] }; + const file = { fileHash }; + const data = { diff_files: [{ file_hash: fileHash, extra_field: 1, existingField: 1 }] }; + + mutations[types.ADD_COLLAPSED_DIFFS](state, { file, data }); + expect(spy).toHaveBeenCalledWith(data, { deep: true }); + + expect(state.diffFiles[1].fileHash).toEqual(fileHash); + expect(state.diffFiles[1].existingField).toEqual(1); + expect(state.diffFiles[1].extraField).toEqual(1); + }); + }); +}); diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js new file mode 100644 index 00000000000..5a024a0f2ad --- /dev/null +++ b/spec/javascripts/diffs/store/utils_spec.js @@ -0,0 +1,179 @@ +import * as utils from '~/diffs/store/utils'; +import { + LINE_POSITION_LEFT, + LINE_POSITION_RIGHT, + TEXT_DIFF_POSITION_TYPE, + DIFF_NOTE_TYPE, + NEW_LINE_TYPE, + OLD_LINE_TYPE, + MATCH_LINE_TYPE, + PARALLEL_DIFF_VIEW_TYPE, +} from '~/diffs/constants'; +import { MERGE_REQUEST_NOTEABLE_TYPE } from '~/notes/constants'; +import diffFileMockData from '../mock_data/diff_file'; +import { noteableDataMock } from '../../notes/mock_data'; + +const getDiffFileMock = () => Object.assign({}, diffFileMockData); + +describe('DiffsStoreUtils', () => { + describe('findDiffFile', () => { + const files = [{ fileHash: 1, name: 'one' }]; + + it('should return correct file', () => { + expect(utils.findDiffFile(files, 1).name).toEqual('one'); + expect(utils.findDiffFile(files, 2)).toBeUndefined(); + }); + }); + + describe('getReversePosition', () => { + it('should return correct line position name', () => { + expect(utils.getReversePosition(LINE_POSITION_RIGHT)).toEqual(LINE_POSITION_LEFT); + expect(utils.getReversePosition(LINE_POSITION_LEFT)).toEqual(LINE_POSITION_RIGHT); + }); + }); + + describe('findIndexInInlineLines and findIndexInParallelLines', () => { + const expectSet = (method, lines, invalidLines) => { + expect(method(lines, { oldLineNumber: 3, newLineNumber: 5 })).toEqual(4); + expect(method(invalidLines || lines, { oldLineNumber: 32, newLineNumber: 53 })).toEqual(-1); + }; + + describe('findIndexInInlineLines', () => { + it('should return correct index for given line numbers', () => { + expectSet(utils.findIndexInInlineLines, getDiffFileMock().highlightedDiffLines); + }); + }); + + describe('findIndexInParallelLines', () => { + it('should return correct index for given line numbers', () => { + expectSet(utils.findIndexInParallelLines, getDiffFileMock().parallelDiffLines, {}); + }); + }); + }); + + describe('removeMatchLine', () => { + it('should remove match line properly by regarding the bottom parameter', () => { + const diffFile = getDiffFileMock(); + const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 }; + const inlineIndex = utils.findIndexInInlineLines(diffFile.highlightedDiffLines, lineNumbers); + const parallelIndex = utils.findIndexInParallelLines(diffFile.parallelDiffLines, lineNumbers); + const atInlineIndex = diffFile.highlightedDiffLines[inlineIndex]; + const atParallelIndex = diffFile.parallelDiffLines[parallelIndex]; + + utils.removeMatchLine(diffFile, lineNumbers, false); + expect(diffFile.highlightedDiffLines[inlineIndex]).not.toEqual(atInlineIndex); + expect(diffFile.parallelDiffLines[parallelIndex]).not.toEqual(atParallelIndex); + + utils.removeMatchLine(diffFile, lineNumbers, true); + expect(diffFile.highlightedDiffLines[inlineIndex + 1]).not.toEqual(atInlineIndex); + expect(diffFile.parallelDiffLines[parallelIndex + 1]).not.toEqual(atParallelIndex); + }); + }); + + describe('addContextLines', () => { + it('should add context lines properly with bottom parameter', () => { + const diffFile = getDiffFileMock(); + const inlineLines = diffFile.highlightedDiffLines; + const parallelLines = diffFile.parallelDiffLines; + const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 }; + const contextLines = [{ lineNumber: 42 }]; + const options = { inlineLines, parallelLines, contextLines, lineNumbers, bottom: true }; + const inlineIndex = utils.findIndexInInlineLines(diffFile.highlightedDiffLines, lineNumbers); + const parallelIndex = utils.findIndexInParallelLines(diffFile.parallelDiffLines, lineNumbers); + const normalizedParallelLine = { + left: options.contextLines[0], + right: options.contextLines[0], + }; + + utils.addContextLines(options); + expect(inlineLines[inlineLines.length - 1]).toEqual(contextLines[0]); + expect(parallelLines[parallelLines.length - 1]).toEqual(normalizedParallelLine); + + delete options.bottom; + utils.addContextLines(options); + expect(inlineLines[inlineIndex]).toEqual(contextLines[0]); + expect(parallelLines[parallelIndex]).toEqual(normalizedParallelLine); + }); + }); + + describe('getNoteFormData', () => { + it('should properly create note form data', () => { + const diffFile = getDiffFileMock(); + noteableDataMock.targetType = MERGE_REQUEST_NOTEABLE_TYPE; + + const options = { + note: 'Hello world!', + noteableData: noteableDataMock, + noteableType: MERGE_REQUEST_NOTEABLE_TYPE, + diffFile, + noteTargetLine: { + lineCode: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3', + metaData: null, + newLine: 3, + oldLine: 1, + }, + diffViewType: PARALLEL_DIFF_VIEW_TYPE, + linePosition: LINE_POSITION_LEFT, + }; + + const position = JSON.stringify({ + base_sha: diffFile.diffRefs.baseSha, + start_sha: diffFile.diffRefs.startSha, + head_sha: diffFile.diffRefs.headSha, + old_path: diffFile.oldPath, + new_path: diffFile.newPath, + position_type: TEXT_DIFF_POSITION_TYPE, + old_line: options.noteTargetLine.oldLine, + new_line: options.noteTargetLine.newLine, + }); + + const postData = { + view: options.diffViewType, + line_type: options.linePosition === LINE_POSITION_RIGHT ? NEW_LINE_TYPE : OLD_LINE_TYPE, + merge_request_diff_head_sha: diffFile.diffRefs.headSha, + in_reply_to_discussion_id: '', + note_project_id: '', + target_type: options.noteableType, + target_id: options.noteableData.id, + note: { + noteable_type: options.noteableType, + noteable_id: options.noteableData.id, + commit_id: '', + type: DIFF_NOTE_TYPE, + line_code: options.noteTargetLine.lineCode, + note: options.note, + position, + }, + }; + + expect(utils.getNoteFormData(options)).toEqual({ + endpoint: options.noteableData.create_note_path, + data: postData, + }); + }); + }); + + describe('addLineReferences', () => { + const lineNumbers = { oldLineNumber: 3, newLineNumber: 4 }; + + it('should add correct line references when bottom set to true', () => { + const lines = [{ type: null }, { type: MATCH_LINE_TYPE }]; + const linesWithReferences = utils.addLineReferences(lines, lineNumbers, true); + + expect(linesWithReferences[0].oldLine).toEqual(lineNumbers.oldLineNumber + 1); + expect(linesWithReferences[0].newLine).toEqual(lineNumbers.newLineNumber + 1); + expect(linesWithReferences[1].metaData.oldPos).toEqual(4); + expect(linesWithReferences[1].metaData.newPos).toEqual(5); + }); + + it('should add correct line references when bottom falsy', () => { + const lines = [{ type: null }, { type: MATCH_LINE_TYPE }, { type: null }]; + const linesWithReferences = utils.addLineReferences(lines, lineNumbers); + + expect(linesWithReferences[0].oldLine).toEqual(0); + expect(linesWithReferences[0].newLine).toEqual(1); + expect(linesWithReferences[1].metaData.oldPos).toEqual(2); + expect(linesWithReferences[1].metaData.newPos).toEqual(3); + }); + }); +}); diff --git a/spec/javascripts/fixtures/commit.rb b/spec/javascripts/fixtures/commit.rb new file mode 100644 index 00000000000..351db6ba184 --- /dev/null +++ b/spec/javascripts/fixtures/commit.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Projects::CommitController, '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + set(:project) { create(:project, :repository) } + set(:user) { create(:user) } + let(:commit) { project.commit("master") } + + render_views + + before(:all) do + clean_frontend_fixtures('commit/') + end + + before do + project.add_master(user) + sign_in(user) + end + + it 'commit/show.html.raw' do |example| + params = { + namespace_id: project.namespace, + project_id: project, + id: commit.id + } + + get :show, params + + expect(response).to be_success + store_frontend_fixture(response, example.description) + end +end diff --git a/spec/javascripts/fixtures/snippet.rb b/spec/javascripts/fixtures/snippet.rb index fa97f352e31..38fc963caf7 100644 --- a/spec/javascripts/fixtures/snippet.rb +++ b/spec/javascripts/fixtures/snippet.rb @@ -7,6 +7,7 @@ describe SnippetsController, '(JavaScript fixtures)', type: :controller do let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') } let(:snippet) { create(:personal_snippet, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: admin) } + let!(:snippet_note) { create(:discussion_note_on_snippet, noteable: snippet, project: project, author: admin, note: '- [ ] Task List Item') } render_views diff --git a/spec/javascripts/helpers/index.js b/spec/javascripts/helpers/index.js new file mode 100644 index 00000000000..d2c5caf0bdb --- /dev/null +++ b/spec/javascripts/helpers/index.js @@ -0,0 +1,3 @@ +import mountComponent, { mountComponentWithStore } from './vue_mount_component_helper'; + +export { mountComponent, mountComponentWithStore }; diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js new file mode 100644 index 00000000000..921d42a0871 --- /dev/null +++ b/spec/javascripts/helpers/init_vue_mr_page_helper.js @@ -0,0 +1,40 @@ +import initMRPage from '~/mr_notes/index'; +import axios from '~/lib/utils/axios_utils'; +import MockAdapter from 'axios-mock-adapter'; +import { userDataMock, notesDataMock, noteableDataMock } from '../notes/mock_data'; +import diffFileMockData from '../diffs/mock_data/diff_file'; + +export default function initVueMRPage() { + const diffsAppEndpoint = '/diffs/app/endpoint'; + const mrEl = document.createElement('div'); + mrEl.className = 'merge-request fixture-mr'; + mrEl.setAttribute('data-mr-action', 'diffs'); + document.body.appendChild(mrEl); + + const mrDiscussionsEl = document.createElement('div'); + mrDiscussionsEl.id = 'js-vue-mr-discussions'; + mrDiscussionsEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); + mrDiscussionsEl.setAttribute('data-noteable-data', JSON.stringify(noteableDataMock)); + mrDiscussionsEl.setAttribute('data-notes-data', JSON.stringify(notesDataMock)); + mrDiscussionsEl.setAttribute('data-noteable-type', 'merge-request'); + document.body.appendChild(mrDiscussionsEl); + + const discussionCounterEl = document.createElement('div'); + discussionCounterEl.id = 'js-vue-discussion-counter'; + document.body.appendChild(discussionCounterEl); + + const diffsAppEl = document.createElement('div'); + diffsAppEl.id = 'js-diffs-app'; + diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint); + diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); + document.body.appendChild(diffsAppEl); + + const mock = new MockAdapter(axios); + mock.onGet(diffsAppEndpoint).reply(200, { + branch_name: 'foo', + diff_files: [diffFileMockData], + }); + + initMRPage(); + return mock; +} diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index a9ec7f42a9d..41ff59949e5 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -556,5 +556,75 @@ describe('common_utils', () => { expect(Object.keys(commonUtils.convertObjectPropsToCamelCase()).length).toBe(0); expect(Object.keys(commonUtils.convertObjectPropsToCamelCase({})).length).toBe(0); }); + + it('does not deep-convert by default', () => { + const obj = { + snake_key: { + child_snake_key: 'value', + }, + }; + + expect( + commonUtils.convertObjectPropsToCamelCase(obj), + ).toEqual({ + snakeKey: { + child_snake_key: 'value', + }, + }); + }); + + describe('deep: true', () => { + it('converts object with child objects', () => { + const obj = { + snake_key: { + child_snake_key: 'value', + }, + }; + + expect( + commonUtils.convertObjectPropsToCamelCase(obj, { deep: true }), + ).toEqual({ + snakeKey: { + childSnakeKey: 'value', + }, + }); + }); + + it('converts array with child objects', () => { + const arr = [ + { + child_snake_key: 'value', + }, + ]; + + expect( + commonUtils.convertObjectPropsToCamelCase(arr, { deep: true }), + ).toEqual([ + { + childSnakeKey: 'value', + }, + ]); + }); + + it('converts array with child arrays', () => { + const arr = [ + [ + { + child_snake_key: 'value', + }, + ], + ]; + + expect( + commonUtils.convertObjectPropsToCamelCase(arr, { deep: true }), + ).toEqual([ + [ + { + childSnakeKey: 'value', + }, + ], + ]); + }); + }); }); }); diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js index eab5c24406a..33987574f00 100644 --- a/spec/javascripts/lib/utils/text_utility_spec.js +++ b/spec/javascripts/lib/utils/text_utility_spec.js @@ -96,4 +96,20 @@ describe('text_utility', () => { expect(textUtils.convertToSentenceCase('Hello World')).toBe('Hello world'); }); }); + + describe('truncateSha', () => { + it('shortens SHAs to 8 characters', () => { + expect(textUtils.truncateSha('verylongsha')).toBe('verylong'); + }); + + it('leaves short SHAs as is', () => { + expect(textUtils.truncateSha('shortsha')).toBe('shortsha'); + }); + }); + + describe('splitCamelCase', () => { + it('separates a PascalCase word to two', () => { + expect(textUtils.splitCamelCase('HelloWorld')).toBe('Hello World'); + }); + }); }); diff --git a/spec/javascripts/matchers.js b/spec/javascripts/matchers.js index 7cc5e753c22..0d465510fd3 100644 --- a/spec/javascripts/matchers.js +++ b/spec/javascripts/matchers.js @@ -1,4 +1,16 @@ export default { + toContainText: () => ({ + compare(vm, text) { + if (!(vm.$el instanceof HTMLElement)) { + throw new Error('vm.$el is not a DOM element!'); + } + + const result = { + pass: vm.$el.innerText.includes(text), + }; + return result; + }, + }), toHaveSpriteIcon: () => ({ compare(element, iconName) { if (!iconName) { @@ -10,7 +22,9 @@ export default { } const iconReferences = [].slice.apply(element.querySelectorAll('svg use')); - const matchingIcon = iconReferences.find(reference => reference.getAttribute('xlink:href').endsWith(`#${iconName}`)); + const matchingIcon = iconReferences.find(reference => + reference.getAttribute('xlink:href').endsWith(`#${iconName}`), + ); const result = { pass: !!matchingIcon, }; @@ -20,7 +34,7 @@ export default { } else { result.message = `${element.outerHTML} does not contain the sprite icon "${iconName}"!`; - const existingIcons = iconReferences.map((reference) => { + const existingIcons = iconReferences.map(reference => { const iconUrl = reference.getAttribute('xlink:href'); return `"${iconUrl.replace(/^.+#/, '')}"`; }); @@ -32,4 +46,12 @@ export default { return result; }, }), + toRender: () => ({ + compare(vm) { + const result = { + pass: vm.$el.nodeType !== Node.COMMENT_NODE, + }; + return result; + }, + }), }; diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js deleted file mode 100644 index dc9dc4d4249..00000000000 --- a/spec/javascripts/merge_request_notes_spec.js +++ /dev/null @@ -1,108 +0,0 @@ -import $ from 'jquery'; -import _ from 'underscore'; -import 'autosize'; -import '~/gl_form'; -import '~/lib/utils/text_utility'; -import '~/behaviors/markdown/render_gfm'; -import Notes from '~/notes'; - -const upArrowKeyCode = 38; - -describe('Merge request notes', () => { - window.gon = window.gon || {}; - window.gl = window.gl || {}; - gl.utils = gl.utils || {}; - - const discussionTabFixture = 'merge_requests/diff_comment.html.raw'; - const changesTabJsonFixture = 'merge_request_diffs/inline_changes_tab_with_comments.json'; - preloadFixtures(discussionTabFixture, changesTabJsonFixture); - - describe('Discussion tab with diff comments', () => { - beforeEach(() => { - loadFixtures(discussionTabFixture); - gl.utils.disableButtonIfEmptyField = _.noop; - window.project_uploads_path = 'http://test.host/uploads'; - $('body').attr('data-page', 'projects:merge_requests:show'); - window.gon.current_user_id = $('.note:last').data('authorId'); - - return new Notes('', []); - }); - - afterEach(() => { - // Undo what we did to the shared - $('body').removeAttr('data-page'); - }); - - describe('up arrow', () => { - it('edits last comment when triggered in main form', () => { - const upArrowEvent = $.Event('keydown'); - upArrowEvent.which = upArrowKeyCode; - - spyOnEvent('.note:last .js-note-edit', 'click'); - - $('.js-note-text').trigger(upArrowEvent); - - expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit'); - }); - - it('edits last comment in discussion when triggered in discussion form', (done) => { - const upArrowEvent = $.Event('keydown'); - upArrowEvent.which = upArrowKeyCode; - - spyOnEvent('.note-discussion .js-note-edit', 'click'); - - $('.js-discussion-reply-button').click(); - - setTimeout(() => { - expect( - $('.note-discussion .js-note-text'), - ).toExist(); - - $('.note-discussion .js-note-text').trigger(upArrowEvent); - - expect('click').toHaveBeenTriggeredOn('.note-discussion .js-note-edit'); - - done(); - }); - }); - }); - }); - - describe('Changes tab with diff comments', () => { - beforeEach(() => { - const diffsResponse = getJSONFixture(changesTabJsonFixture); - const noteFormHtml = `
- -
`; - setFixtures(diffsResponse.html + noteFormHtml); - $('body').attr('data-page', 'projects:merge_requests:show'); - window.gon.current_user_id = $('.note:last').data('authorId'); - - return new Notes('', []); - }); - - afterEach(() => { - // Undo what we did to the shared - $('body').removeAttr('data-page'); - }); - - describe('up arrow', () => { - it('edits last comment in discussion when triggered in discussion form', (done) => { - const upArrowEvent = $.Event('keydown'); - upArrowEvent.which = upArrowKeyCode; - - spyOnEvent('.note:last .js-note-edit', 'click'); - - $('.js-discussion-reply-button').trigger('click'); - - setTimeout(() => { - $('.js-note-text').trigger(upArrowEvent); - - expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit'); - - done(); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index 3dbd9756cd2..08928e13985 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -1,5 +1,4 @@ -/* eslint-disable no-var, comma-dangle, object-shorthand */ - +/* eslint-disable no-var, object-shorthand */ import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; @@ -7,480 +6,228 @@ import MergeRequestTabs from '~/merge_request_tabs'; import '~/commit/pipelines/pipelines_bundle'; import '~/breakpoints'; import '~/lib/utils/common_utils'; -import Diff from '~/diff'; -import Notes from '~/notes'; import 'vendor/jquery.scrollTo'; - -(function () { - describe('MergeRequestTabs', function () { - var stubLocation = {}; - var setLocation = function (stubs) { - var defaults = { - pathname: '', - search: '', - hash: '' - }; - $.extend(stubLocation, defaults, stubs || {}); +import initMrPage from './helpers/init_vue_mr_page_helper'; + +describe('MergeRequestTabs', function() { + let mrPageMock; + var stubLocation = {}; + var setLocation = function(stubs) { + var defaults = { + pathname: '', + search: '', + hash: '', }; + $.extend(stubLocation, defaults, stubs || {}); + }; - const inlineChangesTabJsonFixture = 'merge_request_diffs/inline_changes_tab_with_comments.json'; - const parallelChangesTabJsonFixture = 'merge_request_diffs/parallel_changes_tab_with_comments.json'; - preloadFixtures( - 'merge_requests/merge_request_with_task_list.html.raw', - 'merge_requests/diff_comment.html.raw', - inlineChangesTabJsonFixture, - parallelChangesTabJsonFixture - ); - - beforeEach(function () { - this.class = new MergeRequestTabs({ stubLocation: stubLocation }); - setLocation(); - - this.spies = { - history: spyOn(window.history, 'replaceState').and.callFake(function () {}) - }; - }); - - afterEach(function () { - this.class.unbindEvents(); - this.class.destroyPipelinesView(); - }); - - describe('activateTab', function () { - beforeEach(function () { - spyOn(axios, 'get').and.returnValue(Promise.resolve({ data: {} })); - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - this.subject = this.class.activateTab; - }); - it('shows the notes tab when action is show', function () { - this.subject('show'); - expect($('#notes')).toHaveClass('active'); - }); - it('shows the commits tab when action is commits', function () { - this.subject('commits'); - expect($('#commits')).toHaveClass('active'); - }); - it('shows the diffs tab when action is diffs', function () { - this.subject('diffs'); - expect($('#diffs')).toHaveClass('active'); - }); - }); - - describe('opensInNewTab', function () { - var tabUrl; - var windowTarget = '_blank'; - - beforeEach(function () { - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - - tabUrl = $('.commits-tab a').attr('href'); + preloadFixtures( + 'merge_requests/merge_request_with_task_list.html.raw', + 'merge_requests/diff_comment.html.raw', + ); - spyOn($.fn, 'attr').and.returnValue(tabUrl); - }); - - describe('meta click', () => { - let metakeyEvent; - beforeEach(function () { - metakeyEvent = $.Event('click', { keyCode: 91, ctrlKey: true }); - }); + beforeEach(function() { + mrPageMock = initMrPage(); + this.class = new MergeRequestTabs({ stubLocation: stubLocation }); + setLocation(); - it('opens page when commits link is clicked', function () { - spyOn(window, 'open').and.callFake(function (url, name) { - expect(url).toEqual(tabUrl); - expect(name).toEqual(windowTarget); - }); + this.spies = { + history: spyOn(window.history, 'replaceState').and.callFake(function() {}), + }; + }); - this.class.bindEvents(); - $('.merge-request-tabs .commits-tab a').trigger(metakeyEvent); - }); + afterEach(function() { + this.class.unbindEvents(); + this.class.destroyPipelinesView(); + mrPageMock.restore(); + }); - it('opens page when commits badge is clicked', function () { - spyOn(window, 'open').and.callFake(function (url, name) { - expect(url).toEqual(tabUrl); - expect(name).toEqual(windowTarget); - }); + describe('opensInNewTab', function() { + var tabUrl; + var windowTarget = '_blank'; - this.class.bindEvents(); - $('.merge-request-tabs .commits-tab a .badge').trigger(metakeyEvent); - }); - }); + beforeEach(function() { + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - it('opens page tab in a new browser tab with Ctrl+Click - Windows/Linux', function () { - spyOn(window, 'open').and.callFake(function (url, name) { - expect(url).toEqual(tabUrl); - expect(name).toEqual(windowTarget); - }); + tabUrl = $('.commits-tab a').attr('href'); + }); - this.class.clickTab({ - metaKey: false, - ctrlKey: true, - which: 1, - stopImmediatePropagation: function () {} - }); + describe('meta click', () => { + let metakeyEvent; + beforeEach(function() { + metakeyEvent = $.Event('click', { keyCode: 91, ctrlKey: true }); }); - it('opens page tab in a new browser tab with Cmd+Click - Mac', function () { - spyOn(window, 'open').and.callFake(function (url, name) { + it('opens page when commits link is clicked', function() { + spyOn(window, 'open').and.callFake(function(url, name) { expect(url).toEqual(tabUrl); expect(name).toEqual(windowTarget); }); - this.class.clickTab({ - metaKey: true, - ctrlKey: false, - which: 1, - stopImmediatePropagation: function () {} - }); + this.class.bindEvents(); + $('.merge-request-tabs .commits-tab a').trigger(metakeyEvent); }); - it('opens page tab in a new browser tab with Middle-click - Mac/PC', function () { - spyOn(window, 'open').and.callFake(function (url, name) { + it('opens page when commits badge is clicked', function() { + spyOn(window, 'open').and.callFake(function(url, name) { expect(url).toEqual(tabUrl); expect(name).toEqual(windowTarget); }); - this.class.clickTab({ - metaKey: false, - ctrlKey: false, - which: 2, - stopImmediatePropagation: function () {} - }); + this.class.bindEvents(); + $('.merge-request-tabs .commits-tab a .badge').trigger(metakeyEvent); }); }); - describe('setCurrentAction', function () { - beforeEach(function () { - spyOn(axios, 'get').and.returnValue(Promise.resolve({ data: {} })); - this.subject = this.class.setCurrentAction; - }); - - it('changes from commits', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/commits' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); - expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); - }); - - it('changes from diffs', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/diffs' - }); - - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); - expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + it('opens page tab in a new browser tab with Ctrl+Click - Windows/Linux', function() { + spyOn(window, 'open').and.callFake(function(url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); }); - it('changes from diffs.html', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/diffs.html' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); - expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + this.class.clickTab({ + metaKey: false, + ctrlKey: true, + which: 1, + stopImmediatePropagation: function() {}, }); + }); - it('changes from notes', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1' - }); - expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); - expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + it('opens page tab in a new browser tab with Cmd+Click - Mac', function() { + spyOn(window, 'open').and.callFake(function(url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); }); - it('includes search parameters and hash string', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/diffs', - search: '?view=parallel', - hash: '#L15-35' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35'); + this.class.clickTab({ + metaKey: true, + ctrlKey: false, + which: 1, + stopImmediatePropagation: function() {}, }); + }); - it('replaces the current history state', function () { - var newState; - setLocation({ - pathname: '/foo/bar/merge_requests/1' - }); - newState = this.subject('commits'); - expect(this.spies.history).toHaveBeenCalledWith({ - url: newState - }, document.title, newState); + it('opens page tab in a new browser tab with Middle-click - Mac/PC', function() { + spyOn(window, 'open').and.callFake(function(url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); }); - it('treats "show" like "notes"', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/commits' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + this.class.clickTab({ + metaKey: false, + ctrlKey: false, + which: 2, + stopImmediatePropagation: function() {}, }); }); + }); - describe('tabShown', () => { - let mock; + describe('setCurrentAction', function() { + let mock; - beforeEach(function () { - mock = new MockAdapter(axios); - mock.onGet(/(.*)\/diffs\.json/).reply(200, { - data: { html: '' }, - }); + beforeEach(function() { + mock = new MockAdapter(axios); + mock.onAny().reply({ data: {} }); + this.subject = this.class.setCurrentAction; + }); - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - }); + afterEach(() => { + mock.restore(); + }); - afterEach(() => { - mock.restore(); + it('changes from commits', function() { + setLocation({ + pathname: '/foo/bar/merge_requests/1/commits', }); - describe('with "Side-by-side"/parallel diff view', () => { - beforeEach(function () { - this.class.diffViewType = () => 'parallel'; - Diff.prototype.diffViewType = () => 'parallel'; - }); - - it('maintains `container-limited` for pipelines tab', function (done) { - const asyncClick = function (selector) { - return new Promise((resolve) => { - setTimeout(() => { - document.querySelector(selector).click(); - resolve(); - }); - }); - }; - asyncClick('.merge-request-tabs .pipelines-tab a') - .then(() => asyncClick('.merge-request-tabs .diffs-tab a')) - .then(() => asyncClick('.merge-request-tabs .pipelines-tab a')) - .then(() => { - const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited'); - expect(hasContainerLimitedClass).toBe(true); - }) - .then(done) - .catch((err) => { - done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`); - }); - }); - - it('maintains `container-limited` when switching from "Changes" tab before it loads', function (done) { - const asyncClick = function (selector) { - return new Promise((resolve) => { - setTimeout(() => { - document.querySelector(selector).click(); - resolve(); - }); - }); - }; - - asyncClick('.merge-request-tabs .diffs-tab a') - .then(() => asyncClick('.merge-request-tabs .notes-tab a')) - .then(() => { - const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited'); - expect(hasContainerLimitedClass).toBe(true); - }) - .then(done) - .catch((err) => { - done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`); - }); - }); - }); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); }); - describe('loadDiff', function () { - beforeEach(() => { - loadFixtures('merge_requests/diff_comment.html.raw'); - $('body').attr('data-page', 'projects:merge_requests:show'); - window.gl.ImageFile = () => {}; - Notes.initialize('', []); - spyOn(Notes.instance, 'toggleDiffNote').and.callThrough(); + it('changes from diffs', function() { + setLocation({ + pathname: '/foo/bar/merge_requests/1/diffs', }); - afterEach(() => { - delete window.gl.ImageFile; - delete window.notes; + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); - // Undo what we did to the shared - $('body').removeAttr('data-page'); + it('changes from diffs.html', function() { + setLocation({ + pathname: '/foo/bar/merge_requests/1/diffs.html', }); - it('triggers Ajax request to JSON endpoint', function (done) { - const url = '/foo/bar/merge_requests/1/diffs'; - - spyOn(axios, 'get').and.callFake((reqUrl) => { - expect(reqUrl).toBe(`${url}.json`); - - done(); - - return Promise.resolve({ data: {} }); - }); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); - this.class.loadDiff(url); + it('changes from notes', function() { + setLocation({ + pathname: '/foo/bar/merge_requests/1', }); - it('triggers scroll event when diff already loaded', function (done) { - spyOn(axios, 'get').and.callFake(done.fail); - spyOn(document, 'dispatchEvent'); - - this.class.diffsLoaded = true; - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); + expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); - expect( - document.dispatchEvent, - ).toHaveBeenCalledWith(new CustomEvent('scroll')); - done(); + it('includes search parameters and hash string', function() { + setLocation({ + pathname: '/foo/bar/merge_requests/1/diffs', + search: '?view=parallel', + hash: '#L15-35', }); - describe('with inline diff', () => { - let noteId; - let noteLineNumId; - let mock; - - beforeEach(() => { - const diffsResponse = getJSONFixture(inlineChangesTabJsonFixture); - - const $html = $(diffsResponse.html); - noteId = $html.find('.note').attr('id'); - noteLineNumId = $html - .find('.note') - .closest('.notes_holder') - .prev('.line_holder') - .find('a[data-linenumber]') - .attr('href') - .replace('#', ''); - - mock = new MockAdapter(axios); - mock.onGet(/(.*)\/diffs\.json/).reply(200, diffsResponse); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('with note fragment hash', () => { - it('should expand and scroll to linked fragment hash #note_xxx', function (done) { - spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteId); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - - setTimeout(() => { - expect(noteId.length).toBeGreaterThan(0); - expect(Notes.instance.toggleDiffNote).toHaveBeenCalledWith({ - target: jasmine.any(Object), - lineType: 'old', - forceShow: true, - }); - - done(); - }); - }); - - it('should gracefully ignore non-existant fragment hash', function (done) { - spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue('note_something-that-does-not-exist'); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - - setTimeout(() => { - expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled(); - - done(); - }); - }); - }); - - describe('with line number fragment hash', () => { - it('should gracefully ignore line number fragment hash', function () { - spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteLineNumId); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35'); + }); - expect(noteLineNumId.length).toBeGreaterThan(0); - expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled(); - }); - }); + it('replaces the current history state', function() { + var newState; + setLocation({ + pathname: '/foo/bar/merge_requests/1', }); + newState = this.subject('commits'); - describe('with parallel diff', () => { - let noteId; - let noteLineNumId; - let mock; - - beforeEach(() => { - const diffsResponse = getJSONFixture(parallelChangesTabJsonFixture); - - const $html = $(diffsResponse.html); - noteId = $html.find('.note').attr('id'); - noteLineNumId = $html - .find('.note') - .closest('.notes_holder') - .prev('.line_holder') - .find('a[data-linenumber]') - .attr('href') - .replace('#', ''); - - mock = new MockAdapter(axios); - mock.onGet(/(.*)\/diffs\.json/).reply(200, diffsResponse); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('with note fragment hash', () => { - it('should expand and scroll to linked fragment hash #note_xxx', function (done) { - spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteId); - - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - - setTimeout(() => { - expect(noteId.length).toBeGreaterThan(0); - expect(Notes.instance.toggleDiffNote).toHaveBeenCalledWith({ - target: jasmine.any(Object), - lineType: 'new', - forceShow: true, - }); - - done(); - }); - }); - - it('should gracefully ignore non-existant fragment hash', function (done) { - spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue('note_something-that-does-not-exist'); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - - setTimeout(() => { - expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled(); - done(); - }); - }); - }); - - describe('with line number fragment hash', () => { - it('should gracefully ignore line number fragment hash', function () { - spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteLineNumId); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + expect(this.spies.history).toHaveBeenCalledWith( + { + url: newState, + }, + document.title, + newState, + ); + }); - expect(noteLineNumId.length).toBeGreaterThan(0); - expect(Notes.instance.toggleDiffNote).not.toHaveBeenCalled(); - }); - }); + it('treats "show" like "notes"', function() { + setLocation({ + pathname: '/foo/bar/merge_requests/1/commits', }); + + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); }); + }); - describe('expandViewContainer', function () { - beforeEach(() => { - $('body').append('
'); - }); + describe('expandViewContainer', function() { + beforeEach(() => { + $('body').append( + '
', + ); + }); - afterEach(() => { - $('.content-wrapper').remove(); - }); + afterEach(() => { + $('.content-wrapper').remove(); + }); - it('removes container-limited from containers', function () { - this.class.expandViewContainer(); + it('removes container-limited from containers', function() { + this.class.expandViewContainer(); - expect($('.content-wrapper')).not.toContainElement('.container-limited'); - }); + expect($('.content-wrapper')).not.toContainElement('.container-limited'); + }); - it('does remove container-limited from breadcrumbs', function () { - $('.container-limited').addClass('breadcrumbs'); - this.class.expandViewContainer(); + it('does remove container-limited from breadcrumbs', function() { + $('.container-limited').addClass('breadcrumbs'); + this.class.expandViewContainer(); - expect($('.content-wrapper')).toContainElement('.container-limited'); - }); + expect($('.content-wrapper')).toContainElement('.container-limited'); }); }); -}).call(window); +}); diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js index a7d1e4331eb..155c91dcc46 100644 --- a/spec/javascripts/notes/components/comment_form_spec.js +++ b/spec/javascripts/notes/components/comment_form_spec.js @@ -1,23 +1,27 @@ import $ from 'jquery'; import Vue from 'vue'; import Autosize from 'autosize'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import CommentForm from '~/notes/components/comment_form.vue'; +import * as constants from '~/notes/constants'; import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data'; import { keyboardDownEvent } from '../../issue_show/helpers'; describe('issue_comment_form component', () => { + let store; let vm; const Component = Vue.extend(CommentForm); let mountComponent; beforeEach(() => { - mountComponent = (noteableType = 'issue') => new Component({ - propsData: { - noteableType, - }, - store, - }).$mount(); + store = createStore(); + mountComponent = (noteableType = 'issue') => + new Component({ + propsData: { + noteableType, + }, + store, + }).$mount(); }); afterEach(() => { @@ -34,7 +38,9 @@ describe('issue_comment_form component', () => { }); it('should render user avatar with link', () => { - expect(vm.$el.querySelector('.timeline-icon .user-avatar-link').getAttribute('href')).toEqual(userDataMock.path); + expect(vm.$el.querySelector('.timeline-icon .user-avatar-link').getAttribute('href')).toEqual( + userDataMock.path, + ); }); describe('handleSave', () => { @@ -60,7 +66,7 @@ describe('issue_comment_form component', () => { expect(vm.toggleIssueState).toHaveBeenCalled(); }); - it('should disable action button whilst submitting', (done) => { + it('should disable action button whilst submitting', done => { const saveNotePromise = Promise.resolve(); vm.note = 'hello world'; spyOn(vm, 'saveNote').and.returnValue(saveNotePromise); @@ -87,16 +93,18 @@ describe('issue_comment_form component', () => { ).toEqual('Write a comment or drag your files here…'); }); - it('should make textarea disabled while requesting', (done) => { + it('should make textarea disabled while requesting', done => { const $submitButton = $(vm.$el.querySelector('.js-comment-submit-button')); vm.note = 'hello world'; spyOn(vm, 'stopPolling'); spyOn(vm, 'saveNote').and.returnValue(new Promise(() => {})); - vm.$nextTick(() => { // Wait for vm.note change triggered. It should enable $submitButton. + vm.$nextTick(() => { + // Wait for vm.note change triggered. It should enable $submitButton. $submitButton.trigger('click'); - vm.$nextTick(() => { // Wait for vm.isSubmitting triggered. It should disable textarea. + vm.$nextTick(() => { + // Wait for vm.isSubmitting triggered. It should disable textarea. expect(vm.$el.querySelector('.js-main-target-form textarea').disabled).toBeTruthy(); done(); }); @@ -105,21 +113,27 @@ describe('issue_comment_form component', () => { it('should support quick actions', () => { expect( - vm.$el.querySelector('.js-main-target-form textarea').getAttribute('data-supports-quick-actions'), + vm.$el + .querySelector('.js-main-target-form textarea') + .getAttribute('data-supports-quick-actions'), ).toEqual('true'); }); it('should link to markdown docs', () => { const { markdownDocsPath } = notesDataMock; - expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual('Markdown'); + expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual( + 'Markdown', + ); }); it('should link to quick actions docs', () => { const { quickActionsDocsPath } = notesDataMock; - expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual('quick actions'); + expect( + vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim(), + ).toEqual('quick actions'); }); - it('should resize textarea after note discarded', (done) => { + it('should resize textarea after note discarded', done => { spyOn(Autosize, 'update'); spyOn(vm, 'discard').and.callThrough(); @@ -136,7 +150,9 @@ describe('issue_comment_form component', () => { it('should enter edit mode when arrow up is pressed', () => { spyOn(vm, 'editCurrentUserLastNote').and.callThrough(); vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; - vm.$el.querySelector('.js-main-target-form textarea').dispatchEvent(keyboardDownEvent(38, true)); + vm.$el + .querySelector('.js-main-target-form textarea') + .dispatchEvent(keyboardDownEvent(38, true)); expect(vm.editCurrentUserLastNote).toHaveBeenCalled(); }); @@ -151,7 +167,9 @@ describe('issue_comment_form component', () => { it('should save note when cmd+enter is pressed', () => { spyOn(vm, 'handleSave').and.callThrough(); vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; - vm.$el.querySelector('.js-main-target-form textarea').dispatchEvent(keyboardDownEvent(13, true)); + vm.$el + .querySelector('.js-main-target-form textarea') + .dispatchEvent(keyboardDownEvent(13, true)); expect(vm.handleSave).toHaveBeenCalled(); }); @@ -159,7 +177,9 @@ describe('issue_comment_form component', () => { it('should save note when ctrl+enter is pressed', () => { spyOn(vm, 'handleSave').and.callThrough(); vm.$el.querySelector('.js-main-target-form textarea').value = 'Foo'; - vm.$el.querySelector('.js-main-target-form textarea').dispatchEvent(keyboardDownEvent(13, false, true)); + vm.$el + .querySelector('.js-main-target-form textarea') + .dispatchEvent(keyboardDownEvent(13, false, true)); expect(vm.handleSave).toHaveBeenCalled(); }); @@ -168,41 +188,51 @@ describe('issue_comment_form component', () => { describe('actions', () => { it('should be possible to close the issue', () => { - expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual('Close issue'); + expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual( + 'Close issue', + ); }); it('should render comment button as disabled', () => { - expect(vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled')).toEqual('disabled'); + expect(vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled')).toEqual( + 'disabled', + ); }); - it('should enable comment button if it has note', (done) => { + it('should enable comment button if it has note', done => { vm.note = 'Foo'; Vue.nextTick(() => { - expect(vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled')).toEqual(null); + expect( + vm.$el.querySelector('.js-comment-submit-button').getAttribute('disabled'), + ).toEqual(null); done(); }); }); - it('should update buttons texts when it has note', (done) => { + it('should update buttons texts when it has note', done => { vm.note = 'Foo'; Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual('Comment & close issue'); + expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual( + 'Comment & close issue', + ); expect(vm.$el.querySelector('.js-note-discard')).toBeDefined(); done(); }); }); - it('updates button text with noteable type', (done) => { - vm.noteableType = 'merge_request'; + it('updates button text with noteable type', done => { + vm.noteableType = constants.MERGE_REQUEST_NOTEABLE_TYPE; Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual('Close merge request'); + expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual( + 'Close merge request', + ); done(); }); }); describe('when clicking close/reopen button', () => { - it('should disable button and show a loading spinner', (done) => { + it('should disable button and show a loading spinner', done => { const toggleStateButton = vm.$el.querySelector('.js-action-button'); toggleStateButton.click(); @@ -217,7 +247,7 @@ describe('issue_comment_form component', () => { }); describe('issue is confidential', () => { - it('shows information warning', (done) => { + it('shows information warning', done => { store.dispatch('setNoteableData', Object.assign(noteableDataMock, { confidential: true })); Vue.nextTick(() => { expect(vm.$el.querySelector('.confidential-issue-warning')).toBeDefined(); @@ -237,7 +267,9 @@ describe('issue_comment_form component', () => { }); it('should render signed out widget', () => { - expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual('Please register or sign in to reply'); + expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual( + 'Please register or sign in to reply', + ); }); it('should not render submission form', () => { diff --git a/spec/javascripts/notes/components/diff_file_header_spec.js b/spec/javascripts/notes/components/diff_file_header_spec.js deleted file mode 100644 index ef6d513444a..00000000000 --- a/spec/javascripts/notes/components/diff_file_header_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import Vue from 'vue'; -import DiffFileHeader from '~/notes/components/diff_file_header.vue'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; - -const discussionFixture = 'merge_requests/diff_discussion.json'; - -describe('diff_file_header', () => { - let vm; - const diffDiscussionMock = getJSONFixture(discussionFixture)[0]; - const diffFile = convertObjectPropsToCamelCase(diffDiscussionMock.diff_file); - const props = { - diffFile, - }; - const Component = Vue.extend(DiffFileHeader); - const selectors = { - get copyButton() { - return vm.$el.querySelector('button[data-original-title="Copy file path to clipboard"]'); - }, - get fileName() { - return vm.$el.querySelector('.file-title-name'); - }, - get titleWrapper() { - return vm.$refs.titleWrapper; - }, - }; - - describe('submodule', () => { - beforeEach(() => { - props.diffFile.submodule = true; - props.diffFile.submoduleLink = 'Submodule'; - - vm = mountComponent(Component, props); - }); - - it('shows submoduleLink', () => { - expect(selectors.fileName.innerHTML).toBe(props.diffFile.submoduleLink); - }); - - it('has button to copy blob path', () => { - expect(selectors.copyButton).toExist(); - expect(selectors.copyButton.getAttribute('data-clipboard-text')).toBe(props.diffFile.submoduleLink); - }); - }); - - describe('changed file', () => { - beforeEach(() => { - props.diffFile.submodule = false; - props.diffFile.discussionPath = 'some/discussion/id'; - - vm = mountComponent(Component, props); - }); - - it('shows file type icon', () => { - expect(vm.$el.innerHTML).toContain('fa-file-text-o'); - }); - - it('links to discussion path', () => { - expect(selectors.titleWrapper).toExist(); - expect(selectors.titleWrapper.tagName).toBe('A'); - expect(selectors.titleWrapper.getAttribute('href')).toBe(props.diffFile.discussionPath); - }); - - it('shows plain title if no link given', () => { - props.diffFile.discussionPath = undefined; - vm = mountComponent(Component, props); - - expect(selectors.titleWrapper.tagName).not.toBe('A'); - expect(selectors.titleWrapper.href).toBeFalsy(); - }); - - it('has button to copy file path', () => { - expect(selectors.copyButton).toExist(); - expect(selectors.copyButton.getAttribute('data-clipboard-text')).toBe(props.diffFile.filePath); - }); - - it('shows file mode change', (done) => { - vm.diffFile = { - ...props.diffFile, - modeChanged: true, - aMode: '100755', - bMode: '100644', - }; - - Vue.nextTick(() => { - expect( - vm.$refs.fileMode.textContent.trim(), - ).toBe('100755 → 100644'); - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/notes/components/diff_with_note_spec.js b/spec/javascripts/notes/components/diff_with_note_spec.js index f4ec7132dbd..239d7950907 100644 --- a/spec/javascripts/notes/components/diff_with_note_spec.js +++ b/spec/javascripts/notes/components/diff_with_note_spec.js @@ -1,12 +1,14 @@ import Vue from 'vue'; import DiffWithNote from '~/notes/components/diff_with_note.vue'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import createStore from '~/notes/stores'; +import { mountComponentWithStore } from 'spec/helpers'; const discussionFixture = 'merge_requests/diff_discussion.json'; const imageDiscussionFixture = 'merge_requests/image_diff_discussion.json'; describe('diff_with_note', () => { + let store; let vm; const diffDiscussionMock = getJSONFixture(discussionFixture)[0]; const diffDiscussion = convertObjectPropsToCamelCase(diffDiscussionMock); @@ -29,9 +31,21 @@ describe('diff_with_note', () => { }, }; + beforeEach(() => { + store = createStore(); + store.replaceState({ + ...store.state, + notes: { + noteableData: { + current_user: {}, + }, + }, + }); + }); + describe('text diff', () => { beforeEach(() => { - vm = mountComponent(Component, props); + vm = mountComponentWithStore(Component, { props, store }); }); it('shows text diff', () => { @@ -55,7 +69,7 @@ describe('diff_with_note', () => { }); it('shows image diff', () => { - vm = mountComponent(Component, props); + vm = mountComponentWithStore(Component, { props, store }); expect(selectors.container).toHaveClass('js-image-file'); expect(selectors.diffTable).not.toExist(); diff --git a/spec/javascripts/notes/components/discussion_counter_spec.js b/spec/javascripts/notes/components/discussion_counter_spec.js new file mode 100644 index 00000000000..7b2302e6f47 --- /dev/null +++ b/spec/javascripts/notes/components/discussion_counter_spec.js @@ -0,0 +1,58 @@ +import Vue from 'vue'; +import createStore from '~/notes/stores'; +import DiscussionCounter from '~/notes/components/discussion_counter.vue'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data'; + +describe('DiscussionCounter component', () => { + let store; + let vm; + + beforeEach(() => { + window.mrTabs = {}; + + const Component = Vue.extend(DiscussionCounter); + + store = createStore(); + store.dispatch('setNoteableData', noteableDataMock); + store.dispatch('setNotesData', notesDataMock); + + vm = createComponentWithStore(Component, store); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('methods', () => { + describe('jumpToFirstUnresolvedDiscussion', () => { + it('expands unresolved discussion', () => { + spyOn(vm, 'expandDiscussion').and.stub(); + const discussions = [ + { + ...discussionMock, + id: discussionMock.id, + notes: [{ ...discussionMock.notes[0], resolved: true }], + }, + { + ...discussionMock, + id: discussionMock.id + 1, + notes: [{ ...discussionMock.notes[0], resolved: false }], + }, + ]; + const firstDiscussionId = discussionMock.id + 1; + store.replaceState({ + ...store.state, + discussions, + }); + setFixtures(` +
+ `); + + vm.jumpToFirstUnresolvedDiscussion(); + + expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: firstDiscussionId }); + }); + }); + }); +}); diff --git a/spec/javascripts/notes/components/note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js index c9e549d2096..52cc42cb53d 100644 --- a/spec/javascripts/notes/components/note_actions_spec.js +++ b/spec/javascripts/notes/components/note_actions_spec.js @@ -1,14 +1,16 @@ import Vue from 'vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import noteActions from '~/notes/components/note_actions.vue'; import { userDataMock } from '../mock_data'; describe('issue_note_actions component', () => { let vm; + let store; let Component; beforeEach(() => { Component = Vue.extend(noteActions); + store = createStore(); }); afterEach(() => { @@ -27,7 +29,9 @@ describe('issue_note_actions component', () => { canAwardEmoji: true, canReportAsAbuse: true, noteId: 539, - reportAbusePath: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26', + noteUrl: 'https://localhost:3000/group/project/merge_requests/1#note_1', + reportAbusePath: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26', }; store.dispatch('setUserData', userDataMock); @@ -74,7 +78,9 @@ describe('issue_note_actions component', () => { canAwardEmoji: false, canReportAsAbuse: false, noteId: 539, - reportAbusePath: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26', + noteUrl: 'https://localhost:3000/group/project/merge_requests/1#note_1', + reportAbusePath: + '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26', }; vm = new Component({ store, diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js index d494c63ff11..7eb4d3aed29 100644 --- a/spec/javascripts/notes/components/note_app_spec.js +++ b/spec/javascripts/notes/components/note_app_spec.js @@ -3,7 +3,9 @@ import _ from 'underscore'; import Vue from 'vue'; import notesApp from '~/notes/components/notes_app.vue'; import service from '~/notes/services/notes_service'; +import createStore from '~/notes/stores'; import '~/behaviors/markdown/render_gfm'; +import { mountComponentWithStore } from 'spec/helpers'; import * as mockData from '../mock_data'; const vueMatchers = { @@ -22,6 +24,7 @@ const vueMatchers = { describe('note_app', () => { let mountComponent; let vm; + let store; beforeEach(() => { jasmine.addMatchers(vueMatchers); @@ -29,16 +32,18 @@ describe('note_app', () => { const IssueNotesApp = Vue.extend(notesApp); - mountComponent = (data) => { + store = createStore(); + mountComponent = data => { const props = data || { noteableData: mockData.noteableDataMock, notesData: mockData.notesDataMock, userData: mockData.userDataMock, }; - return new IssueNotesApp({ - propsData: props, - }).$mount(); + return mountComponentWithStore(IssueNotesApp, { + props, + store, + }); }; }); @@ -48,9 +53,11 @@ describe('note_app', () => { describe('set data', () => { const responseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([]), { - status: 200, - })); + next( + request.respondWith(JSON.stringify([]), { + status: 200, + }), + ); }; beforeEach(() => { @@ -74,8 +81,8 @@ describe('note_app', () => { expect(vm.$store.state.userData).toEqual(mockData.userDataMock); }); - it('should fetch notes', () => { - expect(vm.$store.state.notes).toEqual([]); + it('should fetch discussions', () => { + expect(vm.$store.state.discussions).toEqual([]); }); }); @@ -89,15 +96,20 @@ describe('note_app', () => { Vue.http.interceptors = _.without(Vue.http.interceptors, mockData.individualNoteInterceptor); }); - it('should render list of notes', (done) => { - const note = mockData.INDIVIDUAL_NOTE_RESPONSE_MAP.GET['/gitlab-org/gitlab-ce/issues/26/discussions.json'][0].notes[0]; + it('should render list of notes', done => { + const note = + mockData.INDIVIDUAL_NOTE_RESPONSE_MAP.GET[ + '/gitlab-org/gitlab-ce/issues/26/discussions.json' + ][0].notes[0]; setTimeout(() => { expect( vm.$el.querySelector('.main-notes-list .note-header-author-name').textContent.trim(), ).toEqual(note.author.name); - expect(vm.$el.querySelector('.main-notes-list .note-text').innerHTML).toEqual(note.note_html); + expect(vm.$el.querySelector('.main-notes-list .note-text').innerHTML).toEqual( + note.note_html, + ); done(); }, 0); }); @@ -110,9 +122,9 @@ describe('note_app', () => { }); it('should render form comment button as disabled', () => { - expect( - vm.$el.querySelector('.js-note-new-discussion').getAttribute('disabled'), - ).toEqual('disabled'); + expect(vm.$el.querySelector('.js-note-new-discussion').getAttribute('disabled')).toEqual( + 'disabled', + ); }); }); @@ -135,7 +147,7 @@ describe('note_app', () => { describe('update note', () => { describe('individual note', () => { - beforeEach((done) => { + beforeEach(done => { Vue.http.interceptors.push(mockData.individualNoteInterceptor); spyOn(service, 'updateNote').and.callThrough(); vm = mountComponent(); @@ -156,7 +168,7 @@ describe('note_app', () => { expect(vm).toIncludeElement('.js-vue-issue-note-form'); }); - it('calls the service to update the note', (done) => { + it('calls the service to update the note', done => { vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note'; vm.$el.querySelector('.js-vue-issue-save').click(); @@ -169,7 +181,7 @@ describe('note_app', () => { }); describe('discussion note', () => { - beforeEach((done) => { + beforeEach(done => { Vue.http.interceptors.push(mockData.discussionNoteInterceptor); spyOn(service, 'updateNote').and.callThrough(); vm = mountComponent(); @@ -191,7 +203,7 @@ describe('note_app', () => { expect(vm).toIncludeElement('.js-vue-issue-note-form'); }); - it('updates the note and resets the edit form', (done) => { + it('updates the note and resets the edit form', done => { vm.$el.querySelector('.js-vue-issue-note-form').value = 'this is a note'; vm.$el.querySelector('.js-vue-issue-save').click(); @@ -211,12 +223,16 @@ describe('note_app', () => { it('should render markdown docs url', () => { const { markdownDocsPath } = mockData.notesDataMock; - expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual('Markdown'); + expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual( + 'Markdown', + ); }); it('should render quick action docs url', () => { const { quickActionsDocsPath } = mockData.notesDataMock; - expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual('quick actions'); + expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual( + 'quick actions', + ); }); }); @@ -230,7 +246,7 @@ describe('note_app', () => { Vue.http.interceptors = _.without(Vue.http.interceptors, mockData.individualNoteInterceptor); }); - it('should render markdown docs url', (done) => { + it('should render markdown docs url', done => { setTimeout(() => { vm.$el.querySelector('.js-note-edit').click(); const { markdownDocsPath } = mockData.notesDataMock; @@ -244,15 +260,15 @@ describe('note_app', () => { }, 0); }); - it('should not render quick actions docs url', (done) => { + it('should not render quick actions docs url', done => { setTimeout(() => { vm.$el.querySelector('.js-note-edit').click(); const { quickActionsDocsPath } = mockData.notesDataMock; Vue.nextTick(() => { - expect( - vm.$el.querySelector(`.edit-note a[href="${quickActionsDocsPath}"]`), - ).toEqual(null); + expect(vm.$el.querySelector(`.edit-note a[href="${quickActionsDocsPath}"]`)).toEqual( + null, + ); done(); }); }, 0); diff --git a/spec/javascripts/notes/components/note_awards_list_spec.js b/spec/javascripts/notes/components/note_awards_list_spec.js index 1c30d8691b1..9d98ba219da 100644 --- a/spec/javascripts/notes/components/note_awards_list_spec.js +++ b/spec/javascripts/notes/components/note_awards_list_spec.js @@ -1,15 +1,17 @@ import Vue from 'vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import awardsNote from '~/notes/components/note_awards_list.vue'; import { noteableDataMock, notesDataMock } from '../mock_data'; describe('note_awards_list component', () => { + let store; let vm; let awardsMock; beforeEach(() => { const Component = Vue.extend(awardsNote); + store = createStore(); store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); awardsMock = [ @@ -41,7 +43,9 @@ describe('note_awards_list component', () => { it('should render awarded emojis', () => { expect(vm.$el.querySelector('.js-awards-block button [data-name="flag_tz"]')).toBeDefined(); - expect(vm.$el.querySelector('.js-awards-block button [data-name="cartwheel_tone3"]')).toBeDefined(); + expect( + vm.$el.querySelector('.js-awards-block button [data-name="cartwheel_tone3"]'), + ).toBeDefined(); }); it('should be possible to remove awarded emoji', () => { diff --git a/spec/javascripts/notes/components/note_body_spec.js b/spec/javascripts/notes/components/note_body_spec.js index 4e551496ff0..efad0785afe 100644 --- a/spec/javascripts/notes/components/note_body_spec.js +++ b/spec/javascripts/notes/components/note_body_spec.js @@ -1,15 +1,16 @@ - import Vue from 'vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import noteBody from '~/notes/components/note_body.vue'; import { noteableDataMock, notesDataMock, note } from '../mock_data'; describe('issue_note_body component', () => { + let store; let vm; beforeEach(() => { const Component = Vue.extend(noteBody); + store = createStore(); store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); @@ -37,7 +38,7 @@ describe('issue_note_body component', () => { }); describe('isEditing', () => { - beforeEach((done) => { + beforeEach(done => { vm.isEditing = true; Vue.nextTick(done); }); diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js index 413d4f69434..95d400ab3df 100644 --- a/spec/javascripts/notes/components/note_form_spec.js +++ b/spec/javascripts/notes/components/note_form_spec.js @@ -1,16 +1,18 @@ import Vue from 'vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import issueNoteForm from '~/notes/components/note_form.vue'; import { noteableDataMock, notesDataMock } from '../mock_data'; import { keyboardDownEvent } from '../../issue_show/helpers'; describe('issue_note_form component', () => { + let store; let vm; let props; beforeEach(() => { const Component = Vue.extend(issueNoteForm); + store = createStore(); store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); @@ -31,14 +33,18 @@ describe('issue_note_form component', () => { }); describe('conflicts editing', () => { - it('should show conflict message if note changes outside the component', (done) => { + it('should show conflict message if note changes outside the component', done => { vm.isEditing = true; vm.noteBody = 'Foo'; - const message = 'This comment has changed since you started editing, please review the updated comment to ensure information is not lost.'; + const message = + 'This comment has changed since you started editing, please review the updated comment to ensure information is not lost.'; Vue.nextTick(() => { expect( - vm.$el.querySelector('.js-conflict-edit-warning').textContent.replace(/\s+/g, ' ').trim(), + vm.$el + .querySelector('.js-conflict-edit-warning') + .textContent.replace(/\s+/g, ' ') + .trim(), ).toEqual(message); done(); }); @@ -47,14 +53,16 @@ describe('issue_note_form component', () => { describe('form', () => { it('should render text area with placeholder', () => { - expect( - vm.$el.querySelector('textarea').getAttribute('placeholder'), - ).toEqual('Write a comment or drag your files here…'); + expect(vm.$el.querySelector('textarea').getAttribute('placeholder')).toEqual( + 'Write a comment or drag your files here…', + ); }); it('should link to markdown docs', () => { const { markdownDocsPath } = notesDataMock; - expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual('Markdown'); + expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual( + 'Markdown', + ); }); describe('keyboard events', () => { @@ -87,7 +95,7 @@ describe('issue_note_form component', () => { }); describe('actions', () => { - it('should be possible to cancel', (done) => { + it('should be possible to cancel', done => { spyOn(vm, 'cancelHandler').and.callThrough(); vm.isEditing = true; @@ -101,7 +109,7 @@ describe('issue_note_form component', () => { }); }); - it('should be possible to update the note', (done) => { + it('should be possible to update the note', done => { vm.isEditing = true; Vue.nextTick(() => { diff --git a/spec/javascripts/notes/components/note_header_spec.js b/spec/javascripts/notes/components/note_header_spec.js index 5636f8d1a9f..a3c6bf78988 100644 --- a/spec/javascripts/notes/components/note_header_spec.js +++ b/spec/javascripts/notes/components/note_header_spec.js @@ -1,13 +1,15 @@ import Vue from 'vue'; import noteHeader from '~/notes/components/note_header.vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; describe('note_header component', () => { + let store; let vm; let Component; beforeEach(() => { Component = Vue.extend(noteHeader); + store = createStore(); }); afterEach(() => { @@ -38,12 +40,8 @@ describe('note_header component', () => { }); it('should render user information', () => { - expect( - vm.$el.querySelector('.note-header-author-name').textContent.trim(), - ).toEqual('Root'); - expect( - vm.$el.querySelector('.note-header-info a').getAttribute('href'), - ).toEqual('/root'); + expect(vm.$el.querySelector('.note-header-author-name').textContent.trim()).toEqual('Root'); + expect(vm.$el.querySelector('.note-header-info a').getAttribute('href')).toEqual('/root'); }); it('should render timestamp link', () => { @@ -78,7 +76,7 @@ describe('note_header component', () => { expect(vm.$el.querySelector('.js-vue-toggle-button')).toBeDefined(); }); - it('emits toggle event on click', (done) => { + it('emits toggle event on click', done => { spyOn(vm, '$emit'); vm.$el.querySelector('.js-vue-toggle-button').click(); @@ -89,24 +87,24 @@ describe('note_header component', () => { }); }); - it('renders up arrow when open', (done) => { + it('renders up arrow when open', done => { vm.expanded = true; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.js-vue-toggle-button i').classList, - ).toContain('fa-chevron-up'); + expect(vm.$el.querySelector('.js-vue-toggle-button i').classList).toContain( + 'fa-chevron-up', + ); done(); }); }); - it('renders down arrow when closed', (done) => { + it('renders down arrow when closed', done => { vm.expanded = false; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.js-vue-toggle-button i').classList, - ).toContain('fa-chevron-down'); + expect(vm.$el.querySelector('.js-vue-toggle-button i').classList).toContain( + 'fa-chevron-down', + ); done(); }); }); diff --git a/spec/javascripts/notes/components/note_signed_out_widget_spec.js b/spec/javascripts/notes/components/note_signed_out_widget_spec.js index 6cba8053888..e217a2caa73 100644 --- a/spec/javascripts/notes/components/note_signed_out_widget_spec.js +++ b/spec/javascripts/notes/components/note_signed_out_widget_spec.js @@ -1,13 +1,15 @@ import Vue from 'vue'; import noteSignedOut from '~/notes/components/note_signed_out_widget.vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import { notesDataMock } from '../mock_data'; describe('note_signed_out_widget component', () => { + let store; let vm; beforeEach(() => { const Component = Vue.extend(noteSignedOut); + store = createStore(); store.dispatch('setNotesData', notesDataMock); vm = new Component({ @@ -20,18 +22,20 @@ describe('note_signed_out_widget component', () => { }); it('should render sign in link provided in the store', () => { - expect( - vm.$el.querySelector(`a[href="${notesDataMock.newSessionPath}"]`).textContent, - ).toEqual('sign in'); + expect(vm.$el.querySelector(`a[href="${notesDataMock.newSessionPath}"]`).textContent).toEqual( + 'sign in', + ); }); it('should render register link provided in the store', () => { - expect( - vm.$el.querySelector(`a[href="${notesDataMock.registerPath}"]`).textContent, - ).toEqual('register'); + expect(vm.$el.querySelector(`a[href="${notesDataMock.registerPath}"]`).textContent).toEqual( + 'register', + ); }); it('should render information text', () => { - expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual('Please register or sign in to reply'); + expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual( + 'Please register or sign in to reply', + ); }); }); diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js index cda550760fe..058ddb6202f 100644 --- a/spec/javascripts/notes/components/noteable_discussion_spec.js +++ b/spec/javascripts/notes/components/noteable_discussion_spec.js @@ -1,21 +1,24 @@ import Vue from 'vue'; -import store from '~/notes/stores'; -import issueDiscussion from '~/notes/components/noteable_discussion.vue'; +import createStore from '~/notes/stores'; +import noteableDiscussion from '~/notes/components/noteable_discussion.vue'; +import '~/behaviors/markdown/render_gfm'; import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data'; -describe('issue_discussion component', () => { +describe('noteable_discussion component', () => { + let store; let vm; beforeEach(() => { - const Component = Vue.extend(issueDiscussion); + const Component = Vue.extend(noteableDiscussion); + store = createStore(); store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); vm = new Component({ store, propsData: { - note: discussionMock, + discussion: discussionMock, }, }).$mount(); }); @@ -55,4 +58,74 @@ describe('issue_discussion component', () => { ).toBeNull(); }); }); + + describe('computed', () => { + describe('hasMultipleUnresolvedDiscussions', () => { + it('is false if there are no unresolved discussions', done => { + spyOnProperty(vm, 'unresolvedDiscussions').and.returnValue([]); + + Vue.nextTick() + .then(() => { + expect(vm.hasMultipleUnresolvedDiscussions).toBe(false); + }) + .then(done) + .catch(done.fail); + }); + + it('is false if there is one unresolved discussion', done => { + spyOnProperty(vm, 'unresolvedDiscussions').and.returnValue([discussionMock]); + + Vue.nextTick() + .then(() => { + expect(vm.hasMultipleUnresolvedDiscussions).toBe(false); + }) + .then(done) + .catch(done.fail); + }); + + it('is true if there are two unresolved discussions', done => { + spyOnProperty(vm, 'unresolvedDiscussions').and.returnValue([{}, {}]); + + Vue.nextTick() + .then(() => { + expect(vm.hasMultipleUnresolvedDiscussions).toBe(true); + }) + .then(done) + .catch(done.fail); + }); + }); + }); + + describe('methods', () => { + describe('jumpToNextDiscussion', () => { + it('expands next unresolved discussion', () => { + spyOn(vm, 'expandDiscussion').and.stub(); + const discussions = [ + discussionMock, + { + ...discussionMock, + id: discussionMock.id + 1, + notes: [{ ...discussionMock.notes[0], resolved: true }], + }, + { + ...discussionMock, + id: discussionMock.id + 2, + notes: [{ ...discussionMock.notes[0], resolved: false }], + }, + ]; + const nextDiscussionId = discussionMock.id + 2; + store.replaceState({ + ...store.state, + discussions, + }); + setFixtures(` +
+ `); + + vm.jumpToNextDiscussion(); + + expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: nextDiscussionId }); + }); + }); + }); }); diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js index 2ffdec7314d..a31d17cacbb 100644 --- a/spec/javascripts/notes/components/noteable_note_spec.js +++ b/spec/javascripts/notes/components/noteable_note_spec.js @@ -1,16 +1,18 @@ import $ from 'jquery'; import _ from 'underscore'; import Vue from 'vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import issueNote from '~/notes/components/noteable_note.vue'; import { noteableDataMock, notesDataMock, note } from '../mock_data'; describe('issue_note', () => { + let store; let vm; beforeEach(() => { const Component = Vue.extend(issueNote); + store = createStore(); store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); @@ -27,11 +29,14 @@ describe('issue_note', () => { }); it('should render user information', () => { - expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual(note.author.avatar_url); + expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual( + note.author.avatar_url, + ); }); it('should render note header content', () => { - expect(vm.$el.querySelector('.note-header .note-header-author-name').textContent.trim()).toEqual(note.author.name); + const el = vm.$el.querySelector('.note-header .note-header-author-name'); + expect(el.textContent.trim()).toEqual(note.author.name); }); it('should render note actions', () => { @@ -42,7 +47,7 @@ describe('issue_note', () => { expect(vm.$el.querySelector('.note-text').innerHTML).toEqual(note.note_html); }); - it('prevents note preview xss', (done) => { + it('prevents note preview xss', done => { const imgSrc = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; const noteBody = ``; const alertSpy = spyOn(window, 'alert'); @@ -58,7 +63,7 @@ describe('issue_note', () => { }); describe('cancel edit', () => { - it('restores content of updated note', (done) => { + it('restores content of updated note', done => { const noteBody = 'updated note text'; vm.updateNote = () => Promise.resolve(); diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js index fa7adc32193..547efa32694 100644 --- a/spec/javascripts/notes/mock_data.js +++ b/spec/javascripts/notes/mock_data.js @@ -51,6 +51,7 @@ export const noteableDataMock = { time_estimate: 0, title: '14', total_time_spent: 0, + noteable_note_url: '/group/project/merge_requests/1#note_1', updated_at: '2017-08-04T09:53:01.226Z', updated_by_id: 1, web_url: '/gitlab-org/gitlab-ce/issues/26', @@ -99,6 +100,8 @@ export const individualNote = { { name: 'art', user: { id: 1, name: 'Root', username: 'root' } }, ], toggle_award_path: '/gitlab-org/gitlab-ce/notes/1390/toggle_award_emoji', + noteable_note_url: '/group/project/merge_requests/1#note_1', + note_url: '/group/project/merge_requests/1#note_1', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390&user_id=1', path: '/gitlab-org/gitlab-ce/notes/1390', @@ -157,6 +160,8 @@ export const note = { }, ], toggle_award_path: '/gitlab-org/gitlab-ce/notes/546/toggle_award_emoji', + note_url: '/group/project/merge_requests/1#note_1', + noteable_note_url: '/group/project/merge_requests/1#note_1', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1', path: '/gitlab-org/gitlab-ce/notes/546', @@ -198,6 +203,7 @@ export const discussionMock = { discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1', emoji_awardable: true, award_emoji: [], + noteable_note_url: '/group/project/merge_requests/1#note_1', toggle_award_path: '/gitlab-org/gitlab-ce/notes/1395/toggle_award_emoji', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1395&user_id=1', @@ -244,6 +250,7 @@ export const discussionMock = { emoji_awardable: true, award_emoji: [], toggle_award_path: '/gitlab-org/gitlab-ce/notes/1396/toggle_award_emoji', + noteable_note_url: '/group/project/merge_requests/1#note_1', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1396&user_id=1', path: '/gitlab-org/gitlab-ce/notes/1396', @@ -288,6 +295,7 @@ export const discussionMock = { discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1', emoji_awardable: true, award_emoji: [], + noteable_note_url: '/group/project/merge_requests/1#note_1', toggle_award_path: '/gitlab-org/gitlab-ce/notes/1437/toggle_award_emoji', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1437&user_id=1', @@ -335,6 +343,7 @@ export const loggedOutnoteableData = { can_create_note: false, can_update: false, }, + noteable_note_url: '/group/project/merge_requests/1#note_1', create_note_path: '/gitlab-org/gitlab-ce/notes?target_id=98&target_type=issue', preview_note_path: '/gitlab-org/gitlab-ce/preview_markdown?quick_actions_target_id=98&quick_actions_target_type=Issue', @@ -469,6 +478,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = { }, }, ], + noteable_note_url: '/group/project/merge_requests/1#note_1', toggle_award_path: '/gitlab-org/gitlab-ce/notes/1390/toggle_award_emoji', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390\u0026user_id=1', @@ -513,6 +523,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = { discussion_id: '70d5c92a4039a36c70100c6691c18c27e4b0a790', emoji_awardable: true, award_emoji: [], + noteable_note_url: '/group/project/merge_requests/1#note_1', toggle_award_path: '/gitlab-org/gitlab-ce/notes/1391/toggle_award_emoji', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1391\u0026user_id=1', @@ -567,6 +578,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = { discussion_id: 'a3ed36e29b1957efb3b68c53e2d7a2b24b1df052', emoji_awardable: true, award_emoji: [], + noteable_note_url: '/group/project/merge_requests/1#note_1', toggle_award_path: '/gitlab-org/gitlab-ce/notes/1471/toggle_award_emoji', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1', @@ -618,6 +630,7 @@ export const DISCUSSION_NOTE_RESPONSE_MAP = { emoji_awardable: true, award_emoji: [], toggle_award_path: '/gitlab-org/gitlab-ce/notes/1471/toggle_award_emoji', + noteable_note_url: '/group/project/merge_requests/1#note_1', report_abuse_path: '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1', path: '/gitlab-org/gitlab-ce/notes/1471', diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js index 520a25cc5c6..985c2f81ef3 100644 --- a/spec/javascripts/notes/stores/actions_spec.js +++ b/spec/javascripts/notes/stores/actions_spec.js @@ -2,7 +2,7 @@ import Vue from 'vue'; import _ from 'underscore'; import { headersInterceptor } from 'spec/helpers/vue_resource_helper'; import * as actions from '~/notes/stores/actions'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import testAction from '../../helpers/vuex_action_helper'; import { resetStore } from '../helpers'; import { @@ -14,6 +14,12 @@ import { } from '../mock_data'; describe('Actions Notes Store', () => { + let store; + + beforeEach(() => { + store = createStore(); + }); + afterEach(() => { resetStore(store); }); @@ -76,7 +82,7 @@ describe('Actions Notes Store', () => { actions.setInitialNotes, [individualNote], { notes: [] }, - [{ type: 'SET_INITIAL_NOTES', payload: [individualNote] }], + [{ type: 'SET_INITIAL_DISCUSSIONS', payload: [individualNote] }], [], done, ); @@ -109,6 +115,19 @@ describe('Actions Notes Store', () => { }); }); + describe('expandDiscussion', () => { + it('should expand discussion', done => { + testAction( + actions.expandDiscussion, + { discussionId: discussionMock.id }, + { notes: [discussionMock] }, + [{ type: 'EXPAND_DISCUSSION', payload: { discussionId: discussionMock.id } }], + [], + done, + ); + }); + }); + describe('async methods', () => { const interceptor = (request, next) => { next( @@ -194,7 +213,14 @@ describe('Actions Notes Store', () => { }); it('sets issue state as reopened', done => { - testAction(actions.toggleIssueLocalState, 'reopened', {}, [{ type: 'REOPEN_ISSUE' }], [], done); + testAction( + actions.toggleIssueLocalState, + 'reopened', + {}, + [{ type: 'REOPEN_ISSUE' }], + [], + done, + ); }); }); @@ -239,13 +265,7 @@ describe('Actions Notes Store', () => { .dispatch('poll') .then(() => new Promise(resolve => requestAnimationFrame(resolve))) .then(() => { - expect(Vue.http.get).toHaveBeenCalledWith(jasmine.anything(), { - url: jasmine.anything(), - method: 'get', - headers: { - 'X-Last-Fetched-At': undefined, - }, - }); + expect(Vue.http.get).toHaveBeenCalled(); expect(store.state.lastFetchedAt).toBe('123456'); jasmine.clock().tick(1500); diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js index e5550580bf8..5501e50e97b 100644 --- a/spec/javascripts/notes/stores/getters_spec.js +++ b/spec/javascripts/notes/stores/getters_spec.js @@ -1,12 +1,18 @@ import * as getters from '~/notes/stores/getters'; -import { notesDataMock, userDataMock, noteableDataMock, individualNote, collapseNotesMock } from '../mock_data'; +import { + notesDataMock, + userDataMock, + noteableDataMock, + individualNote, + collapseNotesMock, +} from '../mock_data'; describe('Getters Notes Store', () => { let state; beforeEach(() => { state = { - notes: [individualNote], + discussions: [individualNote], targetNoteHash: 'hash', lastFetchedAt: 'timestamp', @@ -15,15 +21,15 @@ describe('Getters Notes Store', () => { noteableData: noteableDataMock, }; }); - describe('notes', () => { - it('should return all notes in the store', () => { - expect(getters.notes(state)).toEqual([individualNote]); + describe('discussions', () => { + it('should return all discussions in the store', () => { + expect(getters.discussions(state)).toEqual([individualNote]); }); }); describe('Collapsed notes', () => { const stateCollapsedNotes = { - notes: collapseNotesMock, + discussions: collapseNotesMock, targetNoteHash: 'hash', lastFetchedAt: 'timestamp', @@ -33,7 +39,7 @@ describe('Getters Notes Store', () => { }; it('should return a single system note when a description was updated multiple times', () => { - expect(getters.notes(stateCollapsedNotes).length).toEqual(1); + expect(getters.discussions(stateCollapsedNotes).length).toEqual(1); }); }); diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js index 98f101d6bc5..556a1c244c0 100644 --- a/spec/javascripts/notes/stores/mutation_spec.js +++ b/spec/javascripts/notes/stores/mutation_spec.js @@ -1,5 +1,12 @@ import mutations from '~/notes/stores/mutations'; -import { note, discussionMock, notesDataMock, userDataMock, noteableDataMock, individualNote } from '../mock_data'; +import { + note, + discussionMock, + notesDataMock, + userDataMock, + noteableDataMock, + individualNote, +} from '../mock_data'; describe('Notes Store mutations', () => { describe('ADD_NEW_NOTE', () => { @@ -7,7 +14,7 @@ describe('Notes Store mutations', () => { let noteData; beforeEach(() => { - state = { notes: [] }; + state = { discussions: [] }; noteData = { expanded: true, id: note.discussion_id, @@ -20,46 +27,60 @@ describe('Notes Store mutations', () => { it('should add a new note to an array of notes', () => { expect(state).toEqual({ - notes: [noteData], + discussions: [noteData], }); - expect(state.notes.length).toBe(1); + expect(state.discussions.length).toBe(1); }); it('should not add the same note to the notes array', () => { mutations.ADD_NEW_NOTE(state, note); - expect(state.notes.length).toBe(1); + expect(state.discussions.length).toBe(1); }); }); describe('ADD_NEW_REPLY_TO_DISCUSSION', () => { it('should add a reply to a specific discussion', () => { - const state = { notes: [discussionMock] }; + const state = { discussions: [discussionMock] }; const newReply = Object.assign({}, note, { discussion_id: discussionMock.id }); mutations.ADD_NEW_REPLY_TO_DISCUSSION(state, newReply); - expect(state.notes[0].notes.length).toEqual(4); + expect(state.discussions[0].notes.length).toEqual(4); }); }); describe('DELETE_NOTE', () => { it('should delete a note ', () => { - const state = { notes: [discussionMock] }; + const state = { discussions: [discussionMock] }; const toDelete = discussionMock.notes[0]; const lengthBefore = discussionMock.notes.length; mutations.DELETE_NOTE(state, toDelete); - expect(state.notes[0].notes.length).toEqual(lengthBefore - 1); + expect(state.discussions[0].notes.length).toEqual(lengthBefore - 1); + }); + }); + + describe('EXPAND_DISCUSSION', () => { + it('should expand a collapsed discussion', () => { + const discussion = Object.assign({}, discussionMock, { expanded: false }); + + const state = { + discussions: [discussion], + }; + + mutations.EXPAND_DISCUSSION(state, { discussionId: discussion.id }); + + expect(state.discussions[0].expanded).toEqual(true); }); }); describe('REMOVE_PLACEHOLDER_NOTES', () => { it('should remove all placeholder notes in indivudal notes and discussion', () => { const placeholderNote = Object.assign({}, individualNote, { isPlaceholderNote: true }); - const state = { notes: [placeholderNote] }; + const state = { discussions: [placeholderNote] }; mutations.REMOVE_PLACEHOLDER_NOTES(state); - expect(state.notes).toEqual([]); + expect(state.discussions).toEqual([]); }); }); @@ -96,26 +117,29 @@ describe('Notes Store mutations', () => { }); }); - describe('SET_INITIAL_NOTES', () => { + describe('SET_INITIAL_DISCUSSIONS', () => { it('should set the initial notes received', () => { const state = { - notes: [], + discussions: [], }; const legacyNote = { id: 2, individual_note: true, - notes: [{ - note: '1', - }, { - note: '2', - }], + notes: [ + { + note: '1', + }, + { + note: '2', + }, + ], }; - mutations.SET_INITIAL_NOTES(state, [note, legacyNote]); - expect(state.notes[0].id).toEqual(note.id); - expect(state.notes[1].notes[0].note).toBe(legacyNote.notes[0].note); - expect(state.notes[2].notes[0].note).toBe(legacyNote.notes[1].note); - expect(state.notes.length).toEqual(3); + mutations.SET_INITIAL_DISCUSSIONS(state, [note, legacyNote]); + expect(state.discussions[0].id).toEqual(note.id); + expect(state.discussions[1].notes[0].note).toBe(legacyNote.notes[0].note); + expect(state.discussions[2].notes[0].note).toBe(legacyNote.notes[1].note); + expect(state.discussions.length).toEqual(3); }); }); @@ -144,17 +168,17 @@ describe('Notes Store mutations', () => { describe('SHOW_PLACEHOLDER_NOTE', () => { it('should set a placeholder note', () => { const state = { - notes: [], + discussions: [], }; mutations.SHOW_PLACEHOLDER_NOTE(state, note); - expect(state.notes[0].isPlaceholderNote).toEqual(true); + expect(state.discussions[0].isPlaceholderNote).toEqual(true); }); }); describe('TOGGLE_AWARD', () => { it('should add award if user has not reacted yet', () => { const state = { - notes: [note], + discussions: [note], userData: userDataMock, }; @@ -164,9 +188,9 @@ describe('Notes Store mutations', () => { }; mutations.TOGGLE_AWARD(state, data); - const lastIndex = state.notes[0].award_emoji.length - 1; + const lastIndex = state.discussions[0].award_emoji.length - 1; - expect(state.notes[0].award_emoji[lastIndex]).toEqual({ + expect(state.discussions[0].award_emoji[lastIndex]).toEqual({ name: 'cartwheel', user: { id: userDataMock.id, name: userDataMock.name, username: userDataMock.username }, }); @@ -174,7 +198,7 @@ describe('Notes Store mutations', () => { it('should remove award if user already reacted', () => { const state = { - notes: [note], + discussions: [note], userData: { id: 1, name: 'Administrator', @@ -187,7 +211,7 @@ describe('Notes Store mutations', () => { awardName: 'bath_tone3', }; mutations.TOGGLE_AWARD(state, data); - expect(state.notes[0].award_emoji.length).toEqual(2); + expect(state.discussions[0].award_emoji.length).toEqual(2); }); }); @@ -196,43 +220,43 @@ describe('Notes Store mutations', () => { const discussion = Object.assign({}, discussionMock, { expanded: false }); const state = { - notes: [discussion], + discussions: [discussion], }; mutations.TOGGLE_DISCUSSION(state, { discussionId: discussion.id }); - expect(state.notes[0].expanded).toEqual(true); + expect(state.discussions[0].expanded).toEqual(true); }); it('should close a opened discussion', () => { const state = { - notes: [discussionMock], + discussions: [discussionMock], }; mutations.TOGGLE_DISCUSSION(state, { discussionId: discussionMock.id }); - expect(state.notes[0].expanded).toEqual(false); + expect(state.discussions[0].expanded).toEqual(false); }); }); describe('UPDATE_NOTE', () => { it('should update a note', () => { const state = { - notes: [individualNote], + discussions: [individualNote], }; const updated = Object.assign({}, individualNote.notes[0], { note: 'Foo' }); mutations.UPDATE_NOTE(state, updated); - expect(state.notes[0].notes[0].note).toEqual('Foo'); + expect(state.discussions[0].notes[0].note).toEqual('Foo'); }); }); describe('CLOSE_ISSUE', () => { it('should set issue as closed', () => { const state = { - notes: [], + discussions: [], targetNoteHash: null, lastFetchedAt: null, isToggleStateButtonLoading: false, @@ -249,7 +273,7 @@ describe('Notes Store mutations', () => { describe('REOPEN_ISSUE', () => { it('should set issue as closed', () => { const state = { - notes: [], + discussions: [], targetNoteHash: null, lastFetchedAt: null, isToggleStateButtonLoading: false, @@ -266,7 +290,7 @@ describe('Notes Store mutations', () => { describe('TOGGLE_STATE_BUTTON_LOADING', () => { it('should set isToggleStateButtonLoading as true', () => { const state = { - notes: [], + discussions: [], targetNoteHash: null, lastFetchedAt: null, isToggleStateButtonLoading: false, @@ -281,7 +305,7 @@ describe('Notes Store mutations', () => { it('should set isToggleStateButtonLoading as false', () => { const state = { - notes: [], + discussions: [], targetNoteHash: null, lastFetchedAt: null, isToggleStateButtonLoading: true, diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 2854263a25a..faeedae40e9 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -35,11 +35,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; describe('Notes', function() { const FLASH_TYPE_ALERT = 'alert'; const NOTES_POST_PATH = /(.*)\/notes\?html=true$/; - var commentsTemplate = 'merge_requests/merge_request_with_comment.html.raw'; - preloadFixtures(commentsTemplate); + var fixture = 'snippets/show.html.raw'; + preloadFixtures(fixture); beforeEach(function() { - loadFixtures(commentsTemplate); + loadFixtures(fixture); gl.utils.disableButtonIfEmptyField = _.noop; window.project_uploads_path = 'http://test.host/uploads'; $('body').attr('data-page', 'projects:merge_requets:show'); @@ -65,16 +65,9 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; let mock; beforeEach(function() { - spyOn(axios, 'patch').and.callThrough(); + spyOn(axios, 'patch').and.callFake(() => new Promise(() => {})); mock = new MockAdapter(axios); - - mock - .onPatch( - `${ - gl.TEST_HOST - }/frontend-fixtures/merge-requests-project/merge_requests/1.json`, - ) - .reply(200, {}); + mock.onAny().reply(200, {}); $('.js-comment-button').on('click', function(e) { e.preventDefault(); @@ -90,26 +83,17 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; const changeEvent = document.createEvent('HTMLEvents'); changeEvent.initEvent('change', true, true); $('input[type=checkbox]') - .attr('checked', true)[1] + .attr('checked', true)[0] .dispatchEvent(changeEvent); - expect($('.js-task-list-field.original-task-list').val()).toBe( - '- [x] Task List Item', - ); + expect($('.js-task-list-field.original-task-list').val()).toBe('- [x] Task List Item'); }); it('submits an ajax request on tasklist:changed', function(done) { $('.js-task-list-container').trigger('tasklist:changed'); setTimeout(() => { - expect(axios.patch).toHaveBeenCalledWith( - `${ - gl.TEST_HOST - }/frontend-fixtures/merge-requests-project/merge_requests/1.json`, - { - note: { note: '' }, - }, - ); + expect(axios.patch).toHaveBeenCalled(); done(); }); }); @@ -200,9 +184,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; updatedNote.note = 'bar'; this.notes.updateNote(updatedNote, $targetNote); - expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith( - $targetNote, - ); + expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote); expect(this.notes.setupNewNote).toHaveBeenCalled(); done(); @@ -282,10 +264,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; Notes.isNewNote.and.returnValue(true); Notes.prototype.renderNote.call(notes, note, null, $notesList); - expect(Notes.animateAppendNote).toHaveBeenCalledWith( - note.html, - $notesList, - ); + expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList); }); }); @@ -300,10 +279,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; Notes.prototype.renderNote.call(notes, note, null, $notesList); - expect(Notes.animateUpdateNote).toHaveBeenCalledWith( - note.html, - $note, - ); + expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note); expect(notes.setupNewNote).toHaveBeenCalledWith($newNote); }); @@ -331,10 +307,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; $notesList.find.and.returnValue($note); Notes.prototype.renderNote.call(notes, note, null, $notesList); - expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith( - note, - $note, - ); + expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note); }); }); }); @@ -400,10 +373,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; $form.length = 1; row = jasmine.createSpyObj('row', ['prevAll', 'first', 'find']); - notes = jasmine.createSpyObj('notes', [ - 'isParallelView', - 'updateNotesCount', - ]); + notes = jasmine.createSpyObj('notes', ['isParallelView', 'updateNotesCount']); notes.note_ids = []; spyOn(Notes, 'isNewNote'); @@ -464,10 +434,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; }); it('should call Notes.animateAppendNote', () => { - expect(Notes.animateAppendNote).toHaveBeenCalledWith( - note.html, - discussionContainer, - ); + expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer); }); }); }); @@ -571,9 +538,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; mockNotesPost(); $('.js-comment-button').click(); - expect($notesContainer.find('.note.being-posted').length > 0).toEqual( - true, - ); + expect($notesContainer.find('.note.being-posted').length > 0).toEqual(true); }); it('should remove placeholder note when new comment is done posting', done => { @@ -617,9 +582,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; $('.js-comment-button').click(); setTimeout(() => { - expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual( - true, - ); + expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(true); done(); }); @@ -734,14 +697,10 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough(); $('.js-comment-button').click(); - expect( - $notesContainer.find('.system-note.being-posted').length, - ).toEqual(1); // Placeholder shown + expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown setTimeout(() => { - expect( - $notesContainer.find('.system-note.being-posted').length, - ).toEqual(0); // Placeholder removed + expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed done(); }); }); @@ -815,9 +774,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; it('should return form metadata object from form reference', () => { $form.find('textarea.js-note-text').val(sampleComment); - const { formData, formContent, formAction } = this.notes.getFormData( - $form, - ); + const { formData, formContent, formAction } = this.notes.getFormData($form); expect(formData.indexOf(sampleComment) > -1).toBe(true); expect(formContent).toEqual(sampleComment); @@ -833,9 +790,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; const { formContent } = this.notes.getFormData($form); expect(_.escape).toHaveBeenCalledWith(sampleComment); - expect(formContent).toEqual( - '<script>alert("Boom!");</script>', - ); + expect(formContent).toEqual('<script>alert("Boom!");</script>'); }); }); @@ -845,8 +800,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; }); it('should return true when comment begins with a quick action', () => { - const sampleComment = - '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; const hasQuickActions = this.notes.hasQuickActions(sampleComment); expect(hasQuickActions).toBeTruthy(); @@ -870,8 +824,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; describe('stripQuickActions', () => { it('should strip quick actions from the comment which begins with a quick action', () => { this.notes = new Notes(); - const sampleComment = - '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; const stripedComment = this.notes.stripQuickActions(sampleComment); expect(stripedComment).toBe(''); @@ -879,8 +832,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; it('should strip quick actions from the comment but leaves plain comment if it is present', () => { this.notes = new Notes(); - const sampleComment = - '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this'; + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this'; const stripedComment = this.notes.stripQuickActions(sampleComment); expect(stripedComment).toBe('Merging this'); @@ -888,8 +840,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; it('should NOT strip string that has slashes within', () => { this.notes = new Notes(); - const sampleComment = - 'http://127.0.0.1:3000/root/gitlab-shell/issues/1'; + const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1'; const stripedComment = this.notes.stripQuickActions(sampleComment); expect(stripedComment).toBe(sampleComment); @@ -909,29 +860,21 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; it('should return executing quick action description when note has single quick action', () => { const sampleComment = '/close'; - expect( - this.notes.getQuickActionDescription( - sampleComment, - availableQuickActions, - ), - ).toBe('Applying command to close this issue'); + expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe( + 'Applying command to close this issue', + ); }); it('should return generic multiple quick action description when note has multiple quick actions', () => { const sampleComment = '/close\n/title [Duplicate] Issue foobar'; - expect( - this.notes.getQuickActionDescription( - sampleComment, - availableQuickActions, - ), - ).toBe('Applying multiple commands'); + expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe( + 'Applying multiple commands', + ); }); it('should return generic quick action description when available quick actions list is not populated', () => { const sampleComment = '/close\n/title [Duplicate] Issue foobar'; - expect(this.notes.getQuickActionDescription(sampleComment)).toBe( - 'Applying command', - ); + expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command'); }); }); @@ -961,17 +904,11 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; expect($tempNote.attr('id')).toEqual(uniqueId); expect($tempNote.hasClass('being-posted')).toBeTruthy(); expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); - $tempNote - .find('.timeline-icon > a, .note-header-info > a') - .each(function() { - expect($(this).attr('href')).toEqual(`/${currentUsername}`); - }); - expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual( - currentUserAvatar, - ); - expect( - $tempNote.find('.timeline-content').hasClass('discussion'), - ).toBeFalsy(); + $tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() { + expect($(this).attr('href')).toEqual(`/${currentUsername}`); + }); + expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar); + expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy(); expect( $tempNoteHeader .find('.d-none.d-sm-inline-block') @@ -1002,9 +939,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; }); expect($tempNote.prop('nodeName')).toEqual('LI'); - expect( - $tempNote.find('.timeline-content').hasClass('discussion'), - ).toBeTruthy(); + expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy(); }); it('should return a escaped user name', () => { @@ -1061,11 +996,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; }); it('shows a flash message', () => { - this.notes.addFlash( - 'Error message', - FLASH_TYPE_ALERT, - this.notes.parentTimeline.get(0), - ); + this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); expect($('.flash-alert').is(':visible')).toBeTruthy(); }); @@ -1078,11 +1009,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper'; }); it('hides visible flash message', () => { - this.notes.addFlash( - 'Error message 1', - FLASH_TYPE_ALERT, - this.notes.parentTimeline.get(0), - ); + this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); this.notes.clearFlash(); diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js index 68043b91bd0..78d8e9e572e 100644 --- a/spec/javascripts/pipelines/pipelines_table_row_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js @@ -4,7 +4,7 @@ import eventHub from '~/pipelines/event_hub'; describe('Pipelines Table Row', () => { const jsonFixtureName = 'pipelines/pipelines.json'; - const buildComponent = (pipeline) => { + const buildComponent = pipeline => { const PipelinesTableRowComponent = Vue.extend(tableRowComp); return new PipelinesTableRowComponent({ el: document.querySelector('.test-dom-element'), @@ -52,9 +52,9 @@ describe('Pipelines Table Row', () => { }); it('should render status text', () => { - expect( - component.$el.querySelector('.table-section.commit-link a').textContent, - ).toContain(pipeline.details.status.text); + expect(component.$el.querySelector('.table-section.commit-link a').textContent).toContain( + pipeline.details.status.text, + ); }); }); @@ -78,11 +78,15 @@ describe('Pipelines Table Row', () => { describe('when a user is provided', () => { it('should render user information', () => { expect( - component.$el.querySelector('.table-section:nth-child(2) a:nth-child(3)').getAttribute('href'), + component.$el + .querySelector('.table-section:nth-child(2) a:nth-child(3)') + .getAttribute('href'), ).toEqual(pipeline.user.path); expect( - component.$el.querySelector('.table-section:nth-child(2) img').getAttribute('data-original-title'), + component.$el + .querySelector('.table-section:nth-child(2) img') + .getAttribute('data-original-title'), ).toEqual(pipeline.user.name); }); }); @@ -105,7 +109,9 @@ describe('Pipelines Table Row', () => { } const commitAuthorLink = commitAuthorElement.getAttribute('href'); - const commitAuthorName = commitAuthorElement.querySelector('img.avatar').getAttribute('data-original-title'); + const commitAuthorName = commitAuthorElement + .querySelector('img.avatar') + .getAttribute('data-original-title'); return { commitAuthorElement, commitAuthorLink, commitAuthorName }; }; @@ -145,7 +151,8 @@ describe('Pipelines Table Row', () => { it('should render an icon for each stage', () => { expect( - component.$el.querySelectorAll('.table-section:nth-child(4) .js-builds-dropdown-button').length, + component.$el.querySelectorAll('.table-section:nth-child(4) .js-builds-dropdown-button') + .length, ).toEqual(pipeline.details.stages.length); }); }); @@ -167,7 +174,7 @@ describe('Pipelines Table Row', () => { }); it('emits `retryPipeline` event when retry button is clicked and toggles loading', () => { - eventHub.$on('retryPipeline', (endpoint) => { + eventHub.$on('retryPipeline', endpoint => { expect(endpoint).toEqual('/retry'); }); @@ -176,7 +183,7 @@ describe('Pipelines Table Row', () => { }); it('emits `openConfirmationModal` event when cancel button is clicked and toggles loading', () => { - eventHub.$on('openConfirmationModal', (data) => { + eventHub.$once('openConfirmationModal', data => { expect(data.endpoint).toEqual('/cancel'); expect(data.pipelineId).toEqual(pipeline.id); }); diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js index b10d8be6781..a4753ab7cde 100644 --- a/spec/javascripts/shortcuts_issuable_spec.js +++ b/spec/javascripts/shortcuts_issuable_spec.js @@ -4,71 +4,102 @@ import ShortcutsIssuable from '~/shortcuts_issuable'; initCopyAsGFM(); -describe('ShortcutsIssuable', function () { - const fixtureName = 'merge_requests/diff_comment.html.raw'; +const FORM_SELECTOR = '.js-main-target-form .js-vue-comment-form'; + +describe('ShortcutsIssuable', function() { + const fixtureName = 'snippets/show.html.raw'; preloadFixtures(fixtureName); + beforeEach(() => { loadFixtures(fixtureName); + $('body').append( + `
+ +
`, + ); document.querySelector('.js-new-note-form').classList.add('js-main-target-form'); this.shortcut = new ShortcutsIssuable(true); }); + + afterEach(() => { + $(FORM_SELECTOR).remove(); + }); + describe('replyWithSelectedText', () => { // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML. - const stubSelection = (html) => { + const stubSelection = html => { window.gl.utils.getSelectedFragment = () => { const node = document.createElement('div'); node.innerHTML = html; + return node; }; }; - beforeEach(() => { - this.selector = '.js-main-target-form #note_note'; - }); describe('with empty selection', () => { it('does not return an error', () => { - this.shortcut.replyWithSelectedText(true); - expect($(this.selector).val()).toBe(''); + ShortcutsIssuable.replyWithSelectedText(true); + + expect($(FORM_SELECTOR).val()).toBe(''); }); + it('triggers `focus`', () => { - this.shortcut.replyWithSelectedText(true); - expect(document.activeElement).toBe(document.querySelector(this.selector)); + const spy = spyOn(document.querySelector(FORM_SELECTOR), 'focus'); + ShortcutsIssuable.replyWithSelectedText(true); + + expect(spy).toHaveBeenCalled(); }); }); + describe('with any selection', () => { beforeEach(() => { stubSelection('

Selected text.

'); }); + it('leaves existing input intact', () => { - $(this.selector).val('This text was already here.'); - expect($(this.selector).val()).toBe('This text was already here.'); - this.shortcut.replyWithSelectedText(true); - expect($(this.selector).val()).toBe('This text was already here.\n\n> Selected text.\n\n'); + $(FORM_SELECTOR).val('This text was already here.'); + expect($(FORM_SELECTOR).val()).toBe('This text was already here.'); + + ShortcutsIssuable.replyWithSelectedText(true); + expect($(FORM_SELECTOR).val()).toBe('This text was already here.\n\n> Selected text.\n\n'); }); + it('triggers `input`', () => { let triggered = false; - $(this.selector).on('input', () => { + $(FORM_SELECTOR).on('input', () => { triggered = true; }); - this.shortcut.replyWithSelectedText(true); + + ShortcutsIssuable.replyWithSelectedText(true); expect(triggered).toBe(true); }); + it('triggers `focus`', () => { - this.shortcut.replyWithSelectedText(true); - expect(document.activeElement).toBe(document.querySelector(this.selector)); + const spy = spyOn(document.querySelector(FORM_SELECTOR), 'focus'); + ShortcutsIssuable.replyWithSelectedText(true); + + expect(spy).toHaveBeenCalled(); }); }); + describe('with a one-line selection', () => { it('quotes the selection', () => { stubSelection('

This text has been selected.

'); - this.shortcut.replyWithSelectedText(true); - expect($(this.selector).val()).toBe('> This text has been selected.\n\n'); + ShortcutsIssuable.replyWithSelectedText(true); + + expect($(FORM_SELECTOR).val()).toBe('> This text has been selected.\n\n'); }); }); + describe('with a multi-line selection', () => { it('quotes the selected lines as a group', () => { - stubSelection('

Selected line one.

\n

Selected line two.

\n

Selected line three.

'); - this.shortcut.replyWithSelectedText(true); - expect($(this.selector).val()).toBe('> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n'); + stubSelection( + '

Selected line one.

\n

Selected line two.

\n

Selected line three.

', + ); + ShortcutsIssuable.replyWithSelectedText(true); + + expect($(FORM_SELECTOR).val()).toBe( + '> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n', + ); }); }); }); diff --git a/spec/javascripts/shortcuts_spec.js b/spec/javascripts/shortcuts_spec.js index ee92295ef5e..94cded7ee37 100644 --- a/spec/javascripts/shortcuts_spec.js +++ b/spec/javascripts/shortcuts_spec.js @@ -2,10 +2,11 @@ import $ from 'jquery'; import Shortcuts from '~/shortcuts'; describe('Shortcuts', () => { - const fixtureName = 'merge_requests/diff_comment.html.raw'; - const createEvent = (type, target) => $.Event(type, { - target, - }); + const fixtureName = 'snippets/show.html.raw'; + const createEvent = (type, target) => + $.Event(type, { + target, + }); preloadFixtures(fixtureName); @@ -21,19 +22,19 @@ describe('Shortcuts', () => { it('focuses preview button in form', () => { Shortcuts.toggleMarkdownPreview( - createEvent('KeyboardEvent', document.querySelector('.js-new-note-form .js-note-text'), - )); + createEvent('KeyboardEvent', document.querySelector('.js-new-note-form .js-note-text')), + ); expect('focus').toHaveBeenTriggeredOn('.js-new-note-form .js-md-preview-button'); }); - it('focues preview button inside edit comment form', (done) => { + it('focues preview button inside edit comment form', done => { document.querySelector('.js-note-edit').click(); setTimeout(() => { Shortcuts.toggleMarkdownPreview( - createEvent('KeyboardEvent', document.querySelector('.edit-note .js-note-text'), - )); + createEvent('KeyboardEvent', document.querySelector('.edit-note .js-note-text')), + ); expect('focus').not.toHaveBeenTriggeredOn('.js-new-note-form .js-md-preview-button'); expect('focus').toHaveBeenTriggeredOn('.edit-note .js-md-preview-button'); diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 994011b262a..2626b439ca6 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -91,7 +91,8 @@ testsContext.keys().forEach(function(path) { try { testsContext(path); } catch (err) { - console.error('[ERROR] Unable to load spec: ', path); + console.log(err); + console.error('[GL SPEC RUNNER ERROR] Unable to load spec: ', path); describe('Test bundle', function() { it(`includes '${path}'`, function() { expect(err).toBeNull(); diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js index ba8ab0b2cd7..7e57c51bf29 100644 --- a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js +++ b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js @@ -1,13 +1,15 @@ import Vue from 'vue'; import issuePlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; import { userDataMock } from '../../../notes/mock_data'; describe('issue placeholder system note component', () => { + let store; let vm; beforeEach(() => { const Component = Vue.extend(issuePlaceholderNote); + store = createStore(); store.dispatch('setUserData', userDataMock); vm = new Component({ store, @@ -21,15 +23,23 @@ describe('issue placeholder system note component', () => { describe('user information', () => { it('should render user avatar with link', () => { - expect(vm.$el.querySelector('.user-avatar-link').getAttribute('href')).toEqual(userDataMock.path); - expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual(userDataMock.avatar_url); + expect(vm.$el.querySelector('.user-avatar-link').getAttribute('href')).toEqual( + userDataMock.path, + ); + expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual( + userDataMock.avatar_url, + ); }); }); describe('note content', () => { it('should render note header information', () => { - expect(vm.$el.querySelector('.note-header-info a').getAttribute('href')).toEqual(userDataMock.path); - expect(vm.$el.querySelector('.note-header-info .note-headline-light').textContent.trim()).toEqual(`@${userDataMock.username}`); + expect(vm.$el.querySelector('.note-header-info a').getAttribute('href')).toEqual( + userDataMock.path, + ); + expect( + vm.$el.querySelector('.note-header-info .note-headline-light').textContent.trim(), + ).toEqual(`@${userDataMock.username}`); }); it('should render note body', () => { diff --git a/spec/javascripts/vue_shared/components/notes/system_note_spec.js b/spec/javascripts/vue_shared/components/notes/system_note_spec.js index 36aaf0a6c2e..aa4c9c4c88c 100644 --- a/spec/javascripts/vue_shared/components/notes/system_note_spec.js +++ b/spec/javascripts/vue_shared/components/notes/system_note_spec.js @@ -1,8 +1,8 @@ import Vue from 'vue'; import issueSystemNote from '~/vue_shared/components/notes/system_note.vue'; -import store from '~/notes/stores'; +import createStore from '~/notes/stores'; -describe('issue system note', () => { +describe('system note component', () => { let vm; let props; @@ -24,6 +24,7 @@ describe('issue system note', () => { }, }; + const store = createStore(); store.dispatch('setTargetNoteHash', `note_${props.note.id}`); const Component = Vue.extend(issueSystemNote); @@ -49,9 +50,10 @@ describe('issue system note', () => { expect(vm.$el.querySelector('.timeline-icon svg')).toBeDefined(); }); - it('should render note header component', () => { - expect( - vm.$el.querySelector('.system-note-message').innerHTML, - ).toEqual(props.note.note_html); + // Redcarpet Markdown renderer wraps text in `

` tags + // we need to strip them because they break layout of commit lists in system notes: + // https://gitlab.com/gitlab-org/gitlab-ce/uploads/b07a10670919254f0220d3ff5c1aa110/jqzI.png + it('removes wrapping paragraph from note HTML', () => { + expect(vm.$el.querySelector('.system-note-message').innerHTML).toEqual('closed'); }); }); diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js index 7fe3bd92049..bdeebe0de75 100644 --- a/spec/javascripts/zen_mode_spec.js +++ b/spec/javascripts/zen_mode_spec.js @@ -1,11 +1,12 @@ import $ from 'jquery'; -import Mousetrap from 'mousetrap'; import Dropzone from 'dropzone'; +import Mousetrap from 'mousetrap'; import ZenMode from '~/zen_mode'; describe('ZenMode', () => { let zen; - const fixtureName = 'merge_requests/merge_request_with_comment.html.raw'; + let dropzoneForElementSpy; + const fixtureName = 'snippets/show.html.raw'; preloadFixtures(fixtureName); @@ -18,15 +19,17 @@ describe('ZenMode', () => { } function escapeKeydown() { - $('.notes-form textarea').trigger($.Event('keydown', { - keyCode: 27, - })); + $('.notes-form textarea').trigger( + $.Event('keydown', { + keyCode: 27, + }), + ); } beforeEach(() => { loadFixtures(fixtureName); - spyOn(Dropzone, 'forElement').and.callFake(() => ({ + dropzoneForElementSpy = spyOn(Dropzone, 'forElement').and.callFake(() => ({ enable: () => true, })); zen = new ZenMode(); @@ -35,11 +38,29 @@ describe('ZenMode', () => { zen.scroll_position = 456; }); + describe('enabling dropzone', () => { + beforeEach(() => { + enterZen(); + }); + + it('should not call dropzone if element is not dropzone valid', () => { + $('.div-dropzone').addClass('js-invalid-dropzone'); + exitZen(); + expect(dropzoneForElementSpy.calls.count()).toEqual(0); + }); + + it('should call dropzone if element is dropzone valid', () => { + $('.div-dropzone').removeClass('js-invalid-dropzone'); + exitZen(); + expect(dropzoneForElementSpy.calls.count()).toEqual(2); + }); + }); + describe('on enter', () => { it('pauses Mousetrap', () => { - spyOn(Mousetrap, 'pause'); + const mouseTrapPauseSpy = spyOn(Mousetrap, 'pause'); enterZen(); - expect(Mousetrap.pause).toHaveBeenCalled(); + expect(mouseTrapPauseSpy).toHaveBeenCalled(); }); it('removes textarea styling', () => { @@ -62,9 +83,9 @@ describe('ZenMode', () => { beforeEach(enterZen); it('unpauses Mousetrap', () => { - spyOn(Mousetrap, 'unpause'); + const mouseTrapUnpauseSpy = spyOn(Mousetrap, 'unpause'); exitZen(); - expect(Mousetrap.unpause).toHaveBeenCalled(); + expect(mouseTrapUnpauseSpy).toHaveBeenCalled(); }); it('restores the scroll position', () => { @@ -73,22 +94,4 @@ describe('ZenMode', () => { expect(zen.scrollTo).toHaveBeenCalled(); }); }); - - describe('enabling dropzone', () => { - beforeEach(() => { - enterZen(); - }); - - it('should not call dropzone if element is not dropzone valid', () => { - $('.div-dropzone').addClass('js-invalid-dropzone'); - exitZen(); - expect(Dropzone.forElement).not.toHaveBeenCalled(); - }); - - it('should call dropzone if element is dropzone valid', () => { - $('.div-dropzone').removeClass('js-invalid-dropzone'); - exitZen(); - expect(Dropzone.forElement).toHaveBeenCalled(); - }); - }); }); diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 6a6c71e6c82..a2cb716cb93 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -828,5 +828,15 @@ describe Note do note.destroy! end + + context 'when issuable etag caching is disabled' do + it 'does not store cache key' do + allow(note.noteable).to receive(:etag_caching_enabled?).and_return(false) + + expect_any_instance_of(Gitlab::EtagCaching::Store).not_to receive(:touch) + + note.save! + end + end end end diff --git a/spec/serializers/blob_entity_spec.rb b/spec/serializers/blob_entity_spec.rb new file mode 100644 index 00000000000..dde59ff72df --- /dev/null +++ b/spec/serializers/blob_entity_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe BlobEntity do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:blob) { project.commit('master').diffs.diff_files.first.blob } + let(:request) { EntityRequest.new(project: project, ref: 'master') } + + let(:entity) do + described_class.new(blob, request: request) + end + + context 'as json' do + subject { entity.as_json } + + it 'exposes needed attributes' do + expect(subject).to include(:readable_text, :url) + end + end +end diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb index 45d7c703df3..c4a6c117b76 100644 --- a/spec/serializers/diff_file_entity_spec.rb +++ b/spec/serializers/diff_file_entity_spec.rb @@ -9,16 +9,48 @@ describe DiffFileEntity do let(:diff_refs) { commit.diff_refs } let(:diff) { commit.raw_diffs.first } let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: diff_refs, repository: repository) } - let(:entity) { described_class.new(diff_file) } + let(:entity) { described_class.new(diff_file, request: {}) } subject { entity.as_json } - it 'exposes correct attributes' do - expect(subject).to include( - :submodule, :submodule_link, :file_path, - :deleted_file, :old_path, :new_path, :mode_changed, - :a_mode, :b_mode, :text, :old_path_html, - :new_path_html - ) + shared_examples 'diff file entity' do + it 'exposes correct attributes' do + expect(subject).to include( + :submodule, :submodule_link, :submodule_tree_url, :file_path, + :deleted_file, :old_path, :new_path, :mode_changed, + :a_mode, :b_mode, :text, :old_path_html, + :new_path_html, :highlighted_diff_lines, :parallel_diff_lines, + :blob, :file_hash, :added_lines, :removed_lines, :diff_refs, :content_sha, + :stored_externally, :external_storage, :too_large, :collapsed, :new_file, + :context_lines_path + ) + end + end + + context 'when there is no merge request' do + it_behaves_like 'diff file entity' + end + + context 'when there is a merge request' do + let(:user) { create(:user) } + let(:request) { EntityRequest.new(project: project, current_user: user) } + let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } + let(:entity) { described_class.new(diff_file, request: request, merge_request: merge_request) } + let(:exposed_urls) { %i(load_collapsed_diff_url edit_path view_path context_lines_path) } + + it_behaves_like 'diff file entity' + + it 'exposes additional attributes' do + expect(subject).to include(*exposed_urls) + expect(subject).to include(:replaced_view_path) + end + + it 'points all urls to merge request target project' do + response = subject + + exposed_urls.each do |attribute| + expect(response[attribute]).to include(merge_request.target_project.to_param) + end + end end end diff --git a/spec/serializers/diffs_entity_spec.rb b/spec/serializers/diffs_entity_spec.rb new file mode 100644 index 00000000000..19a843b0cb7 --- /dev/null +++ b/spec/serializers/diffs_entity_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe DiffsEntity do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:request) { EntityRequest.new(project: project, current_user: user) } + let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } + let(:merge_request_diffs) { merge_request.merge_request_diffs } + + let(:entity) do + described_class.new(merge_request_diffs.first.diffs, request: request, merge_request: merge_request, merge_request_diffs: merge_request_diffs) + end + + context 'as json' do + subject { entity.as_json } + + it 'contains needed attributes' do + expect(subject).to include( + :real_size, :size, :branch_name, + :target_branch_name, :commit, :merge_request_diff, + :start_version, :latest_diff, :latest_version_path, + :added_lines, :removed_lines, :render_overflow_warning, + :email_patch_path, :plain_diff_path, :diff_files, + :merge_request_diffs + ) + end + end +end diff --git a/spec/serializers/discussion_entity_spec.rb b/spec/serializers/discussion_entity_spec.rb index 7e19e74ca00..44d8cc69d9b 100644 --- a/spec/serializers/discussion_entity_spec.rb +++ b/spec/serializers/discussion_entity_spec.rb @@ -19,10 +19,20 @@ describe DiscussionEntity do end it 'exposes correct attributes' do - expect(subject).to include( - :id, :expanded, :notes, :individual_note, - :resolvable, :resolved, :resolve_path, - :resolve_with_issue_path, :diff_discussion + expect(subject.keys.sort).to include( + :diff_discussion, + :expanded, + :id, + :individual_note, + :notes, + :resolvable, + :resolve_path, + :resolve_with_issue_path, + :resolved, + :discussion_path, + :resolved_at, + :for_commit, + :commit_id ) end @@ -30,7 +40,21 @@ describe DiscussionEntity do let(:note) { create(:diff_note_on_merge_request) } it 'exposes diff file attributes' do - expect(subject).to include(:diff_file, :truncated_diff_lines, :image_diff_html) + expect(subject.keys.sort).to include( + :diff_file, + :truncated_diff_lines, + :position, + :line_code, + :active + ) + end + + context 'when diff file is a image' do + it 'exposes image attributes' do + allow(discussion).to receive(:on_image?).and_return(true) + + expect(subject.keys).to include(:image_diff_html) + end end end end diff --git a/spec/serializers/merge_request_diff_entity_spec.rb b/spec/serializers/merge_request_diff_entity_spec.rb new file mode 100644 index 00000000000..84f6833d88a --- /dev/null +++ b/spec/serializers/merge_request_diff_entity_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe MergeRequestDiffEntity do + let(:project) { create(:project, :repository) } + let(:request) { EntityRequest.new(project: project) } + let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } + let(:merge_request_diffs) { merge_request.merge_request_diffs } + + let(:entity) do + described_class.new(merge_request_diffs.first, request: request, merge_request: merge_request, merge_request_diffs: merge_request_diffs) + end + + context 'as json' do + subject { entity.as_json } + + it 'exposes needed attributes' do + expect(subject).to include( + :version_index, :created_at, :commits_count, + :latest, :short_commit_sha, :version_path, + :compare_path + ) + end + end +end diff --git a/spec/serializers/merge_request_user_entity_spec.rb b/spec/serializers/merge_request_user_entity_spec.rb new file mode 100644 index 00000000000..c91ea4aa681 --- /dev/null +++ b/spec/serializers/merge_request_user_entity_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe MergeRequestUserEntity do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:request) { EntityRequest.new(project: project, current_user: user) } + + let(:entity) do + described_class.new(user, request: request) + end + + context 'as json' do + subject { entity.as_json } + + it 'exposes needed attributes' do + expect(subject).to include(:can_fork, :can_create_merge_request, :fork_path) + end + end +end diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb index b4c71d69119..89a5518239d 100644 --- a/spec/support/features/reportable_note_shared_examples.rb +++ b/spec/support/features/reportable_note_shared_examples.rb @@ -22,7 +22,7 @@ shared_examples 'reportable note' do |type| expect(dropdown).to have_link('Report as abuse', href: abuse_report_path) - if type == 'issue' + if type == 'issue' || type == 'merge_request' expect(dropdown).to have_button('Delete comment') else expect(dropdown).to have_link('Delete comment', href: note_url(note, project)) diff --git a/spec/support/helpers/merge_request_diff_helpers.rb b/spec/support/helpers/merge_request_diff_helpers.rb index c98aa503ed1..3b49d0b3319 100644 --- a/spec/support/helpers/merge_request_diff_helpers.rb +++ b/spec/support/helpers/merge_request_diff_helpers.rb @@ -2,7 +2,7 @@ module MergeRequestDiffHelpers def click_diff_line(line_holder, diff_side = nil) line = get_line_components(line_holder, diff_side) line[:content].hover - line[:num].find('.add-diff-note', visible: false).send_keys(:return) + line[:num].find('.js-add-diff-note-button', visible: false).send_keys(:return) end def get_line_components(line_holder, diff_side = nil) diff --git a/spec/support/shared_examples/serializers/note_entity_examples.rb b/spec/support/shared_examples/serializers/note_entity_examples.rb index 9097c8e5513..ec208aba2a9 100644 --- a/spec/support/shared_examples/serializers/note_entity_examples.rb +++ b/spec/support/shared_examples/serializers/note_entity_examples.rb @@ -3,8 +3,8 @@ shared_examples 'note entity' do context 'basic note' do it 'exposes correct elements' do - expect(subject).to include(:type, :author, :note, :note_html, :current_user, - :discussion_id, :emoji_awardable, :award_emoji, :report_abuse_path, :attachment) + expect(subject).to include(:type, :author, :note, :note_html, :current_user, :discussion_id, + :emoji_awardable, :award_emoji, :report_abuse_path, :attachment, :noteable_note_url, :resolvable) end it 'does not expose elements for specific notes cases' do -- cgit v1.2.1 From 77723e2f3ac230e806af315c45ebecb8ba23d12d Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Thu, 21 Jun 2018 15:55:17 +0300 Subject: Move boards modal EmptyState vue component --- spec/javascripts/test_bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 994011b262a..6b049392fdf 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -135,7 +135,7 @@ if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report const troubleMakers = [ './blob_edit/blob_bundle.js', - './boards/components/modal/empty_state.js', + './boards/components/modal/empty_state.vue', './boards/components/modal/footer.js', './boards/components/modal/header.js', './cycle_analytics/cycle_analytics_bundle.js', -- cgit v1.2.1 From 6037c17cd44107b4f63cf291018764fa175e029c Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jun 2018 21:39:28 +0800 Subject: Bring this test from EE --- spec/uploaders/object_storage_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'spec') diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index c7f5694ff43..7e673681c31 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -191,6 +191,18 @@ describe ObjectStorage do it "calls a cache path" do expect { |b| uploader.use_file(&b) }.to yield_with_args(%r[tmp/cache]) end + + it "cleans up the cached file" do + cached_path = '' + + uploader.use_file do |path| + cached_path = path + + expect(File.exist?(cached_path)).to be_truthy + end + + expect(File.exist?(cached_path)).to be_falsey + end end end -- cgit v1.2.1 From 00d661401352fab5bbf1eb49d42eed40778acca4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jun 2018 14:01:23 +0000 Subject: Bring changes from EE --- .../issues/filtered_search/filter_issues_spec.rb | 36 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index bc42618306f..8dca81a8627 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -10,6 +10,7 @@ describe 'Filter issues', :js do # When the name is longer, the filtered search input can end up scrolling # horizontally, and PhantomJS can't handle it. let(:user) { create(:user, name: 'Ann') } + let(:user2) { create(:user, name: 'jane') } let!(:bug_label) { create(:label, project: project, title: 'bug') } let!(:caps_sensitive_label) { create(:label, project: project, title: 'CaPs') } @@ -25,8 +26,6 @@ describe 'Filter issues', :js do before do project.add_master(user) - user2 = create(:user) - create(:issue, project: project, author: user2, title: "Bug report 1") create(:issue, project: project, author: user2, title: "Bug report 2") @@ -113,6 +112,24 @@ describe 'Filter issues', :js do expect_issues_list_count(3) expect_filtered_search_input_empty end + + it 'filters issues by invalid assignee' do + skip('to be tested, issue #26546') + end + + it 'filters issues by multiple assignees' do + create(:issue, project: project, author: user, assignees: [user2, user]) + + input_filtered_search("assignee:@#{user.username} assignee:@#{user2.username}") + + expect_tokens([ + assignee_token(user.name), + assignee_token(user2.name) + ]) + + expect_issues_list_count(1) + expect_filtered_search_input_empty + end end end @@ -491,6 +508,21 @@ describe 'Filter issues', :js do it_behaves_like 'updates atom feed link', :group do let(:path) { issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id) } end + + it 'updates atom feed link for group issues' do + visit issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id) + link = find('.nav-controls a[title="Subscribe to RSS feed"]', visible: false) + params = CGI.parse(URI.parse(link[:href]).query) + auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) + auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) + + expect(params).to include('feed_token' => [user.feed_token]) + expect(params).to include('milestone_title' => [milestone.title]) + expect(params).to include('assignee_id' => [user.id.to_s]) + expect(auto_discovery_params).to include('feed_token' => [user.feed_token]) + expect(auto_discovery_params).to include('milestone_title' => [milestone.title]) + expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s]) + end end context 'URL has a trailing slash' do -- cgit v1.2.1 From c519cac2cbae91096e2b1a9a3c9f1bf02df0c1e4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jun 2018 22:38:58 +0800 Subject: Bring changes from EE --- .../auth/container_registry_authentication_service_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'spec') diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index da8e660c16b..fce73e0ac1f 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -21,6 +21,11 @@ describe Auth::ContainerRegistryAuthenticationService do allow_any_instance_of(JSONWebToken::RSAToken).to receive(:key).and_return(rsa_key) end + shared_examples 'an authenticated' do + it { is_expected.to include(:token) } + it { expect(payload).to include('access') } + end + shared_examples 'a valid token' do it { is_expected.to include(:token) } it { expect(payload).to include('access') } @@ -380,6 +385,14 @@ describe Auth::ContainerRegistryAuthenticationService do current_project.add_developer(current_user) end + context 'allow to use offline_token' do + let(:current_params) do + { offline_token: true } + end + + it_behaves_like 'an authenticated' + end + it_behaves_like 'a valid token' context 'allow to pull and push images' do -- cgit v1.2.1 From 5de2035c4774ef5d86450221918615acb0b986cf Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jun 2018 22:54:11 +0800 Subject: Use expect_next_instance_of to replace expect_any_instance_of --- spec/services/users/destroy_service_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index 76f1e625fda..f82d4b483e7 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -19,7 +19,9 @@ describe Users::DestroyService do end it 'will delete the project' do - expect_any_instance_of(Projects::DestroyService).to receive(:execute).once + expect_next_instance_of(Projects::DestroyService) do |destroy_service| + expect(destroy_service).to receive(:execute).once + end service.execute(user) end @@ -32,7 +34,9 @@ describe Users::DestroyService do end it 'destroys a project in pending_delete' do - expect_any_instance_of(Projects::DestroyService).to receive(:execute).once + expect_next_instance_of(Projects::DestroyService) do |destroy_service| + expect(destroy_service).to receive(:execute).once + end service.execute(user) -- cgit v1.2.1 From 14d1efc0e9d2a0ea9d2fda2face3214d711ea15c Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jun 2018 23:15:59 +0800 Subject: Bring changes from EE --- .../features/project_features_apply_to_issuables_shared_examples.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb index 639b0924197..64c3b80136d 100644 --- a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb +++ b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb @@ -18,7 +18,7 @@ shared_examples 'project features apply to issuables' do |klass| before do _ = issuable - gitlab_sign_in(user) if user + sign_in(user) if user visit path end -- cgit v1.2.1 From 056b47790af776e2ea98813144ca8255092ee008 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jun 2018 23:49:15 +0800 Subject: Bring changes from EE --- spec/support/redis/redis_shared_examples.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb index 8676f895a83..e650a176041 100644 --- a/spec/support/redis/redis_shared_examples.rb +++ b/spec/support/redis/redis_shared_examples.rb @@ -65,6 +65,14 @@ RSpec.shared_examples "redis_shared_examples" do end describe '.url' do + it 'withstands mutation' do + url1 = described_class.url + url2 = described_class.url + url1 << 'foobar' unless url1.frozen? + + expect(url2).not_to end_with('foobar') + end + context 'when yml file with env variable' do let(:config_file_name) { config_with_environment_variable_inside } @@ -101,7 +109,6 @@ RSpec.shared_examples "redis_shared_examples" do before do clear_pool end - after do clear_pool end -- cgit v1.2.1 From 4ab1011d427687b44ed937684c1a5a364d156a70 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 15 Jun 2018 14:32:37 -0300 Subject: Fix refresh cache for open issues count service --- .../batch_open_issues_count_service_spec.rb | 52 ++++++++++++++++++++++ .../projects/open_issues_count_service_spec.rb | 35 +++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 spec/services/projects/batch_open_issues_count_service_spec.rb (limited to 'spec') diff --git a/spec/services/projects/batch_open_issues_count_service_spec.rb b/spec/services/projects/batch_open_issues_count_service_spec.rb new file mode 100644 index 00000000000..720d7036396 --- /dev/null +++ b/spec/services/projects/batch_open_issues_count_service_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe Projects::BatchOpenIssuesCountService do + let!(:project_1) { create(:project) } + let!(:project_2) { create(:project) } + + let(:subject) { described_class.new([project_1, project_2]) } + + context '#refresh_cache', :use_clean_rails_memory_store_caching do + before do + create(:issue, project: project_1) + create(:issue, project: project_1, confidential: true) + + create(:issue, project: project_2) + create(:issue, project: project_2, confidential: true) + end + + context 'when cache is clean' do + it 'refreshes cache keys correctly' do + subject.refresh_cache + + expect(Rails.cache.read(get_cache_key(subject, project_1))).to eq(2) + expect(Rails.cache.read(get_cache_key(subject, project_2))).to eq(2) + expect(Rails.cache.read(get_cache_key(subject, project_1, true))).to eq(1) + expect(Rails.cache.read(get_cache_key(subject, project_1, true))).to eq(1) + end + end + + context 'when issues count is already cached' do + before do + create(:issue, project: project_2) + subject.refresh_cache + end + + it 'does update cache again' do + expect(Rails.cache).not_to receive(:write) + + subject.refresh_cache + end + end + end + + def get_cache_key(subject, project, public_key = false) + service = subject.count_service.new(project) + + if public_key + service.cache_key(service.class::PUBLIC_COUNT_KEY) + else + service.cache_key(service.class::TOTAL_COUNT_KEY) + end + end +end diff --git a/spec/services/projects/open_issues_count_service_spec.rb b/spec/services/projects/open_issues_count_service_spec.rb index 06b470849b3..562c14a8df8 100644 --- a/spec/services/projects/open_issues_count_service_spec.rb +++ b/spec/services/projects/open_issues_count_service_spec.rb @@ -50,5 +50,40 @@ describe Projects::OpenIssuesCountService do end end end + + context '#refresh_cache', :use_clean_rails_memory_store_caching do + let(:subject) { described_class.new(project) } + + before do + create(:issue, :opened, project: project) + create(:issue, :opened, project: project) + create(:issue, :opened, confidential: true, project: project) + end + + context 'when cache is empty' do + it 'refreshes cache keys correctly' do + subject.refresh_cache + + expect(Rails.cache.read(subject.cache_key(described_class::PUBLIC_COUNT_KEY))).to eq(2) + expect(Rails.cache.read(subject.cache_key(described_class::TOTAL_COUNT_KEY))).to eq(3) + end + end + + context 'when cache is outdated' do + before do + subject.refresh_cache + end + + it 'refreshes cache keys correctly' do + create(:issue, :opened, project: project) + create(:issue, :opened, confidential: true, project: project) + + subject.refresh_cache + + expect(Rails.cache.read(subject.cache_key(described_class::PUBLIC_COUNT_KEY))).to eq(3) + expect(Rails.cache.read(subject.cache_key(described_class::TOTAL_COUNT_KEY))).to eq(5) + end + end + end end end -- cgit v1.2.1 From 7d310e0f119426d70ab7be6061f9b095af455c95 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 21 Jun 2018 22:14:46 +0100 Subject: Updated test for promoted milestones success message --- spec/controllers/projects/milestones_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 02b30f9bc6d..b1d83246238 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -124,7 +124,7 @@ describe Projects::MilestonesController do it 'shows group milestone' do post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid - expect(flash[:notice]).to eq("#{milestone.title} promoted to group milestone.") + expect(flash[:notice]).to eq("#{milestone.title} promoted to group milestone.") expect(response).to redirect_to(project_milestones_path(project)) end end -- cgit v1.2.1 From 9b48d9f43f237ccf87594a944a00896996cadf55 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 21 Jun 2018 11:13:08 -0700 Subject: Show a reCAPTCHA on signin page if custom header is set This will only be displayed if `X-GitLab-Show-Login-Captcha` is set as an HTTP header. --- spec/controllers/sessions_controller_spec.rb | 39 +++++++++++++++++++--- .../devise/shared/_signin_box.html.haml_spec.rb | 1 + 2 files changed, 36 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 2b61e0d4a85..cdec26bd421 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -53,21 +53,22 @@ describe SessionsController do include UserActivitiesHelpers let(:user) { create(:user) } + let(:user_params) { { login: user.username, password: user.password } } it 'authenticates user correctly' do - post(:create, user: { login: user.username, password: user.password }) + post(:create, user: user_params) expect(subject.current_user). to eq user end it 'creates an audit log record' do - expect { post(:create, user: { login: user.username, password: user.password }) }.to change { SecurityEvent.count }.by(1) + expect { post(:create, user: user_params) }.to change { SecurityEvent.count }.by(1) expect(SecurityEvent.last.details[:with]).to eq('standard') end include_examples 'user login request with unique ip limit', 302 do def request - post(:create, user: { login: user.username, password: user.password }) + post(:create, user: user_params) expect(subject.current_user).to eq user subject.sign_out user end @@ -75,10 +76,40 @@ describe SessionsController do it 'updates the user activity' do expect do - post(:create, user: { login: user.username, password: user.password }) + post(:create, user: user_params) end.to change { user_activity(user) } end end + + context 'when reCAPTCHA is enabled' do + let(:user) { create(:user) } + let(:user_params) { { login: user.username, password: user.password } } + + before do + stub_application_setting(recaptcha_enabled: true) + request.headers[described_class::CAPTCHA_HEADER] = 1 + end + + it 'displays an error when the reCAPTCHA is not solved' do + # Without this, `verify_recaptcha` arbitraily returns true in test env + Recaptcha.configuration.skip_verify_env.delete('test') + + post(:create, user: user_params) + + expect(response).to render_template(:new) + expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' + expect(subject.current_user).to be_nil + end + + it 'successfully logs in a user when reCAPTCHA is solved' do + # Avoid test ordering issue and ensure `verify_recaptcha` returns true + Recaptcha.configuration.skip_verify_env << 'test' + + post(:create, user: user_params) + + expect(subject.current_user).to eq user + end + end end context 'when using two-factor authentication via OTP' do diff --git a/spec/views/devise/shared/_signin_box.html.haml_spec.rb b/spec/views/devise/shared/_signin_box.html.haml_spec.rb index 0870b8f09f9..66c064e3fba 100644 --- a/spec/views/devise/shared/_signin_box.html.haml_spec.rb +++ b/spec/views/devise/shared/_signin_box.html.haml_spec.rb @@ -6,6 +6,7 @@ describe 'devise/shared/_signin_box' do stub_devise assign(:ldap_servers, []) allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings) + allow(view).to receive(:captcha_enabled?).and_return(false) end it 'is shown when Crowd is enabled' do -- cgit v1.2.1 From 3d3e441c91ac793272723b5f20eae5b9cf19fcc2 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:00:10 +0200 Subject: refactor code based on feedback --- .../import_export/group_project_finder_spec.rb | 70 ---------------------- .../group_project_object_builder_spec.rb | 52 ++++++++++++++++ 2 files changed, 52 insertions(+), 70 deletions(-) delete mode 100644 spec/lib/gitlab/import_export/group_project_finder_spec.rb create mode 100644 spec/lib/gitlab/import_export/group_project_object_builder_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb deleted file mode 100644 index c36d80e51d4..00000000000 --- a/spec/lib/gitlab/import_export/group_project_finder_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'spec_helper' - -describe Gitlab::ImportExport::GroupProjectFinder do - let(:project) do - create(:project, - :builds_disabled, - :issues_disabled, - name: 'project', - path: 'project', - group: create(:group)) - end - - context 'labels' do - it 'finds the right group label' do - group_label = create(:group_label, 'name': 'group label', 'group': project.group) - - expect(described_class.find_or_new(Label, - title: 'group label', - project: project, - group: project.group)).to eq(group_label) - end - - it 'initializes a new label' do - label = described_class.find_or_new(Label, - title: 'group label', - project: project, - group: project.group) - - expect(label.persisted?).to be false - end - - it 'creates a new label' do - label = described_class.find_or_create(Label, - title: 'group label', - project: project, - group: project.group) - - expect(label.persisted?).to be true - end - end - - context 'milestones' do - it 'finds the right group milestone' do - milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group) - - expect(described_class.find_or_new(Milestone, - title: 'group milestone', - project: project, - group: project.group)).to eq(milestone) - end - - it 'initializes a new milestone' do - milestone = described_class.find_or_new(Milestone, - title: 'group milestone', - project: project, - group: project.group) - - expect(milestone.persisted?).to be false - end - - it 'creates a new milestone' do - milestone = described_class.find_or_create(Milestone, - title: 'group milestone', - project: project, - group: project.group) - - expect(milestone.persisted?).to be true - end - end -end diff --git a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb new file mode 100644 index 00000000000..5ca353d612a --- /dev/null +++ b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::GroupProjectObjectBuilder do + let(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + context 'labels' do + it 'finds the right group label' do + group_label = create(:group_label, 'name': 'group label', 'group': project.group) + + expect(described_class.build(Label, + title: 'group label', + project: project, + group: project.group)).to eq(group_label) + end + + it 'initializes a new label' do + label = described_class.build(Label, + title: 'group label', + project: project, + group: project.group) + + expect(label.persisted?).to be false + end + end + + context 'milestones' do + it 'finds the right group milestone' do + milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group) + + expect(described_class.build(Milestone, + title: 'group milestone', + project: project, + group: project.group)).to eq(milestone) + end + + it 'creates a new milestone' do + milestone = described_class.build(Milestone, + title: 'group milestone', + project: project, + group: project.group) + + expect(milestone.persisted?).to be true + end + end +end -- cgit v1.2.1 From 35d69ccf95e18031cd232c594282a1671d97d1ae Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:48:44 +0200 Subject: add more specs and refactor more relation factory code --- .../import_export/project.milestone-iid.json | 80 ++++++++++++++++++++++ .../import_export/project_tree_restorer_spec.rb | 27 ++++++++ 2 files changed, 107 insertions(+) create mode 100644 spec/lib/gitlab/import_export/project.milestone-iid.json (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project.milestone-iid.json b/spec/lib/gitlab/import_export/project.milestone-iid.json new file mode 100644 index 00000000000..b028147b5eb --- /dev/null +++ b/spec/lib/gitlab/import_export/project.milestone-iid.json @@ -0,0 +1,80 @@ +{ + "description": "Nisi et repellendus ut enim quo accusamus vel magnam.", + "import_type": "gitlab_project", + "creator_id": 123, + "visibility_level": 10, + "archived": false, + "issues": [ + { + "id": 1, + "title": "Fugiat est minima quae maxime non similique.", + "assignee_id": null, + "project_id": 8, + "author_id": 1, + "created_at": "2017-07-07T18:13:01.138Z", + "updated_at": "2017-08-15T18:37:40.807Z", + "branch_name": null, + "description": "Quam totam fuga numquam in eveniet.", + "state": "opened", + "iid": 20, + "updated_by_id": 1, + "confidential": false, + "due_date": null, + "moved_to_id": null, + "lock_version": null, + "time_estimate": 0, + "closed_at": null, + "last_edited_at": null, + "last_edited_by_id": null, + "group_milestone_id": null, + "milestone": { + "id": 1, + "title": "Group-level milestone", + "description": "Group-level milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": 8 + } + }, + { + "id": 2, + "title": "est minima quae maxime non similique.", + "assignee_id": null, + "project_id": 8, + "author_id": 1, + "created_at": "2017-07-07T18:13:01.138Z", + "updated_at": "2017-08-15T18:37:40.807Z", + "branch_name": null, + "description": "Quam totam fuga numquam in eveniet.", + "state": "opened", + "iid": 21, + "updated_by_id": 1, + "confidential": false, + "due_date": null, + "moved_to_id": null, + "lock_version": null, + "time_estimate": 0, + "closed_at": null, + "last_edited_at": null, + "last_edited_by_id": null, + "group_milestone_id": null, + "milestone": { + "id": 2, + "title": "Another milestone", + "project_id": 8, + "description": "milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": null + } + } + ], + "snippets": [], + "hooks": [] +} diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 94251e2280b..1601b913b91 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -391,5 +391,32 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do expect(project.milestones.count).to eq(0) end end + + context 'with clashing milestones on IID' do + let!(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + before do + project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.milestone-iid.json") + end + + it 'preserves the project milestone IID' do + create(:milestone, name: 'A milestone', group: project.group) + + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) + + restored_project_json + + expect(project.milestones.count).to eq(2) + expect(Milestone.find_by_title('Another milestone').iid).to eq(1) + expect(Milestone.find_by_title('Group-level milestone').iid).to eq(2) + end + end end end -- cgit v1.2.1 From fb5ca695c475ef9d9eb9b32f53bf9d5471b3c531 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:54:44 +0200 Subject: fix spec --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 2 -- 1 file changed, 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 1601b913b91..612f1ba4043 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -407,8 +407,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do end it 'preserves the project milestone IID' do - create(:milestone, name: 'A milestone', group: project.group) - expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json -- cgit v1.2.1 From d8252efea9c0b8ad18275318e0af762f3b5ba59b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:55:16 +0200 Subject: fix spec --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 612f1ba4043..bac5693c830 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -402,11 +402,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do group: create(:group)) end - before do + it 'preserves the project milestone IID' do project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.milestone-iid.json") - end - it 'preserves the project milestone IID' do expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json -- cgit v1.2.1 From affdcdef88d41103080e04cf1b754e6685634d8b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 22 Jun 2018 18:37:02 +0800 Subject: Bring changes from EE --- .../filtered_search/filtered_search_token_keys_spec.js | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js index fbc3926d332..68158cf52e4 100644 --- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js @@ -17,6 +17,17 @@ describe('Filtered Search Token Keys', () => { }); }); + describe('getKeys', () => { + it('should return keys', () => { + const getKeys = FilteredSearchTokenKeys.getKeys(); + const keys = FilteredSearchTokenKeys.get().map(i => i.key); + + keys.forEach((key, i) => { + expect(key).toEqual(getKeys[i]); + }); + }); + }); + describe('getConditions', () => { let conditions; -- cgit v1.2.1 From 78a9991543cdcc0cbd7ecfaab979c8e2c98b7a75 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Wed, 20 Jun 2018 16:42:38 +0200 Subject: Cleanup ruby sampler metrics * Use a simple counter for sampler duration instead of a histogram. * Use a counter to collect GC time. * Remove unused objects metric. * Cleanup metric names to match Prometheus conventions. * Prefix generic GC stats with `gc_stat`. * Include worker label on memory and file descriptor metrics. --- spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb index 091645ee86f..7972ff253fe 100644 --- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb @@ -45,7 +45,7 @@ describe Gitlab::Metrics::Samplers::RubySampler do it 'adds a metric containing garbage collection time statistics' do expect(GC::Profiler).to receive(:total_time).and_return(0.24) - expect(sampler.metrics[:total_time]).to receive(:set).with({}, 240) + expect(sampler.metrics[:total_time]).to receive(:increment).with({}, 0.24) sampler.sample end -- cgit v1.2.1 From 63d15c83a14538d3cd4e27b89d26e63ccd595431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mica=C3=ABl=20Bergeron?= Date: Fri, 22 Jun 2018 09:32:57 -0400 Subject: port stub_object_storage changes --- spec/support/helpers/stub_object_storage.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index bceaf8277ee..471b0a74a19 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -15,9 +15,14 @@ module StubObjectStorage return unless enabled + stub_object_storage(connection_params: uploader.object_store_credentials, + remote_directory: remote_directory) + end + + def stub_object_storage(connection_params:, remote_directory:) Fog.mock! - ::Fog::Storage.new(uploader.object_store_credentials).tap do |connection| + ::Fog::Storage.new(connection_params).tap do |connection| begin connection.directories.create(key: remote_directory) rescue Excon::Error::Conflict -- cgit v1.2.1 From b2a4b41cd6428c53ae7e5eaa498e5fd8454e26f8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 22 Jun 2018 23:06:00 +0800 Subject: Bring changes from EE --- spec/features/admin/admin_groups_spec.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'spec') diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index d5e603baeae..a4226d7a682 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -31,6 +31,7 @@ feature 'Admin Groups' do path_component = 'gitlab' group_name = 'GitLab group name' group_description = 'Description of group for GitLab' + fill_in 'group_path', with: path_component fill_in 'group_name', with: group_name fill_in 'group_description', with: group_description -- cgit v1.2.1 From 3c242dda8c463f53cb11b1a3609af3b5f50eae9d Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 22 Jun 2018 23:46:33 +0800 Subject: Bring changes from EE --- spec/support/matchers/match_ids.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'spec') diff --git a/spec/support/matchers/match_ids.rb b/spec/support/matchers/match_ids.rb index d8424405b96..1cb6b74acac 100644 --- a/spec/support/matchers/match_ids.rb +++ b/spec/support/matchers/match_ids.rb @@ -10,6 +10,13 @@ RSpec::Matchers.define :match_ids do |*expected| 'matches elements by ids' end + failure_message do + actual_ids = map_ids(actual) + expected_ids = map_ids(expected) + + "expected IDs #{actual_ids} in:\n\n #{actual.inspect}\n\nto match IDs #{expected_ids} in:\n\n #{expected.inspect}" + end + def map_ids(elements) elements = elements.flatten if elements.respond_to?(:flatten) -- cgit v1.2.1 From 4e734a9004f5241a33af8e962781acb33cbbadab Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Fri, 22 Jun 2018 22:16:46 +0200 Subject: Implement MR diff notes autosave feature. --- .../diffs/components/diff_line_note_form_spec.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js index 724d1948214..81cd4f9769a 100644 --- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js +++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js @@ -19,7 +19,15 @@ describe('DiffLineNoteForm', () => { diffLines, line: diffLines[0], noteTargetLine: diffLines[0], - }).$mount(); + }); + + Object.defineProperty(component, 'isLoggedIn', { + get() { + return true; + }, + }); + + component.$mount(); }); describe('methods', () => { @@ -56,6 +64,15 @@ describe('DiffLineNoteForm', () => { }); }); + describe('mounted', () => { + it('should init autosave', () => { + const key = 'autosave/Note/issue///DiffNote//1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1'; + + expect(component.autosave).toBeDefined(); + expect(component.autosave.key).toEqual(key); + }); + }); + describe('template', () => { it('should have note form', () => { const { $el } = component; -- cgit v1.2.1 From 59cb05c8b4abedb49dfa4e896d29832c0b2ffdbb Mon Sep 17 00:00:00 2001 From: Sanad Liaquat Date: Sun, 24 Jun 2018 15:51:07 +0500 Subject: Adds test to cover the fix in #45575 --- spec/features/users/signup_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'spec') diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb index b51ca5d130b..bfe11ddf673 100644 --- a/spec/features/users/signup_spec.rb +++ b/spec/features/users/signup_spec.rb @@ -40,6 +40,15 @@ describe 'Signup' do expect(find('.username')).to have_css '.gl-field-error-outline' end + + it 'shows an error message on submit if the username contains special characters' do + fill_in 'new_user_username', with: 'new$user!username' + wait_for_requests + + click_button "Register" + + expect(page).to have_content("Please create a username with only alphanumeric characters.") + end end context 'with no errors' do -- cgit v1.2.1 From f5ed18e1e3b5e69fa7bd650f3144fcfe26ac315f Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Mon, 11 Jun 2018 17:45:16 -0300 Subject: Delete non-latest merge request diff files upon diffs reload --- spec/models/merge_request_diff_spec.rb | 39 +++++++++++++ spec/models/merge_request_spec.rb | 27 +++------ .../delete_non_latest_diffs_service_spec.rb | 59 ++++++++++++++++++++ .../merge_request_diff_cache_service_spec.rb | 39 ------------- .../merge_requests/post_merge_service_spec.rb | 12 ++++ .../merge_requests/reload_diffs_service_spec.rb | 64 ++++++++++++++++++++++ spec/workers/delete_diff_files_worker_spec.rb | 41 ++++++++++++++ 7 files changed, 223 insertions(+), 58 deletions(-) create mode 100644 spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb delete mode 100644 spec/services/merge_requests/merge_request_diff_cache_service_spec.rb create mode 100644 spec/services/merge_requests/reload_diffs_service_spec.rb create mode 100644 spec/workers/delete_diff_files_worker_spec.rb (limited to 'spec') diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index b4249d72fc8..48c01fc4d4e 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -47,6 +47,45 @@ describe MergeRequestDiff do end describe '#diffs' do + let(:merge_request) { create(:merge_request, :with_diffs) } + let!(:diff) { merge_request.merge_request_diff.reload } + + context 'when it was not cleaned by the system' do + it 'returns persisted diffs' do + expect(diff).to receive(:load_diffs) + + diff.diffs + end + end + + context 'when diff was cleaned by the system' do + before do + diff.clean! + end + + it 'returns diffs from repository if can compare with current diff refs' do + expect(diff).not_to receive(:load_diffs) + + expect(Compare) + .to receive(:new) + .with(instance_of(Gitlab::Git::Compare), merge_request.target_project, + base_sha: diff.base_commit_sha, straight: false) + .and_call_original + + diff.diffs + end + + it 'returns persisted diffs if cannot compare with diff refs' do + expect(diff).to receive(:load_diffs) + + diff.update!(head_commit_sha: 'invalid-sha') + + diff.diffs + end + end + end + + describe '#raw_diffs' do context 'when the :ignore_whitespace_change option is set' do it 'creates a new compare object instead of loading from the DB' do expect(diff_with_commits).not_to receive(:load_diffs) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 7ae70c3afb4..2c75816654e 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1630,28 +1630,17 @@ describe MergeRequest do end describe "#reload_diff" do - let(:discussion) { create(:diff_note_on_merge_request, project: subject.project, noteable: subject).to_discussion } - let(:commit) { subject.project.commit(sample_commit.id) } - - it "does not change existing merge request diff" do - expect(subject.merge_request_diff).not_to receive(:save_git_content) - subject.reload_diff - end + it 'calls MergeRequests::ReloadDiffsService#execute with correct params' do + user = create(:user) + service = instance_double(MergeRequests::ReloadDiffsService, execute: nil) - it "creates new merge request diff" do - expect { subject.reload_diff }.to change { subject.merge_request_diffs.count }.by(1) - end - - it "executes diff cache service" do - expect_any_instance_of(MergeRequests::MergeRequestDiffCacheService).to receive(:execute).with(subject, an_instance_of(MergeRequestDiff)) - - subject.reload_diff - end + expect(MergeRequests::ReloadDiffsService) + .to receive(:new).with(subject, user) + .and_return(service) - it "calls update_diff_discussion_positions" do - expect(subject).to receive(:update_diff_discussion_positions) + subject.reload_diff(user) - subject.reload_diff + expect(service).to have_received(:execute) end context 'when using the after_update hook to update' do diff --git a/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb new file mode 100644 index 00000000000..1c632847940 --- /dev/null +++ b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe MergeRequests::DeleteNonLatestDiffsService, :clean_gitlab_redis_shared_state do + let(:merge_request) { create(:merge_request) } + + let!(:subject) { described_class.new(merge_request) } + + describe '#execute' do + before do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + + 3.times { merge_request.create_merge_request_diff } + end + + it 'schedules non-latest merge request diffs removal' do + diffs = merge_request.merge_request_diffs + + expect(diffs.count).to eq(4) + + Timecop.freeze do + expect(DeleteDiffFilesWorker) + .to receive(:bulk_perform_in) + .with(5.minutes, [[diffs.first.id], [diffs.second.id]]) + expect(DeleteDiffFilesWorker) + .to receive(:bulk_perform_in) + .with(10.minutes, [[diffs.third.id]]) + + subject.execute + end + end + + it 'schedules no removal if it is already cleaned' do + merge_request.merge_request_diffs.each(&:clean!) + + expect(DeleteDiffFilesWorker).not_to receive(:bulk_perform_in) + + subject.execute + end + + it 'schedules no removal if it is empty' do + merge_request.merge_request_diffs.each { |diff| diff.update!(state: :empty) } + + expect(DeleteDiffFilesWorker).not_to receive(:bulk_perform_in) + + subject.execute + end + + it 'schedules no removal if there is no non-latest diffs' do + merge_request + .merge_request_diffs + .where.not(id: merge_request.latest_merge_request_diff_id) + .destroy_all + + expect(DeleteDiffFilesWorker).not_to receive(:bulk_perform_in) + + subject.execute + end + end +end diff --git a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb deleted file mode 100644 index 57b6165cfb0..00000000000 --- a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe MergeRequests::MergeRequestDiffCacheService, :use_clean_rails_memory_store_caching do - let(:subject) { described_class.new } - let(:merge_request) { create(:merge_request) } - - describe '#execute' do - before do - allow_any_instance_of(Gitlab::Diff::File).to receive(:text?).and_return(true) - allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(true) - end - - it 'retrieves the diff files to cache the highlighted result' do - new_diff = merge_request.merge_request_diff - cache_key = new_diff.diffs.cache_key - - expect(Rails.cache).to receive(:read).with(cache_key).and_call_original - expect(Rails.cache).to receive(:write).with(cache_key, anything, anything).and_call_original - - subject.execute(merge_request, new_diff) - end - - it 'clears the cache for older diffs on the merge request' do - old_diff = merge_request.merge_request_diff - old_cache_key = old_diff.diffs.cache_key - - subject.execute(merge_request, old_diff) - - new_diff = merge_request.create_merge_request_diff - new_cache_key = new_diff.diffs.cache_key - - expect(Rails.cache).to receive(:delete).with(old_cache_key).and_call_original - expect(Rails.cache).to receive(:read).with(new_cache_key).and_call_original - expect(Rails.cache).to receive(:write).with(new_cache_key, anything, anything).and_call_original - - subject.execute(merge_request, new_diff) - end - end -end diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb index 70957431942..790ecce8ded 100644 --- a/spec/services/merge_requests/post_merge_service_spec.rb +++ b/spec/services/merge_requests/post_merge_service_spec.rb @@ -35,5 +35,17 @@ describe MergeRequests::PostMergeService do described_class.new(project, user, {}).execute(merge_request) end + + it 'deletes non-latest diffs' do + diff_removal_service = instance_double(MergeRequests::DeleteNonLatestDiffsService, execute: nil) + + expect(MergeRequests::DeleteNonLatestDiffsService) + .to receive(:new).with(merge_request) + .and_return(diff_removal_service) + + described_class.new(project, user, {}).execute(merge_request) + + expect(diff_removal_service).to have_received(:execute) + end end end diff --git a/spec/services/merge_requests/reload_diffs_service_spec.rb b/spec/services/merge_requests/reload_diffs_service_spec.rb new file mode 100644 index 00000000000..a0a27d247fc --- /dev/null +++ b/spec/services/merge_requests/reload_diffs_service_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe MergeRequests::ReloadDiffsService, :use_clean_rails_memory_store_caching do + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:subject) { described_class.new(merge_request, current_user) } + + describe '#execute' do + it 'creates new merge request diff' do + expect { subject.execute }.to change { merge_request.merge_request_diffs.count }.by(1) + end + + it 'calls update_diff_discussion_positions with correct params' do + old_diff_refs = merge_request.diff_refs + new_diff = merge_request.create_merge_request_diff + new_diff_refs = merge_request.diff_refs + + expect(merge_request).to receive(:create_merge_request_diff).and_return(new_diff) + expect(merge_request).to receive(:update_diff_discussion_positions) + .with(old_diff_refs: old_diff_refs, + new_diff_refs: new_diff_refs, + current_user: current_user) + + subject.execute + end + + it 'does not change existing merge request diff' do + expect(merge_request.merge_request_diff).not_to receive(:save_git_content) + + subject.execute + end + + context 'cache clearing' do + before do + allow_any_instance_of(Gitlab::Diff::File).to receive(:text?).and_return(true) + allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(true) + end + + it 'retrieves the diff files to cache the highlighted result' do + new_diff = merge_request.create_merge_request_diff + cache_key = new_diff.diffs_collection.cache_key + + expect(merge_request).to receive(:create_merge_request_diff).and_return(new_diff) + expect(Rails.cache).to receive(:read).with(cache_key).and_call_original + expect(Rails.cache).to receive(:write).with(cache_key, anything, anything).and_call_original + + subject.execute + end + + it 'clears the cache for older diffs on the merge request' do + old_diff = merge_request.merge_request_diff + old_cache_key = old_diff.diffs_collection.cache_key + new_diff = merge_request.create_merge_request_diff + new_cache_key = new_diff.diffs_collection.cache_key + + expect(merge_request).to receive(:create_merge_request_diff).and_return(new_diff) + expect(Rails.cache).to receive(:delete).with(old_cache_key).and_call_original + expect(Rails.cache).to receive(:read).with(new_cache_key).and_call_original + expect(Rails.cache).to receive(:write).with(new_cache_key, anything, anything).and_call_original + subject.execute + end + end + end +end diff --git a/spec/workers/delete_diff_files_worker_spec.rb b/spec/workers/delete_diff_files_worker_spec.rb new file mode 100644 index 00000000000..e0edd313922 --- /dev/null +++ b/spec/workers/delete_diff_files_worker_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe DeleteDiffFilesWorker do + describe '#perform' do + let(:merge_request) { create(:merge_request) } + let(:merge_request_diff) { merge_request.merge_request_diff } + + it 'deletes all merge request diff files' do + expect { described_class.new.perform(merge_request_diff.id) } + .to change { merge_request_diff.merge_request_diff_files.count } + .from(20).to(0) + end + + it 'updates state to without_files' do + expect { described_class.new.perform(merge_request_diff.id) } + .to change { merge_request_diff.reload.state } + .from('collected').to('without_files') + end + + it 'does nothing if diff was already marked as "without_files"' do + merge_request_diff.clean! + + expect_any_instance_of(MergeRequestDiff).not_to receive(:clean!) + + described_class.new.perform(merge_request_diff.id) + end + + it 'rollsback if something goes wrong' do + expect(MergeRequestDiffFile).to receive_message_chain(:where, :delete_all) + .and_raise + + expect { described_class.new.perform(merge_request_diff.id) } + .to raise_error + + merge_request_diff.reload + + expect(merge_request_diff.state).to eq('collected') + expect(merge_request_diff.merge_request_diff_files.count).to eq(20) + end + end +end -- cgit v1.2.1 From 0d22b2141f4ba9f75039d9614027b99946f35622 Mon Sep 17 00:00:00 2001 From: Aram Visser Date: Mon, 25 Jun 2018 10:19:13 +0700 Subject: Set flash error message only for current request when updating project --- spec/controllers/projects_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 90e698925b6..27f04be3fdf 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -329,7 +329,7 @@ describe ProjectsController do expect { update_project path: 'renamed_path' } .not_to change { project.reload.path } - expect(controller).to set_flash[:alert].to(/container registry tags/) + expect(controller).to set_flash.now[:alert].to(/container registry tags/) expect(response).to have_gitlab_http_status(200) end end -- cgit v1.2.1 From a6d3727f7dbb15a061ee26ce2d7e4ed472c1016a Mon Sep 17 00:00:00 2001 From: Jan Beckmann Date: Mon, 25 Jun 2018 08:03:08 +0000 Subject: Resolve "2FA should not attempt to use U2F in unsupported browsers" --- spec/javascripts/u2f/authenticate_spec.js | 84 ++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 30 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js index d84b13b07c4..57e0caa692c 100644 --- a/spec/javascripts/u2f/authenticate_spec.js +++ b/spec/javascripts/u2f/authenticate_spec.js @@ -6,7 +6,7 @@ import MockU2FDevice from './mock_u2f_device'; describe('U2FAuthenticate', function () { preloadFixtures('u2f/authenticate.html.raw'); - beforeEach((done) => { + beforeEach(() => { loadFixtures('u2f/authenticate.html.raw'); this.u2fDevice = new MockU2FDevice(); this.container = $('#js-authenticate-u2f'); @@ -19,46 +19,70 @@ describe('U2FAuthenticate', function () { document.querySelector('#js-login-2fa-device'), document.querySelector('.js-2fa-form'), ); + }); - // bypass automatic form submission within renderAuthenticated - spyOn(this.component, 'renderAuthenticated').and.returnValue(true); + describe('with u2f unavailable', () => { + beforeEach(() => { + spyOn(this.component, 'switchToFallbackUI'); + this.oldu2f = window.u2f; + window.u2f = null; + }); - this.component.start().then(done).catch(done.fail); - }); + afterEach(() => { + window.u2f = this.oldu2f; + }); - it('allows authenticating via a U2F device', () => { - const inProgressMessage = this.container.find('p'); - expect(inProgressMessage.text()).toContain('Trying to communicate with your device'); - this.u2fDevice.respondToAuthenticateRequest({ - deviceData: 'this is data from the device', + it('falls back to normal 2fa', (done) => { + this.component.start().then(() => { + expect(this.component.switchToFallbackUI).toHaveBeenCalled(); + done(); + }).catch(done.fail); }); - expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); }); - describe('errors', () => { - it('displays an error message', () => { - const setupButton = this.container.find('#js-login-u2f-device'); - setupButton.trigger('click'); - this.u2fDevice.respondToAuthenticateRequest({ - errorCode: 'error!', - }); - const errorMessage = this.container.find('p'); - return expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + describe('with u2f available', () => { + beforeEach((done) => { + // bypass automatic form submission within renderAuthenticated + spyOn(this.component, 'renderAuthenticated').and.returnValue(true); + this.u2fDevice = new MockU2FDevice(); + + this.component.start().then(done).catch(done.fail); }); - return it('allows retrying authentication after an error', () => { - let setupButton = this.container.find('#js-login-u2f-device'); - setupButton.trigger('click'); - this.u2fDevice.respondToAuthenticateRequest({ - errorCode: 'error!', - }); - const retryButton = this.container.find('#js-u2f-try-again'); - retryButton.trigger('click'); - setupButton = this.container.find('#js-login-u2f-device'); - setupButton.trigger('click'); + + it('allows authenticating via a U2F device', () => { + const inProgressMessage = this.container.find('p'); + expect(inProgressMessage.text()).toContain('Trying to communicate with your device'); this.u2fDevice.respondToAuthenticateRequest({ deviceData: 'this is data from the device', }); expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); }); + + describe('errors', () => { + it('displays an error message', () => { + const setupButton = this.container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + errorCode: 'error!', + }); + const errorMessage = this.container.find('p'); + return expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + }); + return it('allows retrying authentication after an error', () => { + let setupButton = this.container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + errorCode: 'error!', + }); + const retryButton = this.container.find('#js-u2f-try-again'); + retryButton.trigger('click'); + setupButton = this.container.find('#js-login-u2f-device'); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + deviceData: 'this is data from the device', + }); + expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); + }); + }); }); }); -- cgit v1.2.1 From 2ec741e129b3b525c47d04a227e267afc654293c Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 25 Jun 2018 18:47:11 +0800 Subject: Bring changes from EE --- spec/javascripts/test_bundle.js | 1 - 1 file changed, 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index aeb936b0e3c..0eff98bcc9d 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -3,7 +3,6 @@ import $ from 'jquery'; import 'vendor/jasmine-jquery'; import '~/commons'; - import Vue from 'vue'; import VueResource from 'vue-resource'; import Translate from '~/vue_shared/translate'; -- cgit v1.2.1 From bc955da8eabc54302875d100dde2c61ffce84312 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 25 Jun 2018 11:58:24 +0100 Subject: Fixed the IDE commit list item loosing its active state Closes #48125 --- spec/javascripts/ide/components/repo_tab_spec.js | 26 +++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js index 8cabc6e8935..fc0695a4263 100644 --- a/spec/javascripts/ide/components/repo_tab_spec.js +++ b/spec/javascripts/ide/components/repo_tab_spec.js @@ -38,6 +38,26 @@ describe('RepoTab', () => { expect(name.textContent.trim()).toEqual(vm.tab.name); }); + it('does not call openPendingTab when tab is active', done => { + vm = createComponent({ + tab: { + ...file(), + pending: true, + active: true, + }, + }); + + spyOn(vm, 'openPendingTab'); + + vm.$el.click(); + + vm.$nextTick(() => { + expect(vm.openPendingTab).not.toHaveBeenCalled(); + + done(); + }); + }); + it('fires clickFile when the link is clicked', () => { vm = createComponent({ tab: file(), @@ -112,9 +132,9 @@ describe('RepoTab', () => { }); it('renders a tooltip', () => { - expect( - vm.$el.querySelector('span:nth-child(2)').dataset.originalTitle, - ).toContain('Locked by testuser'); + expect(vm.$el.querySelector('span:nth-child(2)').dataset.originalTitle).toContain( + 'Locked by testuser', + ); }); }); -- cgit v1.2.1 From 4e19db62af18dde1268f852ef9c79aa0ac752e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=A4mmerle?= Date: Mon, 25 Jun 2018 13:43:11 +0200 Subject: Change button text to more specific Discard draft --- spec/features/projects/commit/comments/user_adds_comment_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/features/projects/commit/comments/user_adds_comment_spec.rb b/spec/features/projects/commit/comments/user_adds_comment_spec.rb index 6397df086a7..53866c32c69 100644 --- a/spec/features/projects/commit/comments/user_adds_comment_spec.rb +++ b/spec/features/projects/commit/comments/user_adds_comment_spec.rb @@ -62,7 +62,7 @@ describe "User adds a comment on a commit", :js do click_diff_line(sample_commit.line_code) expect(page).to have_css(".js-temp-notes-holder form.new-note") - .and have_css(".js-close-discussion-note-form", text: "Cancel") + .and have_css(".js-close-discussion-note-form", text: "Discard draft") # The `Cancel` button closes the current form. The page should not have any open forms after that. find(".js-close-discussion-note-form").click -- cgit v1.2.1 From e91b2b5dfffbe9c45b7a8887d9b108a118506607 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Fri, 22 Jun 2018 13:58:06 +0200 Subject: Gitaly tree entry is mandatory Closes https://gitlab.com/gitlab-org/gitaly/issues/310 --- spec/lib/gitlab/git/blob_spec.rb | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 6015086f002..b6061df349d 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::Git::Blob, seed_helper: true do end end - shared_examples 'finding blobs' do + describe '.find' do context 'nil path' do let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) } @@ -125,16 +125,6 @@ describe Gitlab::Git::Blob, seed_helper: true do end end - describe '.find' do - context 'when project_raw_show Gitaly feature is enabled' do - it_behaves_like 'finding blobs' - end - - context 'when project_raw_show Gitaly feature is disabled', :skip_gitaly_mock do - it_behaves_like 'finding blobs' - end - end - shared_examples 'finding blobs by ID' do let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) } let(:bad_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::BigCommit::ID) } -- cgit v1.2.1 From f012b32bd19fe800fe17339d4e0aa7b3bf3828c3 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 25 Jun 2018 20:28:49 +0800 Subject: Prefer `expect_next_instance_of` over `expect_any_instance_of` --- spec/workers/delete_user_worker_spec.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/workers/delete_user_worker_spec.rb b/spec/workers/delete_user_worker_spec.rb index 36594515005..06d9e125105 100644 --- a/spec/workers/delete_user_worker_spec.rb +++ b/spec/workers/delete_user_worker_spec.rb @@ -5,15 +5,17 @@ describe DeleteUserWorker do let!(:current_user) { create(:user) } it "calls the DeleteUserWorker with the params it was given" do - expect_any_instance_of(Users::DestroyService).to receive(:execute) - .with(user, {}) + expect_next_instance_of(Users::DestroyService) do |service| + expect(service).to receive(:execute).with(user, {}) + end described_class.new.perform(current_user.id, user.id) end it "uses symbolized keys" do - expect_any_instance_of(Users::DestroyService).to receive(:execute) - .with(user, test: "test") + expect_next_instance_of(Users::DestroyService) do |service| + expect(service).to receive(:execute).with(user, test: "test") + end described_class.new.perform(current_user.id, user.id, "test" => "test") end -- cgit v1.2.1 From 156339569d52bef66fe41c85d29ab3c9865f7362 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 21 Jun 2018 13:17:35 +0200 Subject: Enforce setting string as value of the CI/CD variable --- .../gitlab/ci/variables/collection/item_spec.rb | 64 +++++++++++++++++++--- spec/lib/gitlab/ci/variables/collection_spec.rb | 12 ++-- spec/models/ci/build_spec.rb | 6 +- 3 files changed, 68 insertions(+), 14 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index e79f0a7f257..adb3ff4321f 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -1,19 +1,69 @@ require 'spec_helper' describe Gitlab::Ci::Variables::Collection::Item do + let(:variable_key) { 'VAR' } + let(:variable_value) { 'something' } + let(:expected_value) { variable_value } + let(:variable) do - { key: 'VAR', value: 'something', public: true } + { key: variable_key, value: variable_value, public: true } end describe '.new' do - it 'raises error if unknown key i specified' do - expect { described_class.new(key: 'VAR', value: 'abc', files: true) } - .to raise_error ArgumentError, 'unknown keyword: files' + context 'when unknown keyword is specified' do + it 'raises error' do + expect { described_class.new(key: variable_key, value: 'abc', files: true) } + .to raise_error ArgumentError, 'unknown keyword: files' + end + end + + context 'when required keywords are not specified' do + it 'raises error' do + expect { described_class.new(key: variable_key) } + .to raise_error ArgumentError, 'missing keyword: value' + end end - it 'raises error when required keywords are not specified' do - expect { described_class.new(key: 'VAR') } - .to raise_error ArgumentError, 'missing keyword: value' + shared_examples 'creates variable' do + subject { described_class.new(key: variable_key, value: variable_value) } + + it 'saves given value' do + expect(subject[:key]).to eq variable_key + expect(subject[:value]).to eq expected_value + end + end + + shared_examples 'raises error for invalid type' do + it do + expect { described_class.new(key: variable_key, value: variable_value) } + .to raise_error ArgumentError, /`value` must be of type String, while it was:/ + end + end + + it_behaves_like 'creates variable' + + context "when it's nil" do + let(:variable_value) { nil } + let(:expected_value) { nil } + + it_behaves_like 'creates variable' + end + + context "when it's an empty string" do + let(:variable_value) { '' } + let(:expected_value) { '' } + + it_behaves_like 'creates variable' + end + + context 'when provided value is not a string' do + [1, false, [], {}, Object.new].each do |val| + context "when it's #{val}" do + let(:variable_value) { val } + + it_behaves_like 'raises error for invalid type' + end + end end end diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb index cb2f7718c9c..5c91816a586 100644 --- a/spec/lib/gitlab/ci/variables/collection_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection_spec.rb @@ -29,7 +29,7 @@ describe Gitlab::Ci::Variables::Collection do end it 'appends an internal resource' do - collection = described_class.new([{ key: 'TEST', value: 1 }]) + collection = described_class.new([{ key: 'TEST', value: '1' }]) subject.append(collection.first) @@ -74,15 +74,15 @@ describe Gitlab::Ci::Variables::Collection do describe '#+' do it 'makes it possible to combine with an array' do - collection = described_class.new([{ key: 'TEST', value: 1 }]) + collection = described_class.new([{ key: 'TEST', value: '1' }]) variables = [{ key: 'TEST', value: 'something' }] expect((collection + variables).count).to eq 2 end it 'makes it possible to combine with another collection' do - collection = described_class.new([{ key: 'TEST', value: 1 }]) - other = described_class.new([{ key: 'TEST', value: 2 }]) + collection = described_class.new([{ key: 'TEST', value: '1' }]) + other = described_class.new([{ key: 'TEST', value: '2' }]) expect((collection + other).count).to eq 2 end @@ -90,10 +90,10 @@ describe Gitlab::Ci::Variables::Collection do describe '#to_runner_variables' do it 'creates an array of hashes in a runner-compatible format' do - collection = described_class.new([{ key: 'TEST', value: 1 }]) + collection = described_class.new([{ key: 'TEST', value: '1' }]) expect(collection.to_runner_variables) - .to eq [{ key: 'TEST', value: 1, public: true }] + .to eq [{ key: 'TEST', value: '1', public: true }] end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 51b9b518117..6758adc59eb 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1871,7 +1871,11 @@ describe Ci::Build do end context 'when yaml_variables are undefined' do - let(:pipeline) { create(:ci_pipeline, project: project) } + let(:pipeline) do + create(:ci_pipeline, project: project, + sha: project.commit.id, + ref: project.default_branch) + end before do build.yaml_variables = nil -- cgit v1.2.1 From 08554787264dbbf24644fa97c06b45954bac6122 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 25 Jun 2018 15:08:20 +0200 Subject: Write Config is mandatory Closes https://gitlab.com/gitlab-org/gitaly/issues/879 --- spec/lib/gitlab/git/repository_spec.rb | 52 ++++++++++++++-------------------- 1 file changed, 21 insertions(+), 31 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 45f0006dc85..79353fe62d3 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1871,49 +1871,39 @@ describe Gitlab::Git::Repository, seed_helper: true do repository_rugged.config["gitlab.fullpath"] = repository_path end - shared_examples 'writing repo config' do - context 'is given a path' do - it 'writes it to disk' do - repository.write_config(full_path: "not-the/real-path.git") + context 'is given a path' do + it 'writes it to disk' do + repository.write_config(full_path: "not-the/real-path.git") - config = File.read(File.join(repository_path, "config")) + config = File.read(File.join(repository_path, "config")) - expect(config).to include("[gitlab]") - expect(config).to include("fullpath = not-the/real-path.git") - end + expect(config).to include("[gitlab]") + expect(config).to include("fullpath = not-the/real-path.git") end + end - context 'it is given an empty path' do - it 'does not write it to disk' do - repository.write_config(full_path: "") + context 'it is given an empty path' do + it 'does not write it to disk' do + repository.write_config(full_path: "") - config = File.read(File.join(repository_path, "config")) + config = File.read(File.join(repository_path, "config")) - expect(config).to include("[gitlab]") - expect(config).to include("fullpath = #{repository_path}") - end + expect(config).to include("[gitlab]") + expect(config).to include("fullpath = #{repository_path}") end + end - context 'repository does not exist' do - it 'raises NoRepository and does not call Gitaly WriteConfig' do - repository = Gitlab::Git::Repository.new('default', 'does/not/exist.git', '') + context 'repository does not exist' do + it 'raises NoRepository and does not call Gitaly WriteConfig' do + repository = Gitlab::Git::Repository.new('default', 'does/not/exist.git', '') - expect(repository.gitaly_repository_client).not_to receive(:write_config) + expect(repository.gitaly_repository_client).not_to receive(:write_config) - expect do - repository.write_config(full_path: 'foo/bar.git') - end.to raise_error(Gitlab::Git::Repository::NoRepository) - end + expect do + repository.write_config(full_path: 'foo/bar.git') + end.to raise_error(Gitlab::Git::Repository::NoRepository) end end - - context "when gitaly_write_config is enabled" do - it_behaves_like "writing repo config" - end - - context "when gitaly_write_config is disabled", :disable_gitaly do - it_behaves_like "writing repo config" - end end describe '#merge' do -- cgit v1.2.1 From 8354ae3cef3454864638af11a401d7a40c33ecc0 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 25 Jun 2018 11:37:24 +0200 Subject: Update remote happens through Gitaly only This change makes closes another migration! And remotes tests around it, mostly due to stubbing this was needed. Also, these are tested on the Gitaly side too: - https://gitlab.com/gitlab-org/gitaly/issues/791 - https://gitlab.com/gitlab-org/gitaly/blob/6dd74543f6af8ebcf22aa10e39da004f388fdda5/internal/service/repository/fetch_remote_test.go Closes https://gitlab.com/gitlab-org/gitaly/issues/789 --- .../projects/update_remote_mirror_service_spec.rb | 305 ++------------------- 1 file changed, 27 insertions(+), 278 deletions(-) (limited to 'spec') diff --git a/spec/services/projects/update_remote_mirror_service_spec.rb b/spec/services/projects/update_remote_mirror_service_spec.rb index 723cb374c37..5c2e79ff9af 100644 --- a/spec/services/projects/update_remote_mirror_service_spec.rb +++ b/spec/services/projects/update_remote_mirror_service_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' describe Projects::UpdateRemoteMirrorService do - let(:project) { create(:project, :repository) } + set(:project) { create(:project, :repository) } + let(:owner) { project.owner } let(:remote_project) { create(:forked_project_with_submodules) } let(:repository) { project.repository } let(:raw_repository) { repository.raw } @@ -9,13 +10,11 @@ describe Projects::UpdateRemoteMirrorService do subject { described_class.new(project, project.creator) } - describe "#execute", :skip_gitaly_mock do + describe "#execute" do before do - create_branch(repository, 'existing-branch') - allow(raw_repository).to receive(:remote_tags) do - generate_tags(repository, 'v1.0.0', 'v1.1.0') - end - allow(raw_repository).to receive(:push_remote_branches).and_return(true) + repository.add_branch(owner, 'existing-branch', 'master') + + allow(remote_mirror).to receive(:update_repository).and_return(true) end it "fetches the remote repository" do @@ -34,307 +33,57 @@ describe Projects::UpdateRemoteMirrorService do expect(result[:status]).to eq(:success) end - describe 'Syncing branches' do + context 'when syncing all branches' do it "push all the branches the first time" do allow(repository).to receive(:fetch_remote) - expect(raw_repository).to receive(:push_remote_branches).with(remote_mirror.remote_name, local_branch_names) - - subject.execute(remote_mirror) - end - - it "does not push anything is remote is up to date" do - allow(repository).to receive(:fetch_remote) { sync_remote(repository, remote_mirror.remote_name, local_branch_names) } - - expect(raw_repository).not_to receive(:push_remote_branches) - - subject.execute(remote_mirror) - end - - it "sync new branches" do - # call local_branch_names early so it is not called after the new branch has been created - current_branches = local_branch_names - allow(repository).to receive(:fetch_remote) { sync_remote(repository, remote_mirror.remote_name, current_branches) } - create_branch(repository, 'my-new-branch') - - expect(raw_repository).to receive(:push_remote_branches).with(remote_mirror.remote_name, ['my-new-branch']) - - subject.execute(remote_mirror) - end - - it "sync updated branches" do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - update_branch(repository, 'existing-branch') - end - - expect(raw_repository).to receive(:push_remote_branches).with(remote_mirror.remote_name, ['existing-branch']) + expect(remote_mirror).to receive(:update_repository).with({}) subject.execute(remote_mirror) end - - context 'when push only protected branches option is set' do - let(:unprotected_branch_name) { 'existing-branch' } - let(:protected_branch_name) do - project.repository.branch_names.find { |n| n != unprotected_branch_name } - end - let!(:protected_branch) do - create(:protected_branch, project: project, name: protected_branch_name) - end - - before do - project.reload - remote_mirror.only_protected_branches = true - end - - it "sync updated protected branches" do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - update_branch(repository, protected_branch_name) - end - - expect(raw_repository).to receive(:push_remote_branches).with(remote_mirror.remote_name, [protected_branch_name]) - - subject.execute(remote_mirror) - end - - it 'does not sync unprotected branches' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - update_branch(repository, unprotected_branch_name) - end - - expect(raw_repository).not_to receive(:push_remote_branches).with(remote_mirror.remote_name, [unprotected_branch_name]) - - subject.execute(remote_mirror) - end - end - - context 'when branch exists in local and remote repo' do - context 'when it has diverged' do - it 'syncs branches' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - update_remote_branch(repository, remote_mirror.remote_name, 'markdown') - end - - expect(raw_repository).to receive(:push_remote_branches).with(remote_mirror.remote_name, ['markdown']) - - subject.execute(remote_mirror) - end - end - end - - describe 'for delete' do - context 'when branch exists in local and remote repo' do - it 'deletes the branch from remote repo' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - delete_branch(repository, 'existing-branch') - end - - expect(raw_repository).to receive(:delete_remote_branches).with(remote_mirror.remote_name, ['existing-branch']) - - subject.execute(remote_mirror) - end - end - - context 'when push only protected branches option is set' do - before do - remote_mirror.only_protected_branches = true - end - - context 'when branch exists in local and remote repo' do - let!(:protected_branch_name) { local_branch_names.first } - - before do - create(:protected_branch, project: project, name: protected_branch_name) - project.reload - end - - it 'deletes the protected branch from remote repo' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - delete_branch(repository, protected_branch_name) - end - - expect(raw_repository).not_to receive(:delete_remote_branches).with(remote_mirror.remote_name, [protected_branch_name]) - - subject.execute(remote_mirror) - end - - it 'does not delete the unprotected branch from remote repo' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - delete_branch(repository, 'existing-branch') - end - - expect(raw_repository).not_to receive(:delete_remote_branches).with(remote_mirror.remote_name, ['existing-branch']) - - subject.execute(remote_mirror) - end - end - - context 'when branch only exists on remote repo' do - let!(:protected_branch_name) { 'remote-branch' } - - before do - create(:protected_branch, project: project, name: protected_branch_name) - end - - context 'when it has diverged' do - it 'does not delete the remote branch' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - - rev = repository.find_branch('markdown').dereferenced_target - create_remote_branch(repository, remote_mirror.remote_name, 'remote-branch', rev.id) - end - - expect(raw_repository).not_to receive(:delete_remote_branches) - - subject.execute(remote_mirror) - end - end - - context 'when it has not diverged' do - it 'deletes the remote branch' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - - masterrev = repository.find_branch('master').dereferenced_target - create_remote_branch(repository, remote_mirror.remote_name, protected_branch_name, masterrev.id) - end - - expect(raw_repository).to receive(:delete_remote_branches).with(remote_mirror.remote_name, [protected_branch_name]) - - subject.execute(remote_mirror) - end - end - end - end - - context 'when branch only exists on remote repo' do - context 'when it has diverged' do - it 'does not delete the remote branch' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - - rev = repository.find_branch('markdown').dereferenced_target - create_remote_branch(repository, remote_mirror.remote_name, 'remote-branch', rev.id) - end - - expect(raw_repository).not_to receive(:delete_remote_branches) - - subject.execute(remote_mirror) - end - end - - context 'when it has not diverged' do - it 'deletes the remote branch' do - allow(repository).to receive(:fetch_remote) do - sync_remote(repository, remote_mirror.remote_name, local_branch_names) - - masterrev = repository.find_branch('master').dereferenced_target - create_remote_branch(repository, remote_mirror.remote_name, 'remote-branch', masterrev.id) - end - - expect(raw_repository).to receive(:delete_remote_branches).with(remote_mirror.remote_name, ['remote-branch']) - - subject.execute(remote_mirror) - end - end - end - end end - describe 'Syncing tags' do - before do - allow(repository).to receive(:fetch_remote) { sync_remote(repository, remote_mirror.remote_name, local_branch_names) } + context 'when only syncing protected branches' do + let(:unprotected_branch_name) { 'existing-branch' } + let(:protected_branch_name) do + project.repository.branch_names.find { |n| n != unprotected_branch_name } end - - context 'when there are not tags to push' do - it 'does not try to push tags' do - allow(repository).to receive(:remote_tags) { {} } - allow(repository).to receive(:tags) { [] } - - expect(repository).not_to receive(:push_tags) - - subject.execute(remote_mirror) - end + let!(:protected_branch) do + create(:protected_branch, project: project, name: protected_branch_name) end - context 'when there are some tags to push' do - it 'pushes tags to remote' do - allow(raw_repository).to receive(:remote_tags) { {} } - - expect(raw_repository).to receive(:push_remote_branches).with(remote_mirror.remote_name, ['v1.0.0', 'v1.1.0']) - - subject.execute(remote_mirror) - end + before do + project.reload + remote_mirror.only_protected_branches = true end - context 'when there are some tags to delete' do - it 'deletes tags from remote' do - remote_tags = generate_tags(repository, 'v1.0.0', 'v1.1.0') - allow(raw_repository).to receive(:remote_tags) { remote_tags } - - repository.rm_tag(create(:user), 'v1.0.0') - - expect(raw_repository).to receive(:delete_remote_branches).with(remote_mirror.remote_name, ['v1.0.0']) + it "sync updated protected branches" do + allow(repository).to receive(:fetch_remote) + expect(remote_mirror).to receive(:update_repository).with(only_branches_matching: [protected_branch_name]) - subject.execute(remote_mirror) - end + subject.execute(remote_mirror) end end end - def create_branch(repository, branch_name) - rugged = repository.rugged - masterrev = repository.find_branch('master').dereferenced_target - parentrev = repository.commit(masterrev).parent_id - - rugged.references.create("refs/heads/#{branch_name}", parentrev) - - repository.expire_branches_cache - end - - def create_remote_branch(repository, remote_name, branch_name, source_id) - rugged = repository.rugged - - rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_id) - end - def sync_remote(repository, remote_name, local_branch_names) - rugged = repository.rugged - local_branch_names.each do |branch| - target = repository.find_branch(branch).try(:dereferenced_target) - rugged.references.create("refs/remotes/#{remote_name}/#{branch}", target.id) if target + commit = repository.commit(branch) + repository.write_ref("refs/remotes/#{remote_name}/#{branch}", commit.id) if commit end end def update_remote_branch(repository, remote_name, branch) - rugged = repository.rugged - masterrev = repository.find_branch('master').dereferenced_target.id + masterrev = repository.commit('master').id - rugged.references.create("refs/remotes/#{remote_name}/#{branch}", masterrev, force: true) + repository.write_ref("refs/remotes/#{remote_name}/#{branch}", masterrev, force: true) repository.expire_branches_cache end def update_branch(repository, branch) - rugged = repository.rugged - masterrev = repository.find_branch('master').dereferenced_target.id - - # Updated existing branch - rugged.references.create("refs/heads/#{branch}", masterrev, force: true) - repository.expire_branches_cache - end - - def delete_branch(repository, branch) - rugged = repository.rugged + masterrev = repository.commit('master').id - rugged.references.delete("refs/heads/#{branch}") + repository.write_ref("refs/heads/#{branch}", masterrev, force: true) repository.expire_branches_cache end -- cgit v1.2.1 From 29b4d657bec11ed8715f4e0ddeead61b9066e9a4 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 25 Jun 2018 14:56:27 +0200 Subject: Moves another RPC to mandatory This specific one isn't used on most machines, therefor low risk. Closes https://gitlab.com/gitlab-org/gitaly/issues/944 --- spec/lib/gitlab/git/repository_spec.rb | 48 +++++++++------------- .../lib/gitlab/import_export/repo_restorer_spec.rb | 2 +- 2 files changed, 20 insertions(+), 30 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 45f0006dc85..ce1bc3ef208 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -2160,43 +2160,33 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#create_from_bundle' do - shared_examples 'creating repo from bundle' do - let(:bundle_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") } - let(:project) { create(:project) } - let(:imported_repo) { project.repository.raw } + let(:bundle_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") } + let(:project) { create(:project) } + let(:imported_repo) { project.repository.raw } - before do - expect(repository.bundle_to_disk(bundle_path)).to be true - end - - after do - FileUtils.rm_rf(bundle_path) - end - - it 'creates a repo from a bundle file' do - expect(imported_repo).not_to exist + before do + expect(repository.bundle_to_disk(bundle_path)).to be_truthy + end - result = imported_repo.create_from_bundle(bundle_path) + after do + FileUtils.rm_rf(bundle_path) + end - expect(result).to be true - expect(imported_repo).to exist - expect { imported_repo.fsck }.not_to raise_exception - end + it 'creates a repo from a bundle file' do + expect(imported_repo).not_to exist - it 'creates a symlink to the global hooks dir' do - imported_repo.create_from_bundle(bundle_path) - hooks_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { File.join(imported_repo.path, 'hooks') } + result = imported_repo.create_from_bundle(bundle_path) - expect(File.readlink(hooks_path)).to eq(Gitlab.config.gitlab_shell.hooks_path) - end + expect(result).to be_truthy + expect(imported_repo).to exist + expect { imported_repo.fsck }.not_to raise_exception end - context 'when Gitaly create_repo_from_bundle feature is enabled' do - it_behaves_like 'creating repo from bundle' - end + it 'creates a symlink to the global hooks dir' do + imported_repo.create_from_bundle(bundle_path) + hooks_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { File.join(imported_repo.path, 'hooks') } - context 'when Gitaly create_repo_from_bundle feature is disabled', :disable_gitaly do - it_behaves_like 'creating repo from bundle' + expect(File.readlink(hooks_path)).to eq(Gitlab.config.gitlab_shell.hooks_path) end end diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb index 013b8895f67..7ffa84f906d 100644 --- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb @@ -30,7 +30,7 @@ describe Gitlab::ImportExport::RepoRestorer do end it 'restores the repo successfully' do - expect(restorer.restore).to be true + expect(restorer.restore).to be_truthy end it 'has the webhooks' do -- cgit v1.2.1 From 1345968ea6bda903670959b248175a0f42a0b3a0 Mon Sep 17 00:00:00 2001 From: Jan Beckmann Date: Mon, 25 Jun 2018 14:44:29 +0000 Subject: Resolve "WebHookService doesn't handle user info with nil passwords" --- spec/services/web_hook_service_spec.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'spec') diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index 7995f2c9ae7..622e56e1da5 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -60,6 +60,36 @@ describe WebHookService do ).once end + context 'when auth credentials are present' do + let(:url) {'https://example.org'} + let(:project_hook) { create(:project_hook, url: 'https://demo:demo@example.org/') } + + it 'uses the credentials' do + WebMock.stub_request(:post, url) + + service_instance.execute + + expect(WebMock).to have_requested(:post, url).with( + headers: headers.merge('Authorization' => 'Basic ZGVtbzpkZW1v') + ).once + end + end + + context 'when auth credentials are partial present' do + let(:url) {'https://example.org'} + let(:project_hook) { create(:project_hook, url: 'https://demo@example.org/') } + + it 'uses the credentials anyways' do + WebMock.stub_request(:post, url) + + service_instance.execute + + expect(WebMock).to have_requested(:post, url).with( + headers: headers.merge('Authorization' => 'Basic ZGVtbzo=') + ).once + end + end + it 'catches exceptions' do WebMock.stub_request(:post, project_hook.url).to_raise(StandardError.new('Some error')) -- cgit v1.2.1 From 625957c2ad70e5af03c988744ff3d4112d84e998 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 22 Jun 2018 14:17:03 -0300 Subject: BatchOpenIssuesCount only updates public issues count --- spec/services/projects/batch_open_issues_count_service_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/services/projects/batch_open_issues_count_service_spec.rb b/spec/services/projects/batch_open_issues_count_service_spec.rb index 720d7036396..599aaf62080 100644 --- a/spec/services/projects/batch_open_issues_count_service_spec.rb +++ b/spec/services/projects/batch_open_issues_count_service_spec.rb @@ -19,8 +19,10 @@ describe Projects::BatchOpenIssuesCountService do it 'refreshes cache keys correctly' do subject.refresh_cache - expect(Rails.cache.read(get_cache_key(subject, project_1))).to eq(2) - expect(Rails.cache.read(get_cache_key(subject, project_2))).to eq(2) + # It does not update total issues cache + expect(Rails.cache.read(get_cache_key(subject, project_1))).to eq(nil) + expect(Rails.cache.read(get_cache_key(subject, project_2))).to eq(nil) + expect(Rails.cache.read(get_cache_key(subject, project_1, true))).to eq(1) expect(Rails.cache.read(get_cache_key(subject, project_1, true))).to eq(1) end -- cgit v1.2.1 From ea25fbb8f594fb615571056c38d38a2a759c4e7b Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Mon, 25 Jun 2018 15:20:29 +0000 Subject: Notify conflict only for opened/locked merge requests --- spec/models/merge_request_spec.rb | 62 +++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 22 deletions(-) (limited to 'spec') diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 2c75816654e..ec72fefd137 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2134,8 +2134,7 @@ describe MergeRequest do describe 'transition to cannot_be_merged' do let(:notification_service) { double(:notification_service) } let(:todo_service) { double(:todo_service) } - - subject { create(:merge_request, merge_status: :unchecked) } + subject { create(:merge_request, state, merge_status: :unchecked) } before do allow(NotificationService).to receive(:new).and_return(notification_service) @@ -2144,33 +2143,52 @@ describe MergeRequest do allow(subject.project.repository).to receive(:can_be_merged?).and_return(false) end - it 'notifies conflict, but does not notify again if rechecking still results in cannot_be_merged' do - expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once - expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once + [:opened, :locked].each do |state| + context state do + let(:state) { state } - subject.mark_as_unmergeable - subject.mark_as_unchecked - subject.mark_as_unmergeable - end + it 'notifies conflict, but does not notify again if rechecking still results in cannot_be_merged' do + expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once + expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once + + subject.mark_as_unmergeable + subject.mark_as_unchecked + subject.mark_as_unmergeable + end + + it 'notifies conflict, whenever newly unmergeable' do + expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice + expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice + + subject.mark_as_unmergeable + subject.mark_as_unchecked + subject.mark_as_mergeable + subject.mark_as_unchecked + subject.mark_as_unmergeable + end + + it 'does not notify whenever merge request is newly unmergeable due to other reasons' do + allow(subject.project.repository).to receive(:can_be_merged?).and_return(true) - it 'notifies conflict, whenever newly unmergeable' do - expect(notification_service).to receive(:merge_request_unmergeable).with(subject).twice - expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).twice + expect(notification_service).not_to receive(:merge_request_unmergeable) + expect(todo_service).not_to receive(:merge_request_became_unmergeable) - subject.mark_as_unmergeable - subject.mark_as_unchecked - subject.mark_as_mergeable - subject.mark_as_unchecked - subject.mark_as_unmergeable + subject.mark_as_unmergeable + end + end end - it 'does not notify whenever merge request is newly unmergeable due to other reasons' do - allow(subject.project.repository).to receive(:can_be_merged?).and_return(true) + [:closed, :merged].each do |state| + let(:state) { state } - expect(notification_service).not_to receive(:merge_request_unmergeable) - expect(todo_service).not_to receive(:merge_request_became_unmergeable) + context state do + it 'does not notify' do + expect(notification_service).not_to receive(:merge_request_unmergeable) + expect(todo_service).not_to receive(:merge_request_became_unmergeable) - subject.mark_as_unmergeable + subject.mark_as_unmergeable + end + end end end -- cgit v1.2.1 From 2efe27ba181daa18db9e227b13be428ebdfc23f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20R=C3=BCttimann?= Date: Mon, 25 Jun 2018 15:32:03 +0000 Subject: Honor saml assurance level to allow 2FA bypassing --- .../omniauth_callbacks_controller_spec.rb | 189 ++++++++++++--------- spec/dependencies/omniauth_saml_spec.rb | 22 +++ spec/features/users/login_spec.rb | 35 +++- spec/fixtures/authentication/saml_response.xml | 42 +++++ spec/lib/gitlab/auth/o_auth/user_spec.rb | 8 + spec/lib/gitlab/auth/saml/auth_hash_spec.rb | 51 ++++++ spec/lib/gitlab/auth/saml/user_spec.rb | 41 +++++ spec/support/helpers/login_helpers.rb | 36 +++- 8 files changed, 332 insertions(+), 92 deletions(-) create mode 100644 spec/dependencies/omniauth_saml_spec.rb create mode 100644 spec/fixtures/authentication/saml_response.xml (limited to 'spec') diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index 5f0e8c5eca9..b23f183fec8 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -1,127 +1,162 @@ require 'spec_helper' -describe OmniauthCallbacksController do +describe OmniauthCallbacksController, type: :controller do include LoginHelpers - let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) } - - before do - mock_auth_hash(provider.to_s, extern_uid, user.email) - stub_omniauth_provider(provider, context: request) - end - - context 'when the user is on the last sign in attempt' do - let(:extern_uid) { 'my-uid' } + describe 'omniauth' do + let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) } before do - user.update(failed_attempts: User.maximum_attempts.pred) - subject.response = ActionDispatch::Response.new + mock_auth_hash(provider.to_s, extern_uid, user.email) + stub_omniauth_provider(provider, context: request) end - context 'when using a form based provider' do - let(:provider) { :ldap } - - it 'locks the user when sign in fails' do - allow(subject).to receive(:params).and_return(ActionController::Parameters.new(username: user.username)) - request.env['omniauth.error.strategy'] = OmniAuth::Strategies::LDAP.new(nil) - - subject.send(:failure) + context 'when the user is on the last sign in attempt' do + let(:extern_uid) { 'my-uid' } - expect(user.reload).to be_access_locked + before do + user.update(failed_attempts: User.maximum_attempts.pred) + subject.response = ActionDispatch::Response.new end - end - context 'when using a button based provider' do - let(:provider) { :github } + context 'when using a form based provider' do + let(:provider) { :ldap } - it 'does not lock the user when sign in fails' do - request.env['omniauth.error.strategy'] = OmniAuth::Strategies::GitHub.new(nil) + it 'locks the user when sign in fails' do + allow(subject).to receive(:params).and_return(ActionController::Parameters.new(username: user.username)) + request.env['omniauth.error.strategy'] = OmniAuth::Strategies::LDAP.new(nil) - subject.send(:failure) + subject.send(:failure) - expect(user.reload).not_to be_access_locked + expect(user.reload).to be_access_locked + end end - end - end - context 'strategies' do - context 'github' do - let(:extern_uid) { 'my-uid' } - let(:provider) { :github } + context 'when using a button based provider' do + let(:provider) { :github } - it 'allows sign in' do - post provider + it 'does not lock the user when sign in fails' do + request.env['omniauth.error.strategy'] = OmniAuth::Strategies::GitHub.new(nil) - expect(request.env['warden']).to be_authenticated - end - - shared_context 'sign_up' do - let(:user) { double(email: 'new@example.com') } + subject.send(:failure) - before do - stub_omniauth_setting(block_auto_created_users: false) + expect(user.reload).not_to be_access_locked end end + end - context 'sign up' do - include_context 'sign_up' + context 'strategies' do + context 'github' do + let(:extern_uid) { 'my-uid' } + let(:provider) { :github } - it 'is allowed' do + it 'allows sign in' do post provider expect(request.env['warden']).to be_authenticated end - end - - context 'when OAuth is disabled' do - before do - stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') - settings = Gitlab::CurrentSettings.current_application_settings - settings.update(disabled_oauth_sign_in_sources: [provider.to_s]) - end - it 'prevents login via POST' do - post provider + shared_context 'sign_up' do + let(:user) { double(email: 'new@example.com') } - expect(request.env['warden']).not_to be_authenticated + before do + stub_omniauth_setting(block_auto_created_users: false) + end end - it 'shows warning when attempting login' do - post provider - - expect(response).to redirect_to new_user_session_path - expect(flash[:alert]).to eq('Signing in using GitHub has been disabled') - end + context 'sign up' do + include_context 'sign_up' - it 'allows linking the disabled provider' do - user.identities.destroy_all - sign_in(user) + it 'is allowed' do + post provider - expect { post provider }.to change { user.reload.identities.count }.by(1) + expect(request.env['warden']).to be_authenticated + end end - context 'sign up' do - include_context 'sign_up' + context 'when OAuth is disabled' do + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + settings = Gitlab::CurrentSettings.current_application_settings + settings.update(disabled_oauth_sign_in_sources: [provider.to_s]) + end - it 'is prevented' do + it 'prevents login via POST' do post provider expect(request.env['warden']).not_to be_authenticated end + + it 'shows warning when attempting login' do + post provider + + expect(response).to redirect_to new_user_session_path + expect(flash[:alert]).to eq('Signing in using GitHub has been disabled') + end + + it 'allows linking the disabled provider' do + user.identities.destroy_all + sign_in(user) + + expect { post provider }.to change { user.reload.identities.count }.by(1) + end + + context 'sign up' do + include_context 'sign_up' + + it 'is prevented' do + post provider + + expect(request.env['warden']).not_to be_authenticated + end + end + end + end + + context 'auth0' do + let(:extern_uid) { '' } + let(:provider) { :auth0 } + + it 'does not allow sign in without extern_uid' do + post 'auth0' + + expect(request.env['warden']).not_to be_authenticated + expect(response.status).to eq(302) + expect(controller).to set_flash[:alert].to('Wrong extern UID provided. Make sure Auth0 is configured correctly.') end end end + end + + describe '#saml' do + let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml') } + let(:mock_saml_response) { File.read('spec/fixtures/authentication/saml_response.xml') } + let(:saml_config) { mock_saml_config_with_upstream_two_factor_authn_contexts } + + before do + stub_omniauth_saml_config({ enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], + providers: [saml_config] }) + mock_auth_hash('saml', 'my-uid', user.email, mock_saml_response) + request.env["devise.mapping"] = Devise.mappings[:user] + request.env['omniauth.auth'] = Rails.application.env_config['omniauth.auth'] + post :saml, params: { SAMLResponse: mock_saml_response } + end - context 'auth0' do - let(:extern_uid) { '' } - let(:provider) { :auth0 } + context 'when worth two factors' do + let(:mock_saml_response) do + File.read('spec/fixtures/authentication/saml_response.xml') + .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN') + end - it 'does not allow sign in without extern_uid' do - post 'auth0' + it 'expects user to be signed_in' do + expect(request.env['warden']).to be_authenticated + end + end + context 'when not worth two factors' do + it 'expects user to provide second factor' do + expect(response).to render_template('devise/sessions/two_factor') expect(request.env['warden']).not_to be_authenticated - expect(response.status).to eq(302) - expect(controller).to set_flash[:alert].to('Wrong extern UID provided. Make sure Auth0 is configured correctly.') end end end diff --git a/spec/dependencies/omniauth_saml_spec.rb b/spec/dependencies/omniauth_saml_spec.rb new file mode 100644 index 00000000000..ccc604dc230 --- /dev/null +++ b/spec/dependencies/omniauth_saml_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' +require 'omniauth/strategies/saml' + +describe 'processing of SAMLResponse in dependencies' do + let(:mock_saml_response) { File.read('spec/fixtures/authentication/saml_response.xml') } + let(:saml_strategy) { OmniAuth::Strategies::SAML.new({}) } + let(:session_mock) { {} } + let(:settings) { OpenStruct.new({ soft: false, idp_cert_fingerprint: 'something' }) } + let(:auth_hash) { Gitlab::Auth::Saml::AuthHash.new(saml_strategy) } + + subject { auth_hash.authn_context } + + before do + allow(saml_strategy).to receive(:session).and_return(session_mock) + allow_any_instance_of(OneLogin::RubySaml::Response).to receive(:is_valid?).and_return(true) + saml_strategy.send(:handle_response, mock_saml_response, {}, settings ) { } + end + + it 'can extract AuthnContextClassRef from SAMLResponse param' do + is_expected.to eq 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password' + end +end diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index 1f8d31a5c88..24a2c89f50b 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -177,14 +177,35 @@ feature 'Login' do end context 'logging in via OAuth' do - it 'shows 2FA prompt after OAuth login' do - stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [mock_saml_config]) - user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml') - gitlab_sign_in_via('saml', user, 'my-uid') + let(:user) { create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')} + let(:mock_saml_response) do + File.read('spec/fixtures/authentication/saml_response.xml') + end - expect(page).to have_content('Two-Factor Authentication') - enter_code(user.current_otp) - expect(current_path).to eq root_path + before do + stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], + providers: [mock_saml_config_with_upstream_two_factor_authn_contexts]) + gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response) + end + + context 'when authn_context is worth two factors' do + let(:mock_saml_response) do + File.read('spec/fixtures/authentication/saml_response.xml') + .gsub('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS') + end + + it 'signs user in without prompting for second factor' do + expect(page).not_to have_content('Two-Factor Authentication') + expect(current_path).to eq root_path + end + end + + context 'when authn_context is not worth two factors' do + it 'shows 2FA prompt after OAuth login' do + expect(page).to have_content('Two-Factor Authentication') + enter_code(user.current_otp) + expect(current_path).to eq root_path + end end end end diff --git a/spec/fixtures/authentication/saml_response.xml b/spec/fixtures/authentication/saml_response.xml new file mode 100644 index 00000000000..ac7b662be22 --- /dev/null +++ b/spec/fixtures/authentication/saml_response.xml @@ -0,0 +1,42 @@ + + + http://idp.example.com/metadata.php + + + z0Y25hsUHVJJnYhgB5LzPVjqbgM=NSdsZopzNX4kJETipLNbU+7dG4GPTj5e40iSBaUeUMc1UUSX4UCe9Qx6R9ADEkEQgNekgYaCFOuY90kLNh9Ky0Czq8gd4w7ykQJEVJ7VF7LakmG8dPedHAKyAMAuZ8y3mNGye31vtR9frYaznCVoxB3eAi9rbVOXkQtdOTRMHec= + MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg== + + + + + http://idp.example.com/metadata.php + + _ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7 + + + + + + + http://sp.example.com/demo1/metadata.php + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + test + + + test@example.com + + + users + examplerole1 + + + + diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb index 64f3d09a25b..3a8667e434d 100644 --- a/spec/lib/gitlab/auth/o_auth/user_spec.rb +++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb @@ -779,4 +779,12 @@ describe Gitlab::Auth::OAuth::User do end end end + + describe '#bypass_two_factor?' do + subject { oauth_user.bypass_two_factor? } + + it 'returns always false' do + is_expected.to be_falsey + end + end end diff --git a/spec/lib/gitlab/auth/saml/auth_hash_spec.rb b/spec/lib/gitlab/auth/saml/auth_hash_spec.rb index bb950e6bbf8..76f49e778fb 100644 --- a/spec/lib/gitlab/auth/saml/auth_hash_spec.rb +++ b/spec/lib/gitlab/auth/saml/auth_hash_spec.rb @@ -37,4 +37,55 @@ describe Gitlab::Auth::Saml::AuthHash do end end end + + describe '#authn_context' do + let(:auth_hash_data) do + { + provider: 'saml', + uid: 'some_uid', + info: + { + name: 'mockuser', + email: 'mock@email.ch', + image: 'mock_user_thumbnail_url' + }, + credentials: + { + token: 'mock_token', + secret: 'mock_secret' + }, + extra: + { + raw_info: + { + info: + { + name: 'mockuser', + email: 'mock@email.ch', + image: 'mock_user_thumbnail_url' + } + } + } + } + end + + subject(:saml_auth_hash) { described_class.new(OmniAuth::AuthHash.new(auth_hash_data)) } + + context 'with response_object' do + before do + auth_hash_data[:extra][:response_object] = { document: + saml_xml(File.read('spec/fixtures/authentication/saml_response.xml')) } + end + + it 'can extract authn_context' do + expect(saml_auth_hash.authn_context).to eq 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password' + end + end + + context 'without response_object' do + it 'returns an empty string' do + expect(saml_auth_hash.authn_context).to be_nil + end + end + end end diff --git a/spec/lib/gitlab/auth/saml/user_spec.rb b/spec/lib/gitlab/auth/saml/user_spec.rb index 62514ca0688..c523f5e177f 100644 --- a/spec/lib/gitlab/auth/saml/user_spec.rb +++ b/spec/lib/gitlab/auth/saml/user_spec.rb @@ -400,4 +400,45 @@ describe Gitlab::Auth::Saml::User do end end end + + describe '#bypass_two_factor?' do + let(:saml_config) { mock_saml_config_with_upstream_two_factor_authn_contexts } + + subject { saml_user.bypass_two_factor? } + + context 'with authn_contexts_worth_two_factors configured' do + before do + stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [saml_config]) + end + + it 'returns true when authn_context is worth two factors' do + allow(saml_user.auth_hash).to receive(:authn_context).and_return('urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS') + is_expected.to be_truthy + end + + it 'returns false when authn_context is not worth two factors' do + allow(saml_user.auth_hash).to receive(:authn_context).and_return('urn:oasis:names:tc:SAML:2.0:ac:classes:Password') + is_expected.to be_falsey + end + + it 'returns false when authn_context is blank' do + is_expected.to be_falsey + end + end + + context 'without auth_contexts_worth_two_factors_configured' do + before do + stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [mock_saml_config]) + end + + it 'returns false when authn_context is present' do + allow(saml_user.auth_hash).to receive(:authn_context).and_return('urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS') + is_expected.to be_falsey + end + + it 'returns false when authn_context is blank' do + is_expected.to be_falsey + end + end + end end diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 329f18cd288..87cfb6c04dc 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -46,8 +46,8 @@ module LoginHelpers @current_user = user end - def gitlab_sign_in_via(provider, user, uid) - mock_auth_hash(provider, uid, user.email) + def gitlab_sign_in_via(provider, user, uid, saml_response = nil) + mock_auth_hash(provider, uid, user.email, saml_response) visit new_user_session_path click_link provider end @@ -87,7 +87,7 @@ module LoginHelpers click_link "oauth-login-#{provider}" end - def mock_auth_hash(provider, uid, email) + def mock_auth_hash(provider, uid, email, saml_response = nil) # The mock_auth configuration allows you to set per-provider (or default) # authentication hashes to return during integration testing. OmniAuth.config.mock_auth[provider.to_sym] = OmniAuth::AuthHash.new({ @@ -109,12 +109,21 @@ module LoginHelpers email: email, image: 'mock_user_thumbnail_url' } + }, + response_object: { + document: saml_xml(saml_response) } } }) Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym] end + def saml_xml(raw_saml_response) + return '' if raw_saml_response.blank? + + XMLSecurity::SignedDocument.new(raw_saml_response, []) + end + def mock_saml_config OpenStruct.new(name: 'saml', label: 'saml', args: { assertion_consumer_service_url: 'https://localhost:3443/users/auth/saml/callback', @@ -125,6 +134,14 @@ module LoginHelpers }) end + def mock_saml_config_with_upstream_two_factor_authn_contexts + config = mock_saml_config + config.args[:upstream_two_factor_authn_contexts] = %w(urn:oasis:names:tc:SAML:2.0:ac:classes:CertificateProtectedTransport + urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS + urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorIGTOKEN) + config + end + def stub_omniauth_provider(provider, context: Rails.application) env = env_from_context(context) @@ -140,13 +157,16 @@ module LoginHelpers env['omniauth.error.strategy'] = strategy end - def stub_omniauth_saml_config(messages) - set_devise_mapping(context: Rails.application) - Rails.application.routes.disable_clear_and_finalize = true - Rails.application.routes.draw do + def stub_omniauth_saml_config(messages, context: Rails.application) + set_devise_mapping(context: context) + routes = Rails.application.routes + routes.disable_clear_and_finalize = true + routes.formatter.clear + routes.draw do post '/users/auth/saml' => 'omniauth_callbacks#saml' end - allow(Gitlab::Auth::OAuth::Provider).to receive_messages(providers: [:saml], config_for: mock_saml_config) + saml_config = messages.key?(:providers) ? messages[:providers].first : mock_saml_config + allow(Gitlab::Auth::OAuth::Provider).to receive_messages(providers: [:saml], config_for: saml_config) stub_omniauth_setting(messages) stub_saml_authorize_path_helpers end -- cgit v1.2.1 From 8c5eff29de2c6204deeb327594af1a936b2d9f7e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 25 Jun 2018 18:39:23 +0200 Subject: Fix branch API can_push attribute and add specs --- spec/requests/api/branches_spec.rb | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'spec') diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 64f51d9843d..0c417baeb77 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -155,6 +155,12 @@ describe API::Branches do end it_behaves_like 'repository branch' + + it 'returns that the current user cannot push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(false) + end end context 'when unauthenticated', 'and project is private' do @@ -169,6 +175,18 @@ describe API::Branches do it_behaves_like 'repository branch' + it 'returns that the current user can push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(true) + end + + it 'returns that the current user can push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(true) + end + context 'when branch contains a dot' do let(:branch_name) { branch_with_dot.name } @@ -202,6 +220,23 @@ describe API::Branches do end end + context 'when authenticated', 'as a developer and branch is protected' do + let(:current_user) { create(:user) } + let!(:protected_branch) { create(:protected_branch, project: project, name: branch_name) } + + before do + project.add_developer(current_user) + end + + it_behaves_like 'repository branch' + + it 'returns that the current user cannot push' do + get api(route, current_user) + + expect(json_response['can_push']).to eq(false) + end + end + context 'when authenticated', 'as a guest' do it_behaves_like '403 response' do let(:request) { get api(route, guest) } -- cgit v1.2.1 From 692db2e4a22f703807a22db77a5eb1adffc26af6 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 26 Jun 2018 02:04:40 +0800 Subject: Bring changes from EE --- spec/requests/api/boards_spec.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'spec') diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb index 92b614b087e..7710f19ce4e 100644 --- a/spec/requests/api/boards_spec.rb +++ b/spec/requests/api/boards_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe API::Boards do set(:user) { create(:user) } - set(:user2) { create(:user) } set(:non_member) { create(:user) } set(:guest) { create(:user) } set(:admin) { create(:user, :admin) } -- cgit v1.2.1 From 23cdae8eee4fb2017399f54ee0373367fd915e63 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Sat, 16 Jun 2018 14:50:13 -0700 Subject: Enable "prefer-destructuring" in JS files Partially addresses #47006. --- spec/javascripts/blob/3d_viewer/mesh_object_spec.js | 4 ++-- spec/javascripts/commit/pipelines/pipelines_spec.js | 2 +- spec/javascripts/deploy_keys/components/key_spec.js | 2 +- .../javascripts/diffs/components/diff_line_gutter_content_spec.js | 2 +- .../components/recent_searches_dropdown_content_spec.js | 8 ++++---- spec/javascripts/filtered_search/recent_searches_root_spec.js | 3 +-- spec/javascripts/gl_field_errors_spec.js | 2 +- spec/javascripts/groups/components/app_spec.js | 6 +++--- spec/javascripts/groups/components/group_item_spec.js | 2 +- spec/javascripts/ide/lib/diff/controller_spec.js | 2 +- spec/javascripts/namespace_select_spec.js | 4 ++-- spec/javascripts/notebook/cells/markdown_spec.js | 1 + spec/javascripts/pipelines/pipelines_table_row_spec.js | 2 +- spec/javascripts/pipelines/pipelines_table_spec.js | 2 +- spec/javascripts/smart_interval_spec.js | 4 ++-- spec/javascripts/vue_shared/components/file_icon_spec.js | 2 +- spec/javascripts/vue_shared/components/icon_spec.js | 2 +- .../vue_shared/components/user_avatar/user_avatar_image_spec.js | 4 ++-- .../vue_shared/components/user_avatar/user_avatar_link_spec.js | 2 +- 19 files changed, 28 insertions(+), 28 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js index d1ebae33dab..7651792be2e 100644 --- a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js +++ b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js @@ -26,7 +26,7 @@ describe('Mesh object', () => { const object = new MeshObject( new BoxGeometry(10, 10, 10), ); - const radius = object.geometry.boundingSphere.radius; + const { radius } = object.geometry.boundingSphere; expect(radius).not.toBeGreaterThan(4); }); @@ -35,7 +35,7 @@ describe('Mesh object', () => { const object = new MeshObject( new BoxGeometry(1, 1, 1), ); - const radius = object.geometry.boundingSphere.radius; + const { radius } = object.geometry.boundingSphere; expect(radius).toBeLessThan(1); }); diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js index 819ed7896ca..a18e09da50a 100644 --- a/spec/javascripts/commit/pipelines/pipelines_spec.js +++ b/spec/javascripts/commit/pipelines/pipelines_spec.js @@ -16,7 +16,7 @@ describe('Pipelines table in Commits and Merge requests', function () { beforeEach(() => { mock = new MockAdapter(axios); - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); PipelinesTable = Vue.extend(pipelinesTable); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js index 4279add21d1..d1de9d132b8 100644 --- a/spec/javascripts/deploy_keys/components/key_spec.js +++ b/spec/javascripts/deploy_keys/components/key_spec.js @@ -88,7 +88,7 @@ describe('Deploy keys key', () => { }); it('expands all project labels after click', done => { - const length = vm.deployKey.deploy_keys_projects.length; + const { length } = vm.deployKey.deploy_keys_projects; vm.$el.querySelectorAll('.deploy-project-label')[1].click(); Vue.nextTick(() => { diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js index 312a684f4d2..cce10c4083c 100644 --- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js +++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js @@ -92,7 +92,7 @@ describe('DiffLineGutterContent', () => { }); it('should return discussions for the given lineCode', () => { - const lineCode = getDiffFileMock().highlightedDiffLines[1].lineCode; + const { lineCode } = getDiffFileMock().highlightedDiffLines[1]; const component = createComponent({ lineCode, showCommentButton: true }); setDiscussions(component); diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js index 59bd2650081..d926663fac0 100644 --- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js +++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js @@ -103,7 +103,7 @@ describe('RecentSearchesDropdownContent', () => { describe('processedItems', () => { it('with items', () => { vm = createComponent(propsDataWithItems); - const processedItems = vm.processedItems; + const { processedItems } = vm; expect(processedItems.length).toEqual(2); @@ -122,7 +122,7 @@ describe('RecentSearchesDropdownContent', () => { it('with no items', () => { vm = createComponent(propsDataWithoutItems); - const processedItems = vm.processedItems; + const { processedItems } = vm; expect(processedItems.length).toEqual(0); }); @@ -131,13 +131,13 @@ describe('RecentSearchesDropdownContent', () => { describe('hasItems', () => { it('with items', () => { vm = createComponent(propsDataWithItems); - const hasItems = vm.hasItems; + const { hasItems } = vm; expect(hasItems).toEqual(true); }); it('with no items', () => { vm = createComponent(propsDataWithoutItems); - const hasItems = vm.hasItems; + const { hasItems } = vm; expect(hasItems).toEqual(false); }); }); diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js index 1e6272bad0b..d063fcf4f2d 100644 --- a/spec/javascripts/filtered_search/recent_searches_root_spec.js +++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js @@ -15,8 +15,7 @@ describe('RecentSearchesRoot', () => { }; VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake((options) => { - data = options.data; - template = options.template; + ({ data, template } = options); }); RecentSearchesRoot.prototype.render.call(recentSearchesRoot); diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index 2839020b2ca..21c462cd040 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -18,7 +18,7 @@ describe('GL Style Field Errors', function() { expect(this.$form).toBeDefined(); expect(this.$form.length).toBe(1); expect(this.fieldErrors).toBeDefined(); - const inputs = this.fieldErrors.state.inputs; + const { inputs } = this.fieldErrors.state; expect(inputs.length).toBe(4); }); diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js index 2b92c485f41..03d4b472b87 100644 --- a/spec/javascripts/groups/components/app_spec.js +++ b/spec/javascripts/groups/components/app_spec.js @@ -67,7 +67,7 @@ describe('AppComponent', () => { it('should return list of groups from store', () => { spyOn(vm.store, 'getGroups'); - const groups = vm.groups; + const { groups } = vm; expect(vm.store.getGroups).toHaveBeenCalled(); expect(groups).not.toBeDefined(); }); @@ -77,7 +77,7 @@ describe('AppComponent', () => { it('should return pagination info from store', () => { spyOn(vm.store, 'getPaginationInfo'); - const pageInfo = vm.pageInfo; + const { pageInfo } = vm; expect(vm.store.getPaginationInfo).toHaveBeenCalled(); expect(pageInfo).not.toBeDefined(); }); @@ -293,7 +293,7 @@ describe('AppComponent', () => { beforeEach(() => { groupItem = Object.assign({}, mockParentGroupItem); groupItem.children = mockChildren; - childGroupItem = groupItem.children[0]; + [childGroupItem] = groupItem.children; groupItem.isChildrenLoading = false; vm.targetGroup = childGroupItem; vm.targetParentGroup = groupItem; diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js index 49a139855c8..d0cac5efc40 100644 --- a/spec/javascripts/groups/components/group_item_spec.js +++ b/spec/javascripts/groups/components/group_item_spec.js @@ -41,7 +41,7 @@ describe('GroupItemComponent', () => { describe('rowClass', () => { it('should return map of classes based on group details', () => { const classes = ['is-open', 'has-children', 'has-description', 'being-removed']; - const rowClass = vm.rowClass; + const { rowClass } = vm; expect(Object.keys(rowClass).length).toBe(classes.length); Object.keys(rowClass).forEach((className) => { diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js index 96abd1dcd9e..90ebb95b687 100644 --- a/spec/javascripts/ide/lib/diff/controller_spec.js +++ b/spec/javascripts/ide/lib/diff/controller_spec.js @@ -63,7 +63,7 @@ describe('Multi-file editor library dirty diff controller', () => { [type]: true, }; - const range = getDecorator(change).range; + const { range } = getDecorator(change); expect(range.startLineNumber).toBe(1); expect(range.endLineNumber).toBe(2); diff --git a/spec/javascripts/namespace_select_spec.js b/spec/javascripts/namespace_select_spec.js index 3b2641f7646..07b82ce721e 100644 --- a/spec/javascripts/namespace_select_spec.js +++ b/spec/javascripts/namespace_select_spec.js @@ -22,7 +22,7 @@ describe('NamespaceSelect', () => { const dropdown = document.createElement('div'); // eslint-disable-next-line no-new new NamespaceSelect({ dropdown }); - glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0]; + [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0); }); it('prevents click events', () => { @@ -43,7 +43,7 @@ describe('NamespaceSelect', () => { dropdown.dataset.isFilter = 'true'; // eslint-disable-next-line no-new new NamespaceSelect({ dropdown }); - glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0]; + [glDropdownOptions] = $.fn.glDropdown.calls.argsFor(0); }); it('does not prevent click events', () => { diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js index 8f8ba231ae8..0b1b11de1fd 100644 --- a/spec/javascripts/notebook/cells/markdown_spec.js +++ b/spec/javascripts/notebook/cells/markdown_spec.js @@ -14,6 +14,7 @@ describe('Markdown component', () => { beforeEach((done) => { json = getJSONFixture('blob/notebook/basic.json'); + // eslint-disable-next-line prefer-destructuring cell = json.cells[1]; vm = new Component({ diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js index 78d8e9e572e..03ffc122795 100644 --- a/spec/javascripts/pipelines/pipelines_table_row_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js @@ -24,7 +24,7 @@ describe('Pipelines Table Row', () => { preloadFixtures(jsonFixtureName); beforeEach(() => { - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); pipelineWithoutAuthor = pipelines.find(p => p.user === null && p.commit !== null); diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js index 4fc3c08145e..d21ba35e96d 100644 --- a/spec/javascripts/pipelines/pipelines_table_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_spec.js @@ -11,7 +11,7 @@ describe('Pipelines Table', () => { preloadFixtures(jsonFixtureName); beforeEach(() => { - const pipelines = getJSONFixture(jsonFixtureName).pipelines; + const { pipelines } = getJSONFixture(jsonFixtureName); PipelinesTableComponent = Vue.extend(pipelinesTableComp); pipeline = pipelines.find(p => p.user !== null && p.commit !== null); diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js index a54219d58c2..60153672214 100644 --- a/spec/javascripts/smart_interval_spec.js +++ b/spec/javascripts/smart_interval_spec.js @@ -87,7 +87,7 @@ describe('SmartInterval', function () { setTimeout(() => { interval.cancel(); - const intervalId = interval.state.intervalId; + const { intervalId } = interval.state; const currentInterval = interval.getCurrentInterval(); const intervalLowerLimit = interval.cfg.startingInterval; @@ -106,7 +106,7 @@ describe('SmartInterval', function () { interval.resume(); - const intervalId = interval.state.intervalId; + const { intervalId } = interval.state; expect(intervalId).toBeTruthy(); diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js index f7581251bf0..1c666fc6c55 100644 --- a/spec/javascripts/vue_shared/components/file_icon_spec.js +++ b/spec/javascripts/vue_shared/components/file_icon_spec.js @@ -74,7 +74,7 @@ describe('File Icon component', () => { size: 120, }); - const classList = vm.$el.firstChild.classList; + const { classList } = vm.$el.firstChild; const containsSizeClass = classList.contains('s120'); const containsCustomClass = classList.contains('extraclasses'); expect(containsSizeClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js index 68d57ebc8f0..cc030e29d61 100644 --- a/spec/javascripts/vue_shared/components/icon_spec.js +++ b/spec/javascripts/vue_shared/components/icon_spec.js @@ -44,7 +44,7 @@ describe('Sprite Icon Component', function () { }); it('should properly render img css', function () { - const classList = icon.$el.classList; + const { classList } = icon.$el; const containsSizeClass = classList.contains('s32'); const containsCustomClass = classList.contains('extraclasses'); expect(containsSizeClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js index 446f025c127..656b57d764e 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js @@ -51,7 +51,7 @@ describe('User Avatar Image Component', function () { }); it('should properly render img css', function () { - const classList = vm.$el.classList; + const { classList } = vm.$el; const containsAvatar = classList.contains('avatar'); const containsSizeClass = classList.contains('s99'); const containsCustomClass = classList.contains(DEFAULT_PROPS.cssClasses); @@ -73,7 +73,7 @@ describe('User Avatar Image Component', function () { }); it('should add lazy attributes', function () { - const classList = vm.$el.classList; + const { classList } = vm.$el; const lazyClass = classList.contains('lazy'); expect(lazyClass).toBe(true); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js index adf80d0c2bb..4c5c242cbb3 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js @@ -21,7 +21,7 @@ describe('User Avatar Link Component', function () { propsData: this.propsData, }).$mount(); - this.userAvatarImage = this.userAvatarLink.$children[0]; + [this.userAvatarImage] = this.userAvatarLink.$children; }); it('should return a defined Vue component', function () { -- cgit v1.2.1 From 90f45e76688d2b79904a5213cc60da2e4af807ff Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 25 Jun 2018 16:00:27 -0500 Subject: CE-EE parity for spec/models/repository_spec.rb --- spec/models/repository_spec.rb | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index d817a8376f4..c7e751130d8 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -46,7 +46,7 @@ describe Repository do it { is_expected.not_to include('feature') } it { is_expected.not_to include('fix') } - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.branch_names_contains(sample_commit.id) @@ -192,7 +192,7 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.last_commit_id_for_path(sample_commit.id, '.gitignore') @@ -226,7 +226,7 @@ describe Repository do is_expected.to eq('c1acaa5') end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.last_commit_for_path(sample_commit.id, '.gitignore').id @@ -391,7 +391,7 @@ describe Repository do it_behaves_like 'finding commits by message' end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error { broken_repository.find_commits_by_message('s') } end @@ -695,7 +695,7 @@ describe Repository do expect(results).to match_array([]) end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.search_files_by_content('feature', 'master') @@ -744,7 +744,7 @@ describe Repository do expect(results).to match_array([]) end - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error { broken_repository.search_files_by_name('files', 'master') } end @@ -796,7 +796,7 @@ describe Repository do describe '#fetch_ref' do let(:broken_repository) { create(:project, :broken_storage).repository } - describe 'when storage is broken', :broken_storage do + describe 'when storage is broken', :broken_storage do it 'should raise a storage error' do expect_to_raise_storage_error do broken_repository.fetch_ref(broken_repository, source_ref: '1', target_ref: '2') @@ -2294,6 +2294,28 @@ describe Repository do end end + describe '#local_branches' do + it 'returns the local branches' do + masterrev = repository.find_branch('master').dereferenced_target + create_remote_branch('joe', 'remote_branch', masterrev) + repository.add_branch(user, 'local_branch', masterrev.id) + + expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) + expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) + end + end + + describe '#remote_branches' do + it 'returns the remote branches' do + masterrev = repository.find_branch('master').dereferenced_target + create_remote_branch('joe', 'remote_branch', masterrev) + repository.add_branch(user, 'local_branch', masterrev.id) + + expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false) + expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true) + end + end + describe '#commit_count' do context 'with a non-existing repository' do it 'returns 0' do -- cgit v1.2.1 From e8edf620f10fb03edfefc32af0b9a9b780ab107a Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 25 Jun 2018 16:46:45 -0300 Subject: Fix sorting by name on explore projects page --- spec/models/concerns/sortable_spec.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb index b821a84d5e0..39c16ae60af 100644 --- a/spec/models/concerns/sortable_spec.rb +++ b/spec/models/concerns/sortable_spec.rb @@ -40,15 +40,25 @@ describe Sortable do describe 'ordering by name' do it 'ascending' do - expect(relation).to receive(:reorder).with("lower(name) asc") + expect(relation).to receive(:reorder).once.and_call_original - relation.order_by('name_asc') + table = Regexp.escape(ActiveRecord::Base.connection.quote_table_name(:namespaces)) + column = Regexp.escape(ActiveRecord::Base.connection.quote_column_name(:name)) + + sql = relation.order_by('name_asc').to_sql + + expect(sql).to match /.+ORDER BY LOWER\(#{table}.#{column}\) ASC\z/ end it 'descending' do - expect(relation).to receive(:reorder).with("lower(name) desc") + expect(relation).to receive(:reorder).once.and_call_original + + table = Regexp.escape(ActiveRecord::Base.connection.quote_table_name(:namespaces)) + column = Regexp.escape(ActiveRecord::Base.connection.quote_column_name(:name)) + + sql = relation.order_by('name_desc').to_sql - relation.order_by('name_desc') + expect(sql).to match /.+ORDER BY LOWER\(#{table}.#{column}\) DESC\z/ end end -- cgit v1.2.1 From dcdcbfa40b7732b12650d8046a84bed346843c90 Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Mon, 25 Jun 2018 21:32:00 +0000 Subject: Improve shelling out in bin/changelog --- spec/bin/changelog_spec.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'spec') diff --git a/spec/bin/changelog_spec.rb b/spec/bin/changelog_spec.rb index fc1bf67d7b9..f278043028f 100644 --- a/spec/bin/changelog_spec.rb +++ b/spec/bin/changelog_spec.rb @@ -56,11 +56,11 @@ describe 'bin/changelog' do it 'parses -h' do expect do expect { described_class.parse(%w[foo -h bar]) }.to output.to_stdout - end.to raise_error(SystemExit) + end.to raise_error(ChangelogHelpers::Done) end it 'assigns title' do - options = described_class.parse(%W[foo -m 1 bar\n -u baz\r\n --amend]) + options = described_class.parse(%W[foo -m 1 bar\n baz\r\n --amend]) expect(options.title).to eq 'foo bar baz' end @@ -82,9 +82,10 @@ describe 'bin/changelog' do it 'shows error message and exits the program' do allow($stdin).to receive(:getc).and_return(type) expect do - expect do - expect { described_class.read_type }.to raise_error(SystemExit) - end.to output("Invalid category index, please select an index between 1 and 8\n").to_stderr + expect { described_class.read_type }.to raise_error( + ChangelogHelpers::Abort, + 'Invalid category index, please select an index between 1 and 8' + ) end.to output.to_stdout end end -- cgit v1.2.1 From c2a48fd163bf9e345ad7baf4707f6bb50de5be78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 25 Jun 2018 10:28:19 +0200 Subject: Ignore unknown OAuth sources in ApplicationSetting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/features/admin/admin_settings_spec.rb | 23 +++++++++++++++++++ spec/models/application_setting_spec.rb | 36 ++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 9 deletions(-) (limited to 'spec') diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index e7aca94db66..f3ab4ff771a 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -124,6 +124,29 @@ feature 'Admin updates settings' do expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).not_to include('google_oauth2') end + scenario 'Oauth providers do not raise validation errors when saving unrelated changes' do + expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to be_empty + + page.within('.as-signin') do + uncheck 'Google' + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2') + + # Remove google_oauth2 from the Omniauth strategies + allow(Devise).to receive(:omniauth_providers).and_return([]) + + # Save an unrelated setting + page.within('.as-ci-cd') do + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + expect(Gitlab::CurrentSettings.disabled_oauth_sign_in_sources).to include('google_oauth2') + end + scenario 'Change Help page' do page.within('.as-help-page') do fill_in 'Help page text', with: 'Example text' diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 3e6656e0f12..02f74e2ea54 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -25,15 +25,6 @@ describe ApplicationSetting do it { is_expected.to allow_value(https).for(:after_sign_out_path) } it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) } - describe 'disabled_oauth_sign_in_sources validations' do - before do - allow(Devise).to receive(:omniauth_providers).and_return([:github]) - end - - it { is_expected.to allow_value(['github']).for(:disabled_oauth_sign_in_sources) } - it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) } - end - describe 'default_artifacts_expire_in' do it 'sets an error if it cannot parse' do setting.update(default_artifacts_expire_in: 'a') @@ -314,6 +305,33 @@ describe ApplicationSetting do end end + describe '#disabled_oauth_sign_in_sources=' do + before do + allow(Devise).to receive(:omniauth_providers).and_return([:github]) + end + + it 'removes unknown sources (as strings) from the array' do + subject.disabled_oauth_sign_in_sources = %w[github test] + + expect(subject).to be_valid + expect(subject.disabled_oauth_sign_in_sources).to eq ['github'] + end + + it 'removes unknown sources (as symbols) from the array' do + subject.disabled_oauth_sign_in_sources = %i[github test] + + expect(subject).to be_valid + expect(subject.disabled_oauth_sign_in_sources).to eq ['github'] + end + + it 'ignores nil' do + subject.disabled_oauth_sign_in_sources = nil + + expect(subject).to be_valid + expect(subject.disabled_oauth_sign_in_sources).to be_empty + end + end + context 'restricted signup domains' do it 'sets single domain' do setting.domain_whitelist_raw = 'example.com' -- cgit v1.2.1 From 7e9448b747aa0be4ff3ea8446f50486501ded044 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 26 Jun 2018 09:27:32 +0000 Subject: Remove duplicate spec --- spec/requests/api/branches_spec.rb | 6 ------ 1 file changed, 6 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 0c417baeb77..9bb6ed62393 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -181,12 +181,6 @@ describe API::Branches do expect(json_response['can_push']).to eq(true) end - it 'returns that the current user can push' do - get api(route, current_user) - - expect(json_response['can_push']).to eq(true) - end - context 'when branch contains a dot' do let(:branch_name) { branch_with_dot.name } -- cgit v1.2.1 From d5a1910f0ce24cf88bc7f0279904a8d5088e2ffa Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 26 Jun 2018 17:58:51 +0800 Subject: Port Namespace#root_ancestor to CE --- spec/models/namespace_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'spec') diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 18b01c3e6b7..70f1a1c8b38 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -655,6 +655,19 @@ describe Namespace do end end + describe '#root_ancestor' do + it 'returns the top most ancestor', :nested_groups do + root_group = create(:group) + nested_group = create(:group, parent: root_group) + deep_nested_group = create(:group, parent: nested_group) + very_deep_nested_group = create(:group, parent: deep_nested_group) + + expect(nested_group.root_ancestor).to eq(root_group) + expect(deep_nested_group.root_ancestor).to eq(root_group) + expect(very_deep_nested_group.root_ancestor).to eq(root_group) + end + end + describe '#remove_exports' do let(:legacy_project) { create(:project, :with_export, :legacy_storage, namespace: namespace) } let(:hashed_project) { create(:project, :with_export, namespace: namespace) } -- cgit v1.2.1 From 33c950f293f13aa4c4e1b2c098695cba3cd36029 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 26 Jun 2018 14:12:08 +0200 Subject: Client-side fix for Gitaly TreeEntry bug --- spec/models/commit_spec.rb | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) (limited to 'spec') diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 090f91168ad..5157d8fc645 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -514,30 +514,21 @@ eos end describe '#uri_type' do - shared_examples 'URI type' do - it 'returns the URI type at the given path' do - expect(commit.uri_type('files/html')).to be(:tree) - expect(commit.uri_type('files/images/logo-black.png')).to be(:raw) - expect(project.commit('video').uri_type('files/videos/intro.mp4')).to be(:raw) - expect(commit.uri_type('files/js/application.js')).to be(:blob) - end - - it "returns nil if the path doesn't exists" do - expect(commit.uri_type('this/path/doesnt/exist')).to be_nil - end - - it 'is nil if the path is nil or empty' do - expect(commit.uri_type(nil)).to be_nil - expect(commit.uri_type("")).to be_nil - end + it 'returns the URI type at the given path' do + expect(commit.uri_type('files/html')).to be(:tree) + expect(commit.uri_type('files/images/logo-black.png')).to be(:raw) + expect(project.commit('video').uri_type('files/videos/intro.mp4')).to be(:raw) + expect(commit.uri_type('files/js/application.js')).to be(:blob) end - context 'when Gitaly commit_tree_entry feature is enabled' do - it_behaves_like 'URI type' + it "returns nil if the path doesn't exists" do + expect(commit.uri_type('this/path/doesnt/exist')).to be_nil + expect(commit.uri_type('../path/doesnt/exist')).to be_nil end - context 'when Gitaly commit_tree_entry feature is disabled', :disable_gitaly do - it_behaves_like 'URI type' + it 'is nil if the path is nil or empty' do + expect(commit.uri_type(nil)).to be_nil + expect(commit.uri_type("")).to be_nil end end -- cgit v1.2.1 From d3d9077830527c21ece7d9dc1a31a94a290afcdc Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 22 Jun 2018 14:26:01 +0100 Subject: Add a helper to rename a column using a background migration This works the same way as change_column_type_using_background_migration, but for renaming a column. It takes a table, not a relation, to match its concurrent counterpart. Also, generalise the cleanup migrations to reduce code duplication. --- spec/lib/gitlab/database/migration_helpers_spec.rb | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 280f799f2ab..eb7148ff108 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -1178,6 +1178,61 @@ describe Gitlab::Database::MigrationHelpers do end end + describe '#rename_column_using_background_migration' do + let!(:issue) { create(:issue, :closed, closed_at: Time.zone.now) } + + it 'renames a column using a background migration' do + expect(model) + .to receive(:add_column) + .with( + 'issues', + :closed_at_timestamp, + :datetime_with_timezone, + limit: anything, + precision: anything, + scale: anything + ) + + expect(model) + .to receive(:install_rename_triggers) + .with('issues', :closed_at, :closed_at_timestamp) + + expect(BackgroundMigrationWorker) + .to receive(:perform_in) + .ordered + .with( + 10.minutes, + 'CopyColumn', + ['issues', :closed_at, :closed_at_timestamp, issue.id, issue.id] + ) + + expect(BackgroundMigrationWorker) + .to receive(:perform_in) + .ordered + .with( + 1.hour + 10.minutes, + 'CleanupConcurrentRename', + ['issues', :closed_at, :closed_at_timestamp] + ) + + expect(Gitlab::BackgroundMigration) + .to receive(:steal) + .ordered + .with('CopyColumn') + + expect(Gitlab::BackgroundMigration) + .to receive(:steal) + .ordered + .with('CleanupConcurrentRename') + + model.rename_column_using_background_migration( + 'issues', + :closed_at, + :closed_at_timestamp + ) + end + end + describe '#perform_background_migration_inline?' do it 'returns true in a test environment' do allow(Rails.env) -- cgit v1.2.1 From 1b617fa677c59e228d0335cef78907fda77143a0 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 26 Jun 2018 13:29:06 +0100 Subject: Moves boards components into a .vue file --- spec/javascripts/boards/issue_card_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index 05acf903933..72e961c8a10 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -9,7 +9,7 @@ import '~/vue_shared/models/assignee'; import '~/boards/models/issue'; import '~/boards/models/list'; import '~/boards/stores/boards_store'; -import '~/boards/components/issue_card_inner'; +import IssueCardInner from '~/boards/components/issue_card_inner.vue'; import { listObj } from './mock_data'; describe('Issue card component', () => { @@ -48,7 +48,7 @@ describe('Issue card component', () => { component = new Vue({ el: document.querySelector('.test-container'), components: { - 'issue-card': gl.issueBoards.IssueCardInner, + 'issue-card': IssueCardInner, }, data() { return { -- cgit v1.2.1 From 1bfbee5665b7a6b5d9aed9f12028b2d76d1da9c3 Mon Sep 17 00:00:00 2001 From: Paul Slaughter Date: Tue, 26 Jun 2018 15:50:13 +0000 Subject: Show file in tree on WebIDE open --- spec/javascripts/ide/helpers.js | 26 +++++++++++++++-- spec/javascripts/ide/stores/actions/tree_spec.js | 36 +++++++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js index 9312e17704e..b5a72d4bd6f 100644 --- a/spec/javascripts/ide/helpers.js +++ b/spec/javascripts/ide/helpers.js @@ -1,3 +1,4 @@ +import * as pathUtils from 'path'; import { decorateData } from '~/ide/stores/utils'; import state from '~/ide/stores/state'; import commitState from '~/ide/stores/modules/commit/state'; @@ -14,13 +15,34 @@ export const resetStore = store => { store.replaceState(newState); }; -export const file = (name = 'name', id = name, type = '') => +export const file = (name = 'name', id = name, type = '', parent = null) => decorateData({ id, type, icon: 'icon', url: 'url', name, - path: name, + path: parent ? `${parent.path}/${name}` : name, + parentPath: parent ? parent.path : '', lastCommit: {}, }); + +export const createEntriesFromPaths = (paths) => + paths + .map(path => ({ + name: pathUtils.basename(path), + dir: pathUtils.dirname(path), + ext: pathUtils.extname(path), + })) + .reduce((entries, path, idx) => { + const name = path.name; + const parent = path.dir ? entries[path.dir] : null; + const type = path.ext ? 'blob' : 'tree'; + + const entry = file(name, (idx + 1).toString(), type, parent); + + return { + [entry.path]: entry, + ...entries, + }; + }, {}); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index e0ef57a3966..cefed9ddb43 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -1,8 +1,11 @@ import Vue from 'vue'; +import testAction from 'spec/helpers/vuex_action_helper'; +import { showTreeEntry } from '~/ide/stores/actions/tree'; +import * as types from '~/ide/stores/mutation_types'; import store from '~/ide/stores'; import service from '~/ide/services'; import router from '~/ide/ide_router'; -import { file, resetStore } from '../../helpers'; +import { file, resetStore, createEntriesFromPaths } from '../../helpers'; describe('Multi-file store tree actions', () => { let projectTree; @@ -96,6 +99,37 @@ describe('Multi-file store tree actions', () => { }); }); + describe('showTreeEntry', () => { + beforeEach(() => { + const paths = [ + 'grandparent', + 'ancestor', + 'grandparent/parent', + 'grandparent/aunt', + 'grandparent/parent/child.txt', + 'grandparent/aunt/cousing.txt', + ]; + + Object.assign(store.state.entries, createEntriesFromPaths(paths)); + }); + + it('opens the parents', done => { + testAction( + showTreeEntry, + 'grandparent/parent/child.txt', + store.state, + [ + { type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }, + { type: types.SET_TREE_OPEN, payload: 'grandparent' }, + ], + [ + { type: 'showTreeEntry' }, + ], + done, + ); + }); + }); + describe('getLastCommitData', () => { beforeEach(() => { spyOn(service, 'getTreeLastCommit').and.returnValue( -- cgit v1.2.1 From f843285d4950cdf4f5f048a77a60f17f0b972621 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 26 Jun 2018 17:18:56 +0100 Subject: Fixed eslint failure in IDE spec helpers --- spec/javascripts/ide/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js index b5a72d4bd6f..569fa5c7aae 100644 --- a/spec/javascripts/ide/helpers.js +++ b/spec/javascripts/ide/helpers.js @@ -27,7 +27,7 @@ export const file = (name = 'name', id = name, type = '', parent = null) => lastCommit: {}, }); -export const createEntriesFromPaths = (paths) => +export const createEntriesFromPaths = paths => paths .map(path => ({ name: pathUtils.basename(path), @@ -35,7 +35,7 @@ export const createEntriesFromPaths = (paths) => ext: pathUtils.extname(path), })) .reduce((entries, path, idx) => { - const name = path.name; + const { name } = path; const parent = path.dir ? entries[path.dir] : null; const type = path.ext ? 'blob' : 'tree'; -- cgit v1.2.1 From 53f77cfee164b3e0f7e3efb6511dee5e72dd75e6 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Tue, 26 Jun 2018 18:49:22 +0000 Subject: Added Diff Viewer to new VUE based MR page --- .../diffs/components/diff_content_spec.js | 96 +++++++++++++++++++++- spec/javascripts/diffs/store/actions_spec.js | 13 +-- spec/javascripts/diffs/store/mutations_spec.js | 8 +- .../javascripts/helpers/init_vue_mr_page_helper.js | 2 + 4 files changed, 109 insertions(+), 10 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js index 7237274eb43..dea600a783a 100644 --- a/spec/javascripts/diffs/components/diff_content_spec.js +++ b/spec/javascripts/diffs/components/diff_content_spec.js @@ -1 +1,95 @@ -// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034 +import Vue from 'vue'; +import DiffContentComponent from '~/diffs/components/diff_content.vue'; +import store from '~/mr_notes/stores'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants'; +import diffFileMockData from '../mock_data/diff_file'; + +describe('DiffContent', () => { + const Component = Vue.extend(DiffContentComponent); + let vm; + const getDiffFileMock = () => Object.assign({}, diffFileMockData); + + beforeEach(() => { + vm = mountComponentWithStore(Component, { + store, + props: { + diffFile: getDiffFileMock(), + }, + }); + }); + + describe('text based files', () => { + it('should render diff inline view', done => { + vm.$store.state.diffs.diffViewType = 'inline'; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(1); + + done(); + }); + }); + + it('should render diff parallel view', done => { + vm.$store.state.diffs.diffViewType = 'parallel'; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.parallel').length).toEqual(18); + + done(); + }); + }); + }); + + describe('Non-Text diffs', () => { + beforeEach(() => { + vm.diffFile.text = false; + }); + + describe('image diff', () => { + beforeEach(() => { + vm.diffFile.newPath = GREEN_BOX_IMAGE_URL; + vm.diffFile.newSha = 'DEF'; + vm.diffFile.oldPath = RED_BOX_IMAGE_URL; + vm.diffFile.oldSha = 'ABC'; + vm.diffFile.viewPath = ''; + }); + + it('should have image diff view in place', done => { + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(0); + + expect(vm.$el.querySelectorAll('.diff-viewer .image').length).toEqual(1); + + done(); + }); + }); + }); + + describe('file diff', () => { + it('should have download buttons in place', done => { + const el = vm.$el; + vm.diffFile.newPath = 'test.abc'; + vm.diffFile.newSha = 'DEF'; + vm.diffFile.oldPath = 'test.abc'; + vm.diffFile.oldSha = 'ABC'; + + vm.$nextTick(() => { + expect(el.querySelectorAll('.js-diff-inline-view').length).toEqual(0); + + expect(el.querySelector('.deleted .file-info').textContent.trim()).toContain('test.abc'); + expect(el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain( + 'Download', + ); + + expect(el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc'); + expect(el.querySelector('.added .btn.btn-default').textContent.trim()).toContain( + 'Download', + ); + + done(); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js index e61780c9928..f0bd098f698 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -12,15 +12,16 @@ import axios from '~/lib/utils/axios_utils'; import testAction from '../../helpers/vuex_action_helper'; describe('DiffsStoreActions', () => { - describe('setEndpoint', () => { - it('should set given endpoint', done => { + describe('setBaseConfig', () => { + it('should set given endpoint and project path', done => { const endpoint = '/diffs/set/endpoint'; + const projectPath = '/root/project'; testAction( - actions.setEndpoint, - endpoint, - { endpoint: '' }, - [{ type: types.SET_ENDPOINT, payload: endpoint }], + actions.setBaseConfig, + { endpoint, projectPath }, + { endpoint: '', projectPath: '' }, + [{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }], [], done, ); diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js index 5f1a6e9def7..02836fcaeea 100644 --- a/spec/javascripts/diffs/store/mutations_spec.js +++ b/spec/javascripts/diffs/store/mutations_spec.js @@ -3,13 +3,15 @@ import * as types from '~/diffs/store/mutation_types'; import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; describe('DiffsStoreMutations', () => { - describe('SET_ENDPOINT', () => { - it('should set endpoint', () => { + describe('SET_BASE_CONFIG', () => { + it('should set endpoint and project path', () => { const state = {}; const endpoint = '/diffs/endpoint'; + const projectPath = '/root/project'; - mutations[types.SET_ENDPOINT](state, endpoint); + mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath }); expect(state.endpoint).toEqual(endpoint); + expect(state.projectPath).toEqual(projectPath); }); }); diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js index 921d42a0871..05c6d587e9c 100644 --- a/spec/javascripts/helpers/init_vue_mr_page_helper.js +++ b/spec/javascripts/helpers/init_vue_mr_page_helper.js @@ -6,6 +6,7 @@ import diffFileMockData from '../diffs/mock_data/diff_file'; export default function initVueMRPage() { const diffsAppEndpoint = '/diffs/app/endpoint'; + const diffsAppProjectPath = 'testproject'; const mrEl = document.createElement('div'); mrEl.className = 'merge-request fixture-mr'; mrEl.setAttribute('data-mr-action', 'diffs'); @@ -26,6 +27,7 @@ export default function initVueMRPage() { const diffsAppEl = document.createElement('div'); diffsAppEl.id = 'js-diffs-app'; diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint); + diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath); diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); document.body.appendChild(diffsAppEl); -- cgit v1.2.1 From 65840591cd8bdc81281357d062728be9309c5597 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 20 Jun 2018 10:21:59 +0200 Subject: Gitaly metrics check for read/writeability Prior to this change, health checks checked for writeability of the NFS shards. Given we're moving away from that, this patch extends the checks for Gitaly to check for read and writeability. Potentially some dashboards will break, as over time these metrics will no longer appear as Prometheus doesn't get the data anymore. Observability in the circuit breaker will be reduced, but its not expected to be turned on and the circuit breaker is being removed soon too. Closes https://gitlab.com/gitlab-org/gitaly/issues/1218 --- spec/controllers/health_controller_spec.rb | 4 +- spec/controllers/metrics_controller_spec.rb | 7 + spec/lib/gitaly/server_spec.rb | 34 ++++ .../gitlab/health_checks/fs_shards_check_spec.rb | 200 --------------------- spec/lib/gitlab/health_checks/gitaly_check_spec.rb | 7 +- 5 files changed, 46 insertions(+), 206 deletions(-) delete mode 100644 spec/lib/gitlab/health_checks/fs_shards_check_spec.rb (limited to 'spec') diff --git a/spec/controllers/health_controller_spec.rb b/spec/controllers/health_controller_spec.rb index 542eddc2d16..d800ad7c187 100644 --- a/spec/controllers/health_controller_spec.rb +++ b/spec/controllers/health_controller_spec.rb @@ -69,8 +69,7 @@ describe HealthController do expect(json_response['cache_check']['status']).to eq('ok') expect(json_response['queues_check']['status']).to eq('ok') expect(json_response['shared_state_check']['status']).to eq('ok') - expect(json_response['fs_shards_check']['status']).to eq('ok') - expect(json_response['fs_shards_check']['labels']['shard']).to eq('default') + expect(json_response['gitaly_check']['status']).to eq('ok') end end @@ -122,7 +121,6 @@ describe HealthController do expect(json_response['cache_check']['status']).to eq('ok') expect(json_response['queues_check']['status']).to eq('ok') expect(json_response['shared_state_check']['status']).to eq('ok') - expect(json_response['fs_shards_check']['status']).to eq('ok') end end diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb index 9e8a37171ec..7376841fac8 100644 --- a/spec/controllers/metrics_controller_spec.rb +++ b/spec/controllers/metrics_controller_spec.rb @@ -59,6 +59,13 @@ describe MetricsController do expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/) end + it 'returns Gitaly metrics' do + get :index + + expect(response.body).to match(/^gitaly_health_check_success{shard="default"} 1$/) + expect(response.body).to match(/^gitaly_health_check_latency_seconds{shard="default"} [0-9\.]+$/) + end + context 'prometheus metrics are disabled' do before do allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(false) diff --git a/spec/lib/gitaly/server_spec.rb b/spec/lib/gitaly/server_spec.rb index ed5d56e91d4..09bf21b5946 100644 --- a/spec/lib/gitaly/server_spec.rb +++ b/spec/lib/gitaly/server_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Gitaly::Server do + let(:server) { described_class.new('default') } + describe '.all' do let(:storages) { Gitlab.config.repositories.storages } @@ -17,6 +19,38 @@ describe Gitaly::Server do it { is_expected.to respond_to(:up_to_date?) } it { is_expected.to respond_to(:address) } + describe 'readable?' do + context 'when the storage is readable' do + it 'returns true' do + expect(server).to be_readable + end + end + + context 'when the storage is not readable' do + let(:server) { described_class.new('broken') } + + it 'returns false' do + expect(server).not_to be_readable + end + end + end + + describe 'writeable?' do + context 'when the storage is writeable' do + it 'returns true' do + expect(server).to be_writeable + end + end + + context 'when the storage is not writeable' do + let(:server) { described_class.new('broken') } + + it 'returns false' do + expect(server).not_to be_writeable + end + end + end + describe 'request memoization' do context 'when requesting multiple properties', :request_store do it 'uses memoization for the info request' do diff --git a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb deleted file mode 100644 index 9dcf272d25e..00000000000 --- a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb +++ /dev/null @@ -1,200 +0,0 @@ -require 'spec_helper' - -describe Gitlab::HealthChecks::FsShardsCheck do - def command_exists?(command) - _, status = Gitlab::Popen.popen(%W{ #{command} 1 echo }) - status.zero? - rescue Errno::ENOENT - false - end - - def timeout_command - @timeout_command ||= - if command_exists?('timeout') - 'timeout' - elsif command_exists?('gtimeout') - 'gtimeout' - else - '' - end - end - - let(:metric_class) { Gitlab::HealthChecks::Metric } - let(:result_class) { Gitlab::HealthChecks::Result } - let(:repository_storages) { ['default'] } - let(:tmp_dir) { Dir.mktmpdir } - - let(:storages_paths) do - { - default: Gitlab::GitalyClient::StorageSettings.new('path' => tmp_dir) - }.with_indifferent_access - end - - before do - allow(described_class).to receive(:repository_storages) { repository_storages } - allow(described_class).to receive(:storages_paths) { storages_paths } - stub_const('Gitlab::HealthChecks::FsShardsCheck::TIMEOUT_EXECUTABLE', timeout_command) - end - - after do - FileUtils.remove_entry_secure(tmp_dir) if Dir.exist?(tmp_dir) - end - - shared_examples 'filesystem checks' do - describe '#readiness' do - subject { described_class.readiness } - - context 'storage has a tripped circuitbreaker', :broken_storage do - let(:repository_storages) { ['broken'] } - let(:storages_paths) do - Gitlab.config.repositories.storages - end - - it { is_expected.to include(result_class.new(false, 'circuitbreaker tripped', shard: 'broken')) } - end - - context 'storage points to not existing folder' do - let(:storages_paths) do - { - default: Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/this/path/doesnt/exist') - }.with_indifferent_access - end - - before do - allow(described_class).to receive(:storage_circuitbreaker_test) { true } - end - - it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) } - end - - context 'storage points to directory that has both read and write rights' do - before do - FileUtils.chmod_R(0755, tmp_dir) - end - - it { is_expected.to include(result_class.new(true, nil, shard: 'default')) } - - it 'cleans up files used for testing' do - expect(described_class).to receive(:storage_write_test).with(any_args).and_call_original - - expect { subject }.not_to change(Dir.entries(tmp_dir), :count) - end - - context 'read test fails' do - before do - allow(described_class).to receive(:storage_read_test).with(any_args).and_return(false) - end - - it { is_expected.to include(result_class.new(false, 'cannot read from storage', shard: 'default')) } - end - - context 'write test fails' do - before do - allow(described_class).to receive(:storage_write_test).with(any_args).and_return(false) - end - - it { is_expected.to include(result_class.new(false, 'cannot write to storage', shard: 'default')) } - end - end - end - - describe '#metrics' do - context 'storage points to not existing folder' do - let(:storages_paths) do - { - default: Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/this/path/doesnt/exist') - }.with_indifferent_access - end - - it 'provides metrics' do - metrics = described_class.metrics - - expect(metrics).to all(have_attributes(labels: { shard: 'default' })) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_circuitbreaker_latency_seconds, value: be >= 0)) - end - end - - context 'storage points to directory that has both read and write rights' do - before do - FileUtils.chmod_R(0755, tmp_dir) - end - - it 'provides metrics' do - metrics = described_class.metrics - - expect(metrics).to all(have_attributes(labels: { shard: 'default' })) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 1)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 1)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 1)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_circuitbreaker_latency_seconds, value: be >= 0)) - end - - it 'cleans up files used for metrics' do - expect { described_class.metrics }.not_to change(Dir.entries(tmp_dir), :count) - end - end - end - end - - context 'when timeout kills fs checks' do - before do - stub_const('Gitlab::HealthChecks::FsShardsCheck::COMMAND_TIMEOUT', '1') - - allow(described_class).to receive(:exec_with_timeout).and_wrap_original { |m| m.call(%w(sleep 60)) } - FileUtils.chmod_R(0755, tmp_dir) - end - - describe '#readiness' do - subject { described_class.readiness } - - it { is_expected.to include(result_class.new(false, 'cannot stat storage', shard: 'default')) } - end - - describe '#metrics' do - it 'provides metrics' do - metrics = described_class.metrics - - expect(metrics).to all(have_attributes(labels: { shard: 'default' })) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) - end - end - end - - context 'when popen always finds required binaries' do - before do - allow(described_class).to receive(:exec_with_timeout).and_wrap_original do |method, *args, &block| - begin - method.call(*args, &block) - rescue RuntimeError, Errno::ENOENT - raise 'expected not to happen' - end - end - - stub_const('Gitlab::HealthChecks::FsShardsCheck::COMMAND_TIMEOUT', '10') - end - - it_behaves_like 'filesystem checks' - end - - context 'when popen never finds required binaries' do - before do - allow(Gitlab::Popen).to receive(:popen).and_raise(Errno::ENOENT) - end - - it_behaves_like 'filesystem checks' - end -end diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb index 724beefff69..4912cd48761 100644 --- a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb +++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb @@ -30,13 +30,14 @@ describe Gitlab::HealthChecks::GitalyCheck do describe '#metrics' do subject { described_class.metrics } + let(:server) { double(storage: 'default', read_writeable?: up) } before do - expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check) + allow(Gitaly::Server).to receive(:new).and_return(server) end context 'Gitaly server is up' do - let(:gitaly_check) { double(check: { success: true }) } + let(:up) { true } it 'provides metrics' do expect(subject).to all(have_attributes(labels: { shard: 'default' })) @@ -46,7 +47,7 @@ describe Gitlab::HealthChecks::GitalyCheck do end context 'Gitaly server is down' do - let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) } + let(:up) { false } it 'provides metrics' do expect(subject).to include(an_object_having_attributes(name: 'gitaly_health_check_success', value: 0)) -- cgit v1.2.1 From 351d5f3e36c9bb3bb187d838c68ede33f6f80b14 Mon Sep 17 00:00:00 2001 From: Jan Beckmann Date: Wed, 27 Jun 2018 08:47:29 +0000 Subject: Resolve "Copying ordered list to new comment becomes unordered" --- spec/javascripts/behaviors/copy_as_gfm_spec.js | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/behaviors/copy_as_gfm_spec.js b/spec/javascripts/behaviors/copy_as_gfm_spec.js index efbe09a10a2..c2db81c6ce4 100644 --- a/spec/javascripts/behaviors/copy_as_gfm_spec.js +++ b/spec/javascripts/behaviors/copy_as_gfm_spec.js @@ -44,4 +44,59 @@ describe('CopyAsGFM', () => { callPasteGFM(); }); }); + + describe('CopyAsGFM.copyGFM', () => { + // Stub getSelection to return a purpose-built object. + const stubSelection = (html, parentNode) => ({ + getRangeAt: () => ({ + commonAncestorContainer: { tagName: parentNode }, + cloneContents: () => { + const fragment = document.createDocumentFragment(); + const node = document.createElement('div'); + node.innerHTML = html; + Array.from(node.childNodes).forEach((item) => fragment.appendChild(item)); + return fragment; + }, + }), + rangeCount: 1, + }); + + const clipboardData = { + setData() {}, + }; + + const simulateCopy = () => { + const e = { + originalEvent: { + clipboardData, + }, + preventDefault() {}, + stopPropagation() {}, + }; + CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); + return clipboardData; + }; + + beforeEach(() => spyOn(clipboardData, 'setData')); + + describe('list handling', () => { + it('uses correct gfm for unordered lists', () => { + const selection = stubSelection('

  • List Item1
  • List Item2
  • \n', 'UL'); + spyOn(window, 'getSelection').and.returnValue(selection); + simulateCopy(); + + const expectedGFM = '- List Item1\n- List Item2'; + expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM); + }); + + it('uses correct gfm for ordered lists', () => { + const selection = stubSelection('
  • List Item1
  • List Item2
  • \n', 'OL'); + spyOn(window, 'getSelection').and.returnValue(selection); + simulateCopy(); + + const expectedGFM = '1. List Item1\n1. List Item2'; + expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM); + }); + }); + }); }); -- cgit v1.2.1 From e08ce52a29dd9a8e276f80e4e2d856fb0aa684db Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 15 Jun 2018 09:57:54 +0100 Subject: Improve Web IDE commit form Closes #47307 --- .../ide/stores/modules/commit/getters_spec.js | 48 +--------------------- 1 file changed, 2 insertions(+), 46 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/modules/commit/getters_spec.js b/spec/javascripts/ide/stores/modules/commit/getters_spec.js index 55580f046ad..45b8d321404 100644 --- a/spec/javascripts/ide/stores/modules/commit/getters_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/getters_spec.js @@ -29,46 +29,6 @@ describe('IDE commit module getters', () => { }); }); - describe('commitButtonDisabled', () => { - const localGetters = { - discardDraftButtonDisabled: false, - }; - const rootState = { - stagedFiles: ['a'], - }; - - it('returns false when discardDraftButtonDisabled is false & stagedFiles is not empty', () => { - expect( - getters.commitButtonDisabled(state, localGetters, rootState), - ).toBeFalsy(); - }); - - it('returns true when discardDraftButtonDisabled is false & stagedFiles is empty', () => { - rootState.stagedFiles.length = 0; - - expect( - getters.commitButtonDisabled(state, localGetters, rootState), - ).toBeTruthy(); - }); - - it('returns true when discardDraftButtonDisabled is true', () => { - localGetters.discardDraftButtonDisabled = true; - - expect( - getters.commitButtonDisabled(state, localGetters, rootState), - ).toBeTruthy(); - }); - - it('returns true when discardDraftButtonDisabled is false & changedFiles is not empty', () => { - localGetters.discardDraftButtonDisabled = false; - rootState.stagedFiles.length = 0; - - expect( - getters.commitButtonDisabled(state, localGetters, rootState), - ).toBeTruthy(); - }); - }); - describe('newBranchName', () => { it('includes username, currentBranchId, patch & random number', () => { gon.current_username = 'username'; @@ -108,9 +68,7 @@ describe('IDE commit module getters', () => { }); it('uses newBranchName when not empty', () => { - expect(getters.branchName(state, localGetters, rootState)).toBe( - 'state-newBranchName', - ); + expect(getters.branchName(state, localGetters, rootState)).toBe('state-newBranchName'); }); it('uses getters newBranchName when state newBranchName is empty', () => { @@ -118,9 +76,7 @@ describe('IDE commit module getters', () => { newBranchName: '', }); - expect(getters.branchName(state, localGetters, rootState)).toBe( - 'newBranchName', - ); + expect(getters.branchName(state, localGetters, rootState)).toBe('newBranchName'); }); }); }); -- cgit v1.2.1 From 9b760c4cec3bad049d508d741cdcc6991f90c7c2 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 15 Jun 2018 16:16:32 +0100 Subject: added getter spec for preBuiltCommitMessage --- .../ide/stores/modules/commit/getters_spec.js | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/modules/commit/getters_spec.js b/spec/javascripts/ide/stores/modules/commit/getters_spec.js index 45b8d321404..44c941d6dbb 100644 --- a/spec/javascripts/ide/stores/modules/commit/getters_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/getters_spec.js @@ -81,4 +81,48 @@ describe('IDE commit module getters', () => { }); }); }); + + describe('preBuiltCommitMessage', () => { + let rootState = {}; + + beforeEach(() => { + rootState.changedFiles = []; + rootState.stagedFiles = []; + }); + + afterEach(() => { + rootState = {}; + }); + + it('returns commitMessage when set', () => { + state.commitMessage = 'test commit message'; + + expect(getters.preBuiltCommitMessage(state, null, rootState)).toBe('test commit message'); + }); + + ['changedFiles', 'stagedFiles'].forEach(key => { + it('returns commitMessage with updated file', () => { + rootState[key].push({ + path: 'test-file', + }); + + expect(getters.preBuiltCommitMessage(state, null, rootState)).toBe('Update test-file'); + }); + + it('returns commitMessage with updated files', () => { + rootState[key].push( + { + path: 'test-file', + }, + { + path: 'index.js', + }, + ); + + expect(getters.preBuiltCommitMessage(state, null, rootState)).toBe( + 'Update test-file, index.js files', + ); + }); + }); + }); }); -- cgit v1.2.1 From 433ad2d0f52cbdc6a15b66c9e435afd7b9f29246 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 25 Jun 2018 11:22:46 +0100 Subject: karma fixes --- spec/javascripts/ide/components/commit_sidebar/form_spec.js | 1 + .../ide/components/commit_sidebar/message_field_spec.js | 1 + spec/javascripts/ide/mock_data.js | 1 + spec/javascripts/ide/stores/getters_spec.js | 10 +++------- 4 files changed, 6 insertions(+), 7 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/commit_sidebar/form_spec.js b/spec/javascripts/ide/components/commit_sidebar/form_spec.js index 8b47a365582..0a20b3b088f 100644 --- a/spec/javascripts/ide/components/commit_sidebar/form_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/form_spec.js @@ -16,6 +16,7 @@ describe('IDE commit form', () => { store.state.changedFiles.push('test'); store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = 'master'; Vue.set(store.state.projects, 'abcproject', { ...projectData }); vm = createComponentWithStore(Component, store).$mount(); diff --git a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js b/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js index d62d58101d6..942cc19f46d 100644 --- a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js @@ -13,6 +13,7 @@ describe('IDE commit message field', () => { Component, { text: '', + placeholder: 'testing', }, '#app', ); diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js index dd87a43f370..80bf664d491 100644 --- a/spec/javascripts/ide/mock_data.js +++ b/spec/javascripts/ide/mock_data.js @@ -8,6 +8,7 @@ export const projectData = { branches: { master: { treeId: 'abcproject/master', + can_push: true, }, }, mergeRequests: {}, diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js index 4833ba3edfd..bfac2f1baf7 100644 --- a/spec/javascripts/ide/stores/getters_spec.js +++ b/spec/javascripts/ide/stores/getters_spec.js @@ -146,13 +146,9 @@ describe('IDE store getters', () => { it('returns the last commit of the current branch on the current project', () => { const commitTitle = 'Example commit title'; const localGetters = { - currentProject: { - branches: { - 'example-branch': { - commit: { - title: commitTitle, - }, - }, + currentBranch: { + commit: { + title: commitTitle, }, }, }; -- cgit v1.2.1 From 9dce759f7eb9a0544ddd565c458aaa1ee9fe7923 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 25 Jun 2018 12:05:46 +0100 Subject: :white_check_mark: fixed getters spec --- spec/javascripts/ide/stores/getters_spec.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js index bfac2f1baf7..00317e15dbc 100644 --- a/spec/javascripts/ide/stores/getters_spec.js +++ b/spec/javascripts/ide/stores/getters_spec.js @@ -146,6 +146,9 @@ describe('IDE store getters', () => { it('returns the last commit of the current branch on the current project', () => { const commitTitle = 'Example commit title'; const localGetters = { + currentProject: { + name: 'test-project', + }, currentBranch: { commit: { title: commitTitle, -- cgit v1.2.1 From c1b29823cb51704c89e784edd6e9789fc1f5696d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 25 Jun 2018 14:51:45 +0100 Subject: :white_check_mark: added specs --- .../ide/components/commit_sidebar/form_spec.js | 12 +++++ .../components/commit_sidebar/radio_group_spec.js | 15 ++++++ spec/javascripts/ide/stores/getters_spec.js | 19 ++++++++ spec/javascripts/ide/stores/utils_spec.js | 54 ++++++++++++++++++++++ 4 files changed, 100 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/ide/components/commit_sidebar/form_spec.js b/spec/javascripts/ide/components/commit_sidebar/form_spec.js index 0a20b3b088f..b7a7afe4db4 100644 --- a/spec/javascripts/ide/components/commit_sidebar/form_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/form_spec.js @@ -147,4 +147,16 @@ describe('IDE commit form', () => { }); }); }); + + describe('commitButtonText', () => { + it('returns commit text when staged files exist', () => { + vm.$store.state.stagedFiles.push('testing'); + + expect(vm.commitButtonText).toBe('Commit'); + }); + + it('returns stage & commit text when staged files do not exist', () => { + expect(vm.commitButtonText).toBe('Stage & Commit'); + }); + }); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js b/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js index 21bfe4be52f..ffc2a4c9ddb 100644 --- a/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js @@ -114,4 +114,19 @@ describe('IDE commit sidebar radio group', () => { }); }); }); + + describe('tooltipTitle', () => { + it('returns title when disabled', () => { + vm.title = 'test title'; + vm.disabled = true; + + expect(vm.tooltipTitle).toBe('test title'); + }); + + it('returns blank when not disabled', () => { + vm.title = 'test title'; + + expect(vm.tooltipTitle).not.toBe('test title'); + }); + }); }); diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js index 00317e15dbc..70883e16b0d 100644 --- a/spec/javascripts/ide/stores/getters_spec.js +++ b/spec/javascripts/ide/stores/getters_spec.js @@ -160,4 +160,23 @@ describe('IDE store getters', () => { expect(getters.lastCommit(localState, localGetters).title).toBe(commitTitle); }); }); + + describe('currentBranch', () => { + it('returns current projects branch', () => { + const localGetters = { + currentProject: { + branches: { + master: { + name: 'master', + }, + }, + }, + }; + localState.currentBranchId = 'master'; + + expect(getters.currentBranch(localState, localGetters)).toEqual({ + name: 'master', + }); + }); + }); }); diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js index a7bd443af51..6c5980cfae4 100644 --- a/spec/javascripts/ide/stores/utils_spec.js +++ b/spec/javascripts/ide/stores/utils_spec.js @@ -94,6 +94,7 @@ describe('Multi-file store utils', () => { newBranch: false, state, rootState, + getters: {}, }); expect(payload).toEqual({ @@ -118,5 +119,58 @@ describe('Multi-file store utils', () => { start_branch: undefined, }); }); + + it('uses prebuilt commit message when commit message is empty', () => { + const rootState = { + stagedFiles: [ + { + ...file('staged'), + path: 'staged', + content: 'updated file content', + lastCommitSha: '123456789', + }, + { + ...file('newFile'), + path: 'added', + tempFile: true, + content: 'new file content', + base64: true, + lastCommitSha: '123456789', + }, + ], + currentBranchId: 'master', + }; + const payload = utils.createCommitPayload({ + branch: 'master', + newBranch: false, + state: {}, + rootState, + getters: { + preBuiltCommitMessage: 'prebuilt test commit message', + }, + }); + + expect(payload).toEqual({ + branch: 'master', + commit_message: 'prebuilt test commit message', + actions: [ + { + action: 'update', + file_path: 'staged', + content: 'updated file content', + encoding: 'text', + last_commit_id: '123456789', + }, + { + action: 'create', + file_path: 'added', + content: 'new file content', + encoding: 'base64', + last_commit_id: '123456789', + }, + ], + start_branch: undefined, + }); + }); }); }); -- cgit v1.2.1 From 093e43a01157854f0ef1ce435e7935feba041e27 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 26 Jun 2018 16:51:57 +0100 Subject: :white_check_mark: store specs --- .../javascripts/ide/stores/actions/project_spec.js | 86 +++++++++++++++++++++- spec/javascripts/ide/stores/actions_spec.js | 14 ++++ spec/javascripts/ide/stores/mutations_spec.js | 8 ++ 3 files changed, 107 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js index d71fc0e035e..5529fe44ce5 100644 --- a/spec/javascripts/ide/stores/actions/project_spec.js +++ b/spec/javascripts/ide/stores/actions/project_spec.js @@ -1,6 +1,12 @@ -import { refreshLastCommitData } from '~/ide/stores/actions'; +import { + refreshLastCommitData, + showBranchNotFoundError, + createNewBranchFromDefault, +} from '~/ide/stores/actions'; import store from '~/ide/stores'; +import projectActions from '~/ide/stores/actions/project'; import service from '~/ide/services'; +import api from '~/api'; import { resetStore } from '../../helpers'; import testAction from '../../../helpers/vuex_action_helper'; @@ -80,4 +86,82 @@ describe('IDE store project actions', () => { ); }); }); + + describe('showBranchNotFoundError', () => { + it('dispatches setErrorMessage', done => { + testAction( + showBranchNotFoundError, + 'master', + null, + [], + [ + { + type: 'setErrorMessage', + payload: { + text: "Branch master was not found in this project's repository.", + action: 'createNewBranchFromDefault', + actionText: 'Create branch', + actionPayload: 'master', + }, + }, + ], + done, + ); + }); + }); + + describe('createNewBranchFromDefault', () => { + it('calls API', done => { + spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); + spyOnDependency(projectActions, 'refreshCurrentPage'); + + createNewBranchFromDefault( + { + state: { + currentProjectId: 'project-path', + }, + getters: { + currentProject: { + default_branch: 'master', + }, + }, + }, + 'new-branch-name', + ); + + setTimeout(() => { + expect(api.createBranch).toHaveBeenCalledWith('project-path', { + ref: 'master', + branch: 'new-branch-name', + }); + + done(); + }); + }); + + it('reloads window', done => { + spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); + const refreshSpy = spyOnDependency(projectActions, 'refreshCurrentPage'); + + createNewBranchFromDefault( + { + state: { + currentProjectId: 'project-path', + }, + getters: { + currentProject: { + default_branch: 'master', + }, + }, + }, + 'new-branch-name', + ); + + setTimeout(() => { + expect(refreshSpy).toHaveBeenCalled(); + + done(); + }); + }); + }); }); diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js index 062c3497623..8b665a6d79e 100644 --- a/spec/javascripts/ide/stores/actions_spec.js +++ b/spec/javascripts/ide/stores/actions_spec.js @@ -6,6 +6,7 @@ import actions, { setEmptyStateSvgs, updateActivityBarView, updateTempFlagForEntry, + setErrorMessage, } from '~/ide/stores/actions'; import store from '~/ide/stores'; import * as types from '~/ide/stores/mutation_types'; @@ -443,4 +444,17 @@ describe('Multi-file store actions', () => { ); }); }); + + describe('setErrorMessage', () => { + it('commis error messsage', done => { + testAction( + setErrorMessage, + 'error', + null, + [{ type: types.SET_ERROR_MESSAGE, payload: 'error' }], + [], + done, + ); + }); + }); }); diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js index 972713c5ad2..98016f593aa 100644 --- a/spec/javascripts/ide/stores/mutations_spec.js +++ b/spec/javascripts/ide/stores/mutations_spec.js @@ -148,4 +148,12 @@ describe('Multi-file store mutations', () => { expect(localState.unusedSeal).toBe(false); }); }); + + describe('SET_ERROR_MESSAGE', () => { + it('updates error message', () => { + mutations.SET_ERROR_MESSAGE(localState, 'error'); + + expect(localState.errorMessage).toBe('error'); + }); + }); }); -- cgit v1.2.1 From 5c483fd8656805b39b451fcd25c1efbd112d3cae Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 27 Jun 2018 10:43:26 +0100 Subject: specs --- spec/javascripts/api_spec.js | 25 +++++ .../ide/components/error_message_spec.js | 104 +++++++++++++++++++ spec/javascripts/ide/components/ide_spec.js | 14 +++ .../javascripts/ide/stores/actions/project_spec.js | 43 +++++++- spec/javascripts/ide/stores/actions/tree_spec.js | 115 ++++++++++++++------- 5 files changed, 262 insertions(+), 39 deletions(-) create mode 100644 spec/javascripts/ide/components/error_message_spec.js (limited to 'spec') diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js index e8435116221..c53b6da4b48 100644 --- a/spec/javascripts/api_spec.js +++ b/spec/javascripts/api_spec.js @@ -362,4 +362,29 @@ describe('Api', () => { .catch(done.fail); }); }); + + describe('createBranch', () => { + it('creates new branch', done => { + const ref = 'master'; + const branch = 'new-branch-name'; + const dummyProjectPath = 'gitlab-org/gitlab-ce'; + const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${encodeURIComponent( + dummyProjectPath, + )}/repository/branches`; + + spyOn(axios, 'post').and.callThrough(); + + mock.onPost(expectedUrl).replyOnce(200, { + name: branch, + }); + + Api.createBranch(dummyProjectPath, { ref, branch }) + .then(({ data }) => { + expect(data.name).toBe(branch); + expect(axios.post).toHaveBeenCalledWith(expectedUrl, { ref, branch }); + }) + .then(done) + .catch(done.fail); + }); + }); }); diff --git a/spec/javascripts/ide/components/error_message_spec.js b/spec/javascripts/ide/components/error_message_spec.js new file mode 100644 index 00000000000..70d13a6ceb2 --- /dev/null +++ b/spec/javascripts/ide/components/error_message_spec.js @@ -0,0 +1,104 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import ErrorMessage from '~/ide/components/error_message.vue'; +import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; +import { resetStore } from '../helpers'; + +describe('IDE error message component', () => { + const Component = Vue.extend(ErrorMessage); + let vm; + + beforeEach(() => { + vm = createComponentWithStore(Component, store, { + message: { + text: 'error message', + action: null, + actionText: null, + }, + }).$mount(); + }); + + afterEach(() => { + vm.$destroy(); + resetStore(vm.$store); + }); + + it('renders error message', () => { + expect(vm.$el.textContent).toContain('error message'); + }); + + it('clears error message on click', () => { + spyOn(vm, 'setErrorMessage'); + + vm.$el.click(); + + expect(vm.setErrorMessage).toHaveBeenCalledWith(null); + }); + + describe('with action', () => { + beforeEach(done => { + vm.message.action = 'testAction'; + vm.message.actionText = 'test action'; + vm.message.actionPayload = 'testActionPayload'; + + spyOn(vm.$store, 'dispatch').and.returnValue(Promise.resolve()); + + vm.$nextTick(done); + }); + + it('renders action button', () => { + expect(vm.$el.querySelector('.flash-action')).not.toBe(null); + expect(vm.$el.textContent).toContain('test action'); + }); + + it('does not clear error message on click', () => { + spyOn(vm, 'setErrorMessage'); + + vm.$el.click(); + + expect(vm.setErrorMessage).not.toHaveBeenCalled(); + }); + + it('dispatches action', done => { + vm.$el.querySelector('.flash-action').click(); + + vm.$nextTick(() => { + expect(vm.$store.dispatch).toHaveBeenCalledWith('testAction', 'testActionPayload'); + + done(); + }); + }); + + it('does not dispatch action when already loading', () => { + vm.isLoading = true; + + vm.$el.querySelector('.flash-action').click(); + + expect(vm.$store.dispatch).not.toHaveBeenCalledWith(); + }); + + it('resets isLoading after click', done => { + vm.$el.querySelector('.flash-action').click(); + + expect(vm.isLoading).toBe(true); + + vm.$nextTick(() => { + expect(vm.isLoading).toBe(false); + + done(); + }); + }); + + it('shows loading icon when isLoading is true', done => { + expect(vm.$el.querySelector('.loading-container').style.display).not.toBe(''); + + vm.isLoading = true; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.loading-container').style.display).toBe(''); + + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js index 045a60e56a0..708c9fe69af 100644 --- a/spec/javascripts/ide/components/ide_spec.js +++ b/spec/javascripts/ide/components/ide_spec.js @@ -114,4 +114,18 @@ describe('ide component', () => { expect(vm.mousetrapStopCallback(null, document.querySelector('.inputarea'), 't')).toBe(true); }); }); + + it('shows error message when set', done => { + expect(vm.$el.querySelector('.flash-container')).toBe(null); + + vm.$store.state.errorMessage = { + text: 'error', + }; + + vm.$nextTick(() => { + expect(vm.$el.querySelector('.flash-container')).not.toBe(null); + + done(); + }); + }); }); diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js index 5529fe44ce5..ab8bca52093 100644 --- a/spec/javascripts/ide/stores/actions/project_spec.js +++ b/spec/javascripts/ide/stores/actions/project_spec.js @@ -1,7 +1,10 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import { refreshLastCommitData, showBranchNotFoundError, createNewBranchFromDefault, + getBranchData, } from '~/ide/stores/actions'; import store from '~/ide/stores'; import projectActions from '~/ide/stores/actions/project'; @@ -11,11 +14,19 @@ import { resetStore } from '../../helpers'; import testAction from '../../../helpers/vuex_action_helper'; describe('IDE store project actions', () => { + let mock; + beforeEach(() => { - store.state.projects['abc/def'] = {}; + mock = new MockAdapter(axios); + + store.state.projects['abc/def'] = { + branches: {}, + }; }); afterEach(() => { + mock.restore(); + resetStore(store); }); @@ -164,4 +175,34 @@ describe('IDE store project actions', () => { }); }); }); + + describe('getBranchData', () => { + describe('error', () => { + it('dispatches branch not found action when response is 404', done => { + const dispatch = jasmine.createSpy('dispatchSpy'); + + mock.onGet(/(.*)/).replyOnce(404); + + getBranchData( + { + commit() {}, + dispatch, + state: store.state, + }, + { + projectId: 'abc/def', + branchId: 'master-testing', + }, + ) + .then(done.fail) + .catch(() => { + expect(dispatch.calls.argsFor(0)).toEqual([ + 'showBranchNotFoundError', + 'master-testing', + ]); + done(); + }); + }); + }); + }); }); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index cefed9ddb43..612a439cfbf 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -1,7 +1,9 @@ +import MockAdapter from 'axios-mock-adapter'; import Vue from 'vue'; import testAction from 'spec/helpers/vuex_action_helper'; -import { showTreeEntry } from '~/ide/stores/actions/tree'; +import { showTreeEntry, getFiles } from '~/ide/stores/actions/tree'; import * as types from '~/ide/stores/mutation_types'; +import axios from '~/lib/utils/axios_utils'; import store from '~/ide/stores'; import service from '~/ide/services'; import router from '~/ide/ide_router'; @@ -9,6 +11,7 @@ import { file, resetStore, createEntriesFromPaths } from '../../helpers'; describe('Multi-file store tree actions', () => { let projectTree; + let mock; const basicCallParameters = { endpoint: 'rootEndpoint', @@ -20,6 +23,8 @@ describe('Multi-file store tree actions', () => { beforeEach(() => { spyOn(router, 'push'); + mock = new MockAdapter(axios); + store.state.currentProjectId = 'abcproject'; store.state.currentBranchId = 'master'; store.state.projects.abcproject = { @@ -33,49 +38,85 @@ describe('Multi-file store tree actions', () => { }); afterEach(() => { + mock.restore(); resetStore(store); }); describe('getFiles', () => { - beforeEach(() => { - spyOn(service, 'getFiles').and.returnValue( - Promise.resolve({ - json: () => - Promise.resolve([ - 'file.txt', - 'folder/fileinfolder.js', - 'folder/subfolder/fileinsubfolder.js', - ]), - }), - ); + describe('success', () => { + beforeEach(() => { + spyOn(service, 'getFiles').and.callThrough(); + + mock + .onGet(/(.*)/) + .replyOnce(200, [ + 'file.txt', + 'folder/fileinfolder.js', + 'folder/subfolder/fileinsubfolder.js', + ]); + }); + + it('calls service getFiles', done => { + store + .dispatch('getFiles', basicCallParameters) + .then(() => { + expect(service.getFiles).toHaveBeenCalledWith('', 'master'); + + done(); + }) + .catch(done.fail); + }); + + it('adds data into tree', done => { + store + .dispatch('getFiles', basicCallParameters) + .then(() => { + projectTree = store.state.trees['abcproject/master']; + expect(projectTree.tree.length).toBe(2); + expect(projectTree.tree[0].type).toBe('tree'); + expect(projectTree.tree[0].tree[1].name).toBe('fileinfolder.js'); + expect(projectTree.tree[1].type).toBe('blob'); + expect(projectTree.tree[0].tree[0].tree[0].type).toBe('blob'); + expect(projectTree.tree[0].tree[0].tree[0].name).toBe('fileinsubfolder.js'); + + done(); + }) + .catch(done.fail); + }); }); - it('calls service getFiles', done => { - store - .dispatch('getFiles', basicCallParameters) - .then(() => { - expect(service.getFiles).toHaveBeenCalledWith('', 'master'); + describe('error', () => { + it('dispatches branch not found actions when response is 404', done => { + const dispatch = jasmine.createSpy('dispatchSpy'); - done(); - }) - .catch(done.fail); - }); + store.state.projects = { + 'abc/def': { + web_url: `${gl.TEST_HOST}/files`, + }, + }; - it('adds data into tree', done => { - store - .dispatch('getFiles', basicCallParameters) - .then(() => { - projectTree = store.state.trees['abcproject/master']; - expect(projectTree.tree.length).toBe(2); - expect(projectTree.tree[0].type).toBe('tree'); - expect(projectTree.tree[0].tree[1].name).toBe('fileinfolder.js'); - expect(projectTree.tree[1].type).toBe('blob'); - expect(projectTree.tree[0].tree[0].tree[0].type).toBe('blob'); - expect(projectTree.tree[0].tree[0].tree[0].name).toBe('fileinsubfolder.js'); + mock.onGet(/(.*)/).replyOnce(404); - done(); - }) - .catch(done.fail); + getFiles( + { + commit() {}, + dispatch, + state: store.state, + }, + { + projectId: 'abc/def', + branchId: 'master-testing', + }, + ) + .then(done.fail) + .catch(() => { + expect(dispatch.calls.argsFor(0)).toEqual([ + 'showBranchNotFoundError', + 'master-testing', + ]); + done(); + }); + }); }); }); @@ -122,9 +163,7 @@ describe('Multi-file store tree actions', () => { { type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }, { type: types.SET_TREE_OPEN, payload: 'grandparent' }, ], - [ - { type: 'showTreeEntry' }, - ], + [{ type: 'showTreeEntry' }], done, ); }); -- cgit v1.2.1 From 4eebd231b8596e9e7cc69d9fc7b5b86d5a3ca036 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 27 Jun 2018 11:53:51 +0200 Subject: update code based on feedback --- .../group_project_object_builder_spec.rb | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb index 5ca353d612a..6a803c48b34 100644 --- a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb +++ b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb @@ -15,18 +15,18 @@ describe Gitlab::ImportExport::GroupProjectObjectBuilder do group_label = create(:group_label, 'name': 'group label', 'group': project.group) expect(described_class.build(Label, - title: 'group label', - project: project, - group: project.group)).to eq(group_label) + 'title' => 'group label', + 'project' => project, + 'group' => project.group)).to eq(group_label) end - it 'initializes a new label' do + it 'creates a new label' do label = described_class.build(Label, - title: 'group label', - project: project, - group: project.group) + 'title' => 'group label', + 'project' => project, + 'group' => project.group) - expect(label.persisted?).to be false + expect(label.persisted?).to be true end end @@ -35,16 +35,16 @@ describe Gitlab::ImportExport::GroupProjectObjectBuilder do milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group) expect(described_class.build(Milestone, - title: 'group milestone', - project: project, - group: project.group)).to eq(milestone) + 'title' => 'group milestone', + 'project' => project, + 'group' => project.group)).to eq(milestone) end it 'creates a new milestone' do milestone = described_class.build(Milestone, - title: 'group milestone', - project: project, - group: project.group) + 'title' => 'group milestone', + 'project' => project, + 'group' => project.group) expect(milestone.persisted?).to be true end -- cgit v1.2.1 From c2939c714d305699b1002cc572fa21fd5f607fa7 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Wed, 27 Jun 2018 16:31:56 +0530 Subject: Fix merge requests data source key to be camelCase --- spec/helpers/application_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 593b2ca1825..14297a1a544 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -157,7 +157,7 @@ describe ApplicationHelper do let(:noteable_type) { Issue } it 'returns paths for autocomplete_sources_controller' do sources = helper.autocomplete_data_sources(project, noteable_type) - expect(sources.keys).to match_array([:members, :issues, :merge_requests, :labels, :milestones, :commands]) + expect(sources.keys).to match_array([:members, :issues, :mergeRequests, :labels, :milestones, :commands]) sources.keys.each do |key| expect(sources[key]).not_to be_nil end -- cgit v1.2.1 From c3de6a86734f8756de214cc87ac230820fa33acc Mon Sep 17 00:00:00 2001 From: Aram Visser Date: Sun, 24 Jun 2018 13:10:15 +0700 Subject: Add transfer project endpoint to the Projects API --- spec/requests/api/projects_spec.rb | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'spec') diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 99103039f77..abf9ad738bd 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1990,6 +1990,38 @@ describe API::Projects do end end + describe 'PUT /projects/:id/transfer' do + context 'when authenticated as owner' do + let(:group) { create :group } + + it 'transfers the project to the new namespace' do + group.add_owner(user) + + put api("/projects/#{project.id}/transfer", user), namespace: group.id + + expect(response).to have_gitlab_http_status(200) + end + + it 'fails when transferring to a non owned namespace' do + put api("/projects/#{project.id}/transfer", user), namespace: group.id + + expect(response).to have_gitlab_http_status(404) + end + + it 'fails when transferring to an unknown namespace' do + put api("/projects/#{project.id}/transfer", user), namespace: 'unknown' + + expect(response).to have_gitlab_http_status(404) + end + + it 'fails on missing namespace' do + put api("/projects/#{project.id}/transfer", user) + + expect(response).to have_gitlab_http_status(400) + end + end + end + it_behaves_like 'custom attributes endpoints', 'projects' do let(:attributable) { project } let(:other_attributable) { project2 } -- cgit v1.2.1 From 4878ef2fa7176d7d82da66eef0c6ce185f91441a Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 27 Jun 2018 11:41:16 +0000 Subject: CE port gitlab-ee!6112 --- spec/features/protected_branches_spec.rb | 47 +++++++++------------- .../protected_branches_access_control_ce.rb | 36 +++++++++++++++++ 2 files changed, 56 insertions(+), 27 deletions(-) (limited to 'spec') diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 4c0f9971425..39bd4af6cd0 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -60,33 +60,6 @@ feature 'Protected Branches', :js do expect(page).to have_content('No branches to show') end end - - describe "Saved defaults" do - it "keeps the allowed to merge and push dropdowns defaults based on the previous selection" do - visit project_protected_branches_path(project) - form = '.js-new-protected-branch' - - within form do - find(".js-allowed-to-merge").click - wait_for_requests - click_link 'No one' - find(".js-allowed-to-push").click - wait_for_requests - click_link 'Developers + Maintainers' - end - - visit project_protected_branches_path(project) - - within form do - page.within(".js-allowed-to-merge") do - expect(page.find(".dropdown-toggle-text")).to have_content("No one") - end - page.within(".js-allowed-to-push") do - expect(page.find(".dropdown-toggle-text")).to have_content("Developers + Maintainers") - end - end - end - end end context 'logged in as admin' do @@ -97,6 +70,7 @@ feature 'Protected Branches', :js do describe "explicit protected branches" do it "allows creating explicit protected branches" do visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -110,6 +84,7 @@ feature 'Protected Branches', :js do project.repository.add_branch(admin, 'some-branch', commit.id) visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -118,6 +93,7 @@ feature 'Protected Branches', :js do it "displays an error message if the named branch does not exist" do visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('some-branch') click_on "Protect" @@ -128,6 +104,7 @@ feature 'Protected Branches', :js do describe "wildcard protected branches" do it "allows creating protected branches with a wildcard" do visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -141,6 +118,7 @@ feature 'Protected Branches', :js do project.repository.add_branch(admin, 'staging-stable', 'master') visit project_protected_branches_path(project) + set_defaults set_protected_branch_name('*-stable') click_on "Protect" @@ -157,6 +135,7 @@ feature 'Protected Branches', :js do visit project_protected_branches_path(project) set_protected_branch_name('*-stable') + set_defaults click_on "Protect" visit project_protected_branches_path(project) @@ -180,4 +159,18 @@ feature 'Protected Branches', :js do find(".dropdown-input-field").set(branch_name) click_on("Create wildcard #{branch_name}") end + + def set_defaults + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + end end diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb index 5241c0fa6f1..a8f2c2e7a5a 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb @@ -5,6 +5,12 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + within('.js-new-protected-branch') do allowed_to_push_button = find(".js-allowed-to-push") @@ -25,6 +31,18 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + click_on "Protect" expect(ProtectedBranch.count).to eq(1) @@ -59,6 +77,12 @@ shared_examples "protected branches > access control > CE" do end end + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + click_on "Protect" expect(ProtectedBranch.count).to eq(1) @@ -70,6 +94,18 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') + find(".js-allowed-to-merge").click + within('.qa-allowed-to-merge-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + + find(".js-allowed-to-push").click + within('.qa-allowed-to-push-dropdown') do + expect(first("li")).to have_content("Roles") + find(:link, 'No one').click + end + click_on "Protect" expect(ProtectedBranch.count).to eq(1) -- cgit v1.2.1 From be5e6d586f48de0a57d7eed702214d2f428ece71 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 27 Jun 2018 13:46:51 +0200 Subject: Make search and message RPC's mandatory --- spec/models/repository_spec.rb | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'spec') diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c7e751130d8..27a14ff5d5b 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -664,7 +664,7 @@ describe Repository do end end - shared_examples "search_files_by_content" do + describe "search_files_by_content" do let(:results) { repository.search_files_by_content('feature', 'master') } subject { results } @@ -711,7 +711,7 @@ describe Repository do end end - shared_examples "search_files_by_name" do + describe "search_files_by_name" do let(:results) { repository.search_files_by_name('files', 'master') } it 'returns result' do @@ -751,16 +751,6 @@ describe Repository do end end - describe 'with gitaly enabled' do - it_behaves_like 'search_files_by_content' - it_behaves_like 'search_files_by_name' - end - - describe 'with gitaly disabled', :disable_gitaly do - it_behaves_like 'search_files_by_content' - it_behaves_like 'search_files_by_name' - end - describe '#async_remove_remote' do before do masterrev = repository.find_branch('master').dereferenced_target -- cgit v1.2.1 From f4d68347866a9f2def248864be40f2249713b351 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 27 Jun 2018 14:29:03 +0200 Subject: Make Gitaly signature RPC's mandatory --- spec/lib/gitlab/git/commit_spec.rb | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index ae69a362dda..0d7c930127c 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -309,7 +309,7 @@ describe Gitlab::Git::Commit, seed_helper: true do it { is_expected.not_to include(SeedRepo::FirstCommit::ID) } end - shared_examples '.shas_with_signatures' do + describe '.shas_with_signatures' do let(:signed_shas) { %w[5937ac0a7beb003549fc5fd26fc247adbce4a52e 570e7b2abdd848b95f2f578043fc23bd6f6fd24d] } let(:unsigned_shas) { %w[19e2e9b4ef76b422ce1154af39a91323ccc57434 c642fe9b8b9f28f9225d7ea953fe14e74748d53b] } let(:first_signed_shas) { %w[5937ac0a7beb003549fc5fd26fc247adbce4a52e c642fe9b8b9f28f9225d7ea953fe14e74748d53b] } @@ -330,14 +330,6 @@ describe Gitlab::Git::Commit, seed_helper: true do end end - describe '.shas_with_signatures with gitaly on' do - it_should_behave_like '.shas_with_signatures' - end - - describe '.shas_with_signatures with gitaly disabled', :disable_gitaly do - it_should_behave_like '.shas_with_signatures' - end - describe '.find_all' do shared_examples 'finding all commits' do it 'should return a return a collection of commits' do @@ -498,7 +490,7 @@ describe Gitlab::Git::Commit, seed_helper: true do end describe '.extract_signature_lazily' do - shared_examples 'loading signatures in batch once' do + describe 'loading signatures in batch once' do it 'fetches signatures in batch once' do commit_ids = %w[0b4bc9a49b562e85de7cc9e834518ea6828729b9 4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6] signatures = commit_ids.map do |commit_id| @@ -516,27 +508,13 @@ describe Gitlab::Git::Commit, seed_helper: true do subject { described_class.extract_signature_lazily(repository, commit_id).itself } - context 'with Gitaly extract_commit_signature_in_batch feature enabled' do - it_behaves_like 'extracting commit signature' - it_behaves_like 'loading signatures in batch once' - end - - context 'with Gitaly extract_commit_signature_in_batch feature disabled', :disable_gitaly do - it_behaves_like 'extracting commit signature' - it_behaves_like 'loading signatures in batch once' - end + it_behaves_like 'extracting commit signature' end describe '.extract_signature' do subject { described_class.extract_signature(repository, commit_id) } - context 'with gitaly' do - it_behaves_like 'extracting commit signature' - end - - context 'without gitaly', :disable_gitaly do - it_behaves_like 'extracting commit signature' - end + it_behaves_like 'extracting commit signature' end end -- cgit v1.2.1 From dba2d6ee7f94c5657903fb20e9ef4fdac667df74 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Wed, 27 Jun 2018 12:04:54 -0300 Subject: Mark MR as merged regardless of errors when closing issues We should mark the MR as merged as first thing on PostMergeService as in practice it is already merged on the repository. Happens that errors may happen when executing external services such as Issues::CloseService, and we do not want a MR hanging as opened because of that. --- spec/services/merge_requests/post_merge_service_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'spec') diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb index 790ecce8ded..46e4e3559dc 100644 --- a/spec/services/merge_requests/post_merge_service_spec.rb +++ b/spec/services/merge_requests/post_merge_service_spec.rb @@ -47,5 +47,18 @@ describe MergeRequests::PostMergeService do expect(diff_removal_service).to have_received(:execute) end + + it 'marks MR as merged regardless of errors when closing issues' do + merge_request.update(target_branch: 'foo') + allow(project).to receive(:default_branch).and_return('foo') + + issue = create(:issue, project: project) + allow(merge_request).to receive(:closes_issues).and_return([issue]) + allow_any_instance_of(Issues::CloseService).to receive(:execute).with(issue, commit: merge_request).and_raise + + expect { described_class.new(project, user, {}).execute(merge_request) }.to raise_error + + expect(merge_request.reload).to be_merged + end end end -- cgit v1.2.1 From 5d5579b3f5e7394ee7158a5cc462980b9a966b63 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 27 Jun 2018 16:15:16 +0100 Subject: don't reload the full page instead it just pushes a router & re-fetches what it needs --- .../javascripts/ide/stores/actions/project_spec.js | 62 +++++++++++++++------- 1 file changed, 44 insertions(+), 18 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js index ab8bca52093..c1f9fc4f148 100644 --- a/spec/javascripts/ide/stores/actions/project_spec.js +++ b/spec/javascripts/ide/stores/actions/project_spec.js @@ -7,9 +7,9 @@ import { getBranchData, } from '~/ide/stores/actions'; import store from '~/ide/stores'; -import projectActions from '~/ide/stores/actions/project'; import service from '~/ide/services'; import api from '~/api'; +import router from '~/ide/ide_router'; import { resetStore } from '../../helpers'; import testAction from '../../../helpers/vuex_action_helper'; @@ -124,7 +124,7 @@ describe('IDE store project actions', () => { describe('createNewBranchFromDefault', () => { it('calls API', done => { spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); - spyOnDependency(projectActions, 'refreshCurrentPage'); + spyOn(router, 'push'); createNewBranchFromDefault( { @@ -136,23 +136,49 @@ describe('IDE store project actions', () => { default_branch: 'master', }, }, + dispatch() {}, }, 'new-branch-name', - ); + ) + .then(() => { + expect(api.createBranch).toHaveBeenCalledWith('project-path', { + ref: 'master', + branch: 'new-branch-name', + }); + }) + .then(done) + .catch(done.fail); + }); - setTimeout(() => { - expect(api.createBranch).toHaveBeenCalledWith('project-path', { - ref: 'master', - branch: 'new-branch-name', - }); + it('clears error message', done => { + const dispatchSpy = jasmine.createSpy('dispatch'); + spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); + spyOn(router, 'push'); - done(); - }); + createNewBranchFromDefault( + { + state: { + currentProjectId: 'project-path', + }, + getters: { + currentProject: { + default_branch: 'master', + }, + }, + dispatch: dispatchSpy, + }, + 'new-branch-name', + ) + .then(() => { + expect(dispatchSpy).toHaveBeenCalledWith('setErrorMessage', null); + }) + .then(done) + .catch(done.fail); }); it('reloads window', done => { spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); - const refreshSpy = spyOnDependency(projectActions, 'refreshCurrentPage'); + spyOn(router, 'push'); createNewBranchFromDefault( { @@ -164,15 +190,15 @@ describe('IDE store project actions', () => { default_branch: 'master', }, }, + dispatch() {}, }, 'new-branch-name', - ); - - setTimeout(() => { - expect(refreshSpy).toHaveBeenCalled(); - - done(); - }); + ) + .then(() => { + expect(router.push).toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); }); }); -- cgit v1.2.1 From 6a3f9ceff009c2bb93c005e9bfbf641aa45c1cb3 Mon Sep 17 00:00:00 2001 From: Chantal Rollison Date: Wed, 20 Jun 2018 07:41:23 -0700 Subject: Keep label on issue when moving --- spec/services/issues/move_service_spec.rb | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index a9aee9e100f..609eef76d2c 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -5,8 +5,11 @@ describe Issues::MoveService do let(:author) { create(:user) } let(:title) { 'Some issue' } let(:description) { 'Some issue description' } - let(:old_project) { create(:project) } - let(:new_project) { create(:project) } + let(:group) { create(:group, :private) } + let(:sub_group_1) { create(:group, :private, parent: group) } + let(:sub_group_2) { create(:group, :private, parent: group) } + let(:old_project) { create(:project, namespace: sub_group_1) } + let(:new_project) { create(:project, namespace: sub_group_2) } let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') } let(:old_issue) do @@ -14,7 +17,7 @@ describe Issues::MoveService do project: old_project, author: author, milestone: milestone1) end - let(:move_service) do + subject(:move_service) do described_class.new(old_project, user) end @@ -102,6 +105,23 @@ describe Issues::MoveService do end end + context 'issue with group labels', :nested_groups do + it 'assigns group labels to new issue' do + label = create(:group_label, group: group) + label_issue = create(:labeled_issue, description: description, project: old_project, + milestone: milestone1, labels: [label]) + old_project.add_reporter(user) + new_project.add_reporter(user) + + new_issue = move_service.execute(label_issue, new_project) + + expect(new_issue).to have_attributes( + project: new_project, + labels: include(label) + ) + end + end + context 'generic issue' do include_context 'issue move executed' -- cgit v1.2.1 From c7d671a97d414b4679525e3cd9c177e31178754b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 27 Jun 2018 15:10:50 +0100 Subject: Uses tooltip vue directive in issue card component --- spec/javascripts/boards/issue_card_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index 72e961c8a10..7a32e84bced 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -255,7 +255,7 @@ describe('Issue card component', () => { it('renders label', () => { const nodes = []; component.$el.querySelectorAll('.badge').forEach((label) => { - nodes.push(label.title); + nodes.push(label.getAttribute('data-original-title')); }); expect( @@ -265,7 +265,7 @@ describe('Issue card component', () => { it('sets label description as title', () => { expect( - component.$el.querySelector('.badge').getAttribute('title'), + component.$el.querySelector('.badge').getAttribute('data-original-title'), ).toContain(label1.description); }); -- cgit v1.2.1 From 1142e2c32eb0c4f4040c13d40d73fb8f84f35628 Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Wed, 27 Jun 2018 16:20:03 +0000 Subject: Migrate storage nesting check to Gitaly --- spec/initializers/6_validations_spec.rb | 43 --------------------------------- 1 file changed, 43 deletions(-) (limited to 'spec') diff --git a/spec/initializers/6_validations_spec.rb b/spec/initializers/6_validations_spec.rb index 8d9dc092547..f96e5a2133f 100644 --- a/spec/initializers/6_validations_spec.rb +++ b/spec/initializers/6_validations_spec.rb @@ -44,49 +44,6 @@ describe '6_validations' do end end - describe 'validate_storages_paths' do - context 'with correct settings' do - before do - mock_storages('foo' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/paths/a/b/c'), 'bar' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/paths/a/b/d')) - end - - it 'passes through' do - expect { validate_storages_paths }.not_to raise_error - end - end - - context 'with nested storage paths' do - before do - mock_storages('foo' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/paths/a/b/c'), 'bar' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/paths/a/b/c/d')) - end - - it 'throws an error' do - expect { validate_storages_paths }.to raise_error('bar is a nested path of foo. Nested paths are not supported for repository storages. Please fix this in your gitlab.yml before starting GitLab.') - end - end - - context 'with similar but un-nested storage paths' do - before do - mock_storages('foo' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/paths/a/b/c'), 'bar' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/paths/a/b/c2')) - end - - it 'passes through' do - expect { validate_storages_paths }.not_to raise_error - end - end - - describe 'inaccessible storage' do - before do - mock_storages('foo' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/a/path/that/does/not/exist')) - end - - it 'passes through with a warning' do - expect(Rails.logger).to receive(:error) - expect { validate_storages_paths }.not_to raise_error - end - end - end - def mock_storages(storages) allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) end -- cgit v1.2.1 From f0eaf225538b40db5d0b2a6110378b65a240eac8 Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 27 Jun 2018 18:25:25 +0200 Subject: Rails5 fix MySQL milliseconds problem in specs --- spec/lib/gitlab/data_builder/note_spec.rb | 8 ++++---- .../gitlab/github_import/importer/pull_requests_importer_spec.rb | 2 +- spec/models/project_spec.rb | 4 ++-- spec/services/keys/last_used_service_spec.rb | 4 ++-- spec/services/update_merge_request_metrics_service_spec.rb | 4 ++-- spec/support/shared_examples/throttled_touch.rb | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb index 4f8412108ba..b236c1a9c49 100644 --- a/spec/lib/gitlab/data_builder/note_spec.rb +++ b/spec/lib/gitlab/data_builder/note_spec.rb @@ -52,7 +52,7 @@ describe Gitlab::DataBuilder::Note do expect(data[:issue].except('updated_at')) .to eq(issue.reload.hook_attrs.except('updated_at')) expect(data[:issue]['updated_at']) - .to be > issue.hook_attrs['updated_at'] + .to be >= issue.hook_attrs['updated_at'] end context 'with confidential issue' do @@ -84,7 +84,7 @@ describe Gitlab::DataBuilder::Note do expect(data[:merge_request].except('updated_at')) .to eq(merge_request.reload.hook_attrs.except('updated_at')) expect(data[:merge_request]['updated_at']) - .to be > merge_request.hook_attrs['updated_at'] + .to be >= merge_request.hook_attrs['updated_at'] end include_examples 'project hook data' @@ -107,7 +107,7 @@ describe Gitlab::DataBuilder::Note do expect(data[:merge_request].except('updated_at')) .to eq(merge_request.reload.hook_attrs.except('updated_at')) expect(data[:merge_request]['updated_at']) - .to be > merge_request.hook_attrs['updated_at'] + .to be >= merge_request.hook_attrs['updated_at'] end include_examples 'project hook data' @@ -130,7 +130,7 @@ describe Gitlab::DataBuilder::Note do expect(data[:snippet].except('updated_at')) .to eq(snippet.reload.hook_attrs.except('updated_at')) expect(data[:snippet]['updated_at']) - .to be > snippet.hook_attrs['updated_at'] + .to be >= snippet.hook_attrs['updated_at'] end include_examples 'project hook data' diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb index 44695acbe7d..51fad6c6838 100644 --- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb @@ -164,7 +164,7 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do Timecop.freeze do importer.update_repository - expect(project.last_repository_updated_at).to eq(Time.zone.now) + expect(project.last_repository_updated_at).to be_like_time(Time.zone.now) end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index a2f8fac2f38..abdc65336ca 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -571,13 +571,13 @@ describe Project do last_activity_at: timestamp, last_repository_updated_at: timestamp - 1.hour) - expect(project.last_activity_date).to eq(timestamp) + expect(project.last_activity_date).to be_like_time(timestamp) project.update_attributes(updated_at: timestamp, last_activity_at: timestamp - 1.hour, last_repository_updated_at: nil) - expect(project.last_activity_date).to eq(timestamp) + expect(project.last_activity_date).to be_like_time(timestamp) end end end diff --git a/spec/services/keys/last_used_service_spec.rb b/spec/services/keys/last_used_service_spec.rb index bb0fb6acf39..8e553c2f1fa 100644 --- a/spec/services/keys/last_used_service_spec.rb +++ b/spec/services/keys/last_used_service_spec.rb @@ -8,7 +8,7 @@ describe Keys::LastUsedService do Timecop.freeze(time) { described_class.new(key).execute } - expect(key.last_used_at).to eq(time) + expect(key.reload.last_used_at).to be_like_time(time) end it 'does not update the key when it has been used recently' do @@ -17,7 +17,7 @@ describe Keys::LastUsedService do described_class.new(key).execute - expect(key.last_used_at).to eq(time) + expect(key.last_used_at).to be_like_time(time) end it 'does not update the updated_at field' do diff --git a/spec/services/update_merge_request_metrics_service_spec.rb b/spec/services/update_merge_request_metrics_service_spec.rb index b5fb999381d..812dd42934d 100644 --- a/spec/services/update_merge_request_metrics_service_spec.rb +++ b/spec/services/update_merge_request_metrics_service_spec.rb @@ -12,7 +12,7 @@ describe MergeRequestMetricsService do service.merge(event) expect(metrics.merged_by).to eq(user) - expect(metrics.merged_at).to eq(event.created_at) + expect(metrics.merged_at).to be_like_time(event.created_at) end end @@ -25,7 +25,7 @@ describe MergeRequestMetricsService do service.close(event) expect(metrics.latest_closed_by).to eq(user) - expect(metrics.latest_closed_at).to eq(event.created_at) + expect(metrics.latest_closed_at).to be_like_time(event.created_at) end end diff --git a/spec/support/shared_examples/throttled_touch.rb b/spec/support/shared_examples/throttled_touch.rb index 4a25bb9b750..eba990d4037 100644 --- a/spec/support/shared_examples/throttled_touch.rb +++ b/spec/support/shared_examples/throttled_touch.rb @@ -3,7 +3,7 @@ shared_examples_for 'throttled touch' do it 'updates the updated_at timestamp' do Timecop.freeze do subject.touch - expect(subject.updated_at).to eq(Time.zone.now) + expect(subject.updated_at).to be_like_time(Time.zone.now) end end @@ -14,7 +14,7 @@ shared_examples_for 'throttled touch' do Timecop.freeze(first_updated_at) { subject.touch } Timecop.freeze(second_updated_at) { subject.touch } - expect(subject.updated_at).to eq(first_updated_at) + expect(subject.updated_at).to be_like_time(first_updated_at) end end end -- cgit v1.2.1 From a5bd978ad7d856d4171a413e3a17b2ead4092dea Mon Sep 17 00:00:00 2001 From: Jasper Maes Date: Wed, 27 Jun 2018 18:39:46 +0200 Subject: Rails5 fix Mysql comparison failure caused by milliseconds problem --- spec/models/concerns/resolvable_discussion_spec.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'spec') diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb index 2a2ef5a304d..2f9f63ce7e0 100644 --- a/spec/models/concerns/resolvable_discussion_spec.rb +++ b/spec/models/concerns/resolvable_discussion_spec.rb @@ -534,11 +534,18 @@ describe Discussion, ResolvableDiscussion do describe "#last_resolved_note" do let(:current_user) { create(:user) } + let(:time) { Time.now.utc } before do - first_note.resolve!(current_user) - third_note.resolve!(current_user) - second_note.resolve!(current_user) + Timecop.freeze(time - 1.second) do + first_note.resolve!(current_user) + end + Timecop.freeze(time) do + third_note.resolve!(current_user) + end + Timecop.freeze(time + 1.second) do + second_note.resolve!(current_user) + end end it "returns the last note that was resolved" do -- cgit v1.2.1 From 6cfc5c6a5ed80d2b4fc3793ee08fc47706ecef6d Mon Sep 17 00:00:00 2001 From: Jamie Schembri Date: Sat, 23 Jun 2018 19:50:50 +0200 Subject: Fix #39604 - Update header avatar after user changes avatar --- .../uploads/user_uploads_avatar_to_profile_spec.rb | 27 +++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'spec') diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb index 52003bb0859..766bb4f09cd 100644 --- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb +++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb @@ -1,17 +1,16 @@ require 'rails_helper' feature 'User uploads avatar to profile' do - scenario 'they see their new avatar' do - user = create(:user) - sign_in(user) + let!(:user) { create(:user) } + let(:avatar_file_path) { Rails.root.join('spec', 'fixtures', 'dk.png') } + before do + sign_in user visit profile_path - attach_file( - 'user_avatar', - Rails.root.join('spec', 'fixtures', 'dk.png'), - visible: false - ) + end + scenario 'they see their new avatar on their profile' do + attach_file('user_avatar', avatar_file_path, visible: false) click_button 'Update profile settings' visit user_path(user) @@ -21,4 +20,16 @@ feature 'User uploads avatar to profile' do # Cheating here to verify something that isn't user-facing, but is important expect(user.reload.avatar.file).to exist end + + scenario 'their new avatar is immediately visible in the header', :js do + find('.js-user-avatar-input', visible: false).set(avatar_file_path) + + click_button 'Set new profile picture' + click_button 'Update profile settings' + + wait_for_all_requests + + data_uri = find('.avatar-image .avatar')['src'] + expect(page.find('.header-user-avatar')['src']).to eq data_uri + end end -- cgit v1.2.1 From 48901bdecfe30fd201c01a608fdc3b35e4f70e08 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Tue, 26 Jun 2018 14:27:01 +0200 Subject: Bring Gitlab::ShardHealthCache to CE It already existed in EE in the Geo namespace. This change brings it to CE. --- spec/lib/gitlab/shard_health_cache_spec.rb | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 spec/lib/gitlab/shard_health_cache_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/shard_health_cache_spec.rb b/spec/lib/gitlab/shard_health_cache_spec.rb new file mode 100644 index 00000000000..e1a69261939 --- /dev/null +++ b/spec/lib/gitlab/shard_health_cache_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe Gitlab::ShardHealthCache, :clean_gitlab_redis_cache do + let(:shards) { %w(foo bar) } + + before do + described_class.update(shards) + end + + describe '.clear' do + it 'leaves no shards around' do + described_class.clear + + expect(described_class.healthy_shard_count).to eq(0) + end + end + + describe '.update' do + it 'returns the healthy shards' do + expect(described_class.cached_healthy_shards).to match_array(shards) + end + + it 'replaces the existing set' do + new_set = %w(test me more) + described_class.update(new_set) + + expect(described_class.cached_healthy_shards).to match_array(new_set) + end + end + + describe '.healthy_shard_count' do + it 'returns the healthy shard count' do + expect(described_class.healthy_shard_count).to eq(2) + end + + it 'returns 0 if no shards are available' do + described_class.update([]) + + expect(described_class.healthy_shard_count).to eq(0) + end + end + + describe '.healthy_shard?' do + it 'returns true for a healthy shard' do + expect(described_class.healthy_shard?('foo')).to be_truthy + end + + it 'returns false for an unknown shard' do + expect(described_class.healthy_shard?('unknown')).to be_falsey + end + end +end -- cgit v1.2.1 From 95ac8b0e1d0385429f5a09edf0dc908346fdd3a7 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Tue, 26 Jun 2018 15:12:29 +0200 Subject: Add RepositoryCheck::DispatchWorker to start worker per shard The RepositoryCheck::DispatchWorker will start a RepositoryCheck::BatchWorker for each healthy shard. Closes gitlab-org/gitlab-ce#48042 --- spec/workers/repository_check/batch_worker_spec.rb | 29 ++++++++++++++--- .../repository_check/dispatch_worker_spec.rb | 36 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 spec/workers/repository_check/dispatch_worker_spec.rb (limited to 'spec') diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb index 6cd27d2fafb..6bc551be9ad 100644 --- a/spec/workers/repository_check/batch_worker_spec.rb +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -1,14 +1,19 @@ require 'spec_helper' describe RepositoryCheck::BatchWorker do + let(:shard_name) { 'default' } subject { described_class.new } + before do + Gitlab::ShardHealthCache.update([shard_name]) + end + it 'prefers projects that have never been checked' do projects = create_list(:project, 3, created_at: 1.week.ago) projects[0].update_column(:last_repository_check_at, 4.months.ago) projects[2].update_column(:last_repository_check_at, 3.months.ago) - expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) + expect(subject.perform(shard_name)).to eq(projects.values_at(1, 0, 2).map(&:id)) end it 'sorts projects by last_repository_check_at' do @@ -17,7 +22,7 @@ describe RepositoryCheck::BatchWorker do projects[1].update_column(:last_repository_check_at, 4.months.ago) projects[2].update_column(:last_repository_check_at, 3.months.ago) - expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) + expect(subject.perform(shard_name)).to eq(projects.values_at(1, 2, 0).map(&:id)) end it 'excludes projects that were checked recently' do @@ -26,7 +31,14 @@ describe RepositoryCheck::BatchWorker do projects[1].update_column(:last_repository_check_at, 2.months.ago) projects[2].update_column(:last_repository_check_at, 3.days.ago) - expect(subject.perform).to eq([projects[1].id]) + expect(subject.perform(shard_name)).to eq([projects[1].id]) + end + + it 'excludes projects on another shard' do + projects = create_list(:project, 2, created_at: 1.week.ago) + projects[0].update_column(:repository_storage, 'other') + + expect(subject.perform(shard_name)).to eq([projects[1].id]) end it 'does nothing when repository checks are disabled' do @@ -34,13 +46,20 @@ describe RepositoryCheck::BatchWorker do stub_application_setting(repository_checks_enabled: false) - expect(subject.perform).to eq(nil) + expect(subject.perform(shard_name)).to eq(nil) + end + + it 'does nothing when shard is unhealthy' do + shard_name = 'broken' + create(:project, created_at: 1.week.ago, repository_storage: shard_name) + + expect(subject.perform(shard_name)).to eq(nil) end it 'skips projects created less than 24 hours ago' do project = create(:project) project.update_column(:created_at, 23.hours.ago) - expect(subject.perform).to eq([]) + expect(subject.perform(shard_name)).to eq([]) end end diff --git a/spec/workers/repository_check/dispatch_worker_spec.rb b/spec/workers/repository_check/dispatch_worker_spec.rb new file mode 100644 index 00000000000..20a4f1f5344 --- /dev/null +++ b/spec/workers/repository_check/dispatch_worker_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe RepositoryCheck::DispatchWorker do + subject { described_class.new } + + it 'does nothing when repository checks are disabled' do + stub_application_setting(repository_checks_enabled: false) + + expect(RepositoryCheck::BatchWorker).not_to receive(:perform_async) + + subject.perform + end + + it 'dispatches work to RepositoryCheck::BatchWorker' do + expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once) + + subject.perform + end + + context 'with unhealthy shard' do + let(:default_shard_name) { 'default' } + let(:unhealthy_shard_name) { 'unhealthy' } + let(:default_shard) { Gitlab::HealthChecks::Result.new(true, nil, shard: default_shard_name) } + let(:unhealthy_shard) { Gitlab::HealthChecks::Result.new(false, '14:Connect Failed', shard: unhealthy_shard_name) } + + before do + allow(Gitlab::HealthChecks::GitalyCheck).to receive(:readiness).and_return([default_shard, unhealthy_shard]) + end + + it 'only triggers RepositoryCheck::BatchWorker for healthy shards' do + expect(RepositoryCheck::BatchWorker).to receive(:perform_async).with('default') + + subject.perform + end + end +end -- cgit v1.2.1 From c9ecf49d8cf9836698d84e69ed9335e9fe326eea Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 27 Jun 2018 15:15:08 -0500 Subject: Correct "autorize" typos --- spec/requests/api/issues_spec.rb | 4 ++-- spec/requests/api/project_snippets_spec.rb | 2 +- spec/requests/api/snippets_spec.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index a15d60aafe0..95eff029f98 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -1679,7 +1679,7 @@ describe API::Issues do let!(:user_agent_detail) { create(:user_agent_detail, subject: issue) } context 'when unauthenticated' do - it "returns unautorized" do + it "returns unauthorized" do get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail") expect(response).to have_gitlab_http_status(401) @@ -1695,7 +1695,7 @@ describe API::Issues do expect(json_response['akismet_submitted']).to eq(user_agent_detail.submitted) end - it "returns unautorized for non-admin users" do + it "returns unauthorized for non-admin users" do get api("/projects/#{project.id}/issues/#{issue.iid}/user_agent_detail", user) expect(response).to have_gitlab_http_status(403) diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 4a2289ca137..a3b5e8c6223 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -25,7 +25,7 @@ describe API::ProjectSnippets do expect(response).to have_gitlab_http_status(404) end - it "returns unautorized for non-admin users" do + it "returns unauthorized for non-admin users" do get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/user_agent_detail", user) expect(response).to have_gitlab_http_status(403) diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb index c5456977b60..6da769cb3ed 100644 --- a/spec/requests/api/snippets_spec.rb +++ b/spec/requests/api/snippets_spec.rb @@ -314,7 +314,7 @@ describe API::Snippets do expect(json_response['akismet_submitted']).to eq(user_agent_detail.submitted) end - it "returns unautorized for non-admin users" do + it "returns unauthorized for non-admin users" do get api("/snippets/#{snippet.id}/user_agent_detail", user) expect(response).to have_gitlab_http_status(403) -- cgit v1.2.1 From 1ef3b3efbddd6c4a81102acf940674d80e0c0a8d Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 27 Jun 2018 13:42:22 -0700 Subject: Update tests for gitlab:db:configure --- spec/tasks/gitlab/db_rake_spec.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb index fc52c04e78d..81b9ac94ff3 100644 --- a/spec/tasks/gitlab/db_rake_spec.rb +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -20,7 +20,7 @@ describe 'gitlab:db namespace rake task' do describe 'configure' do it 'invokes db:migrate when schema has already been loaded' do - allow(ActiveRecord::Base.connection).to receive(:tables).and_return(['default']) + allow(ActiveRecord::Base.connection).to receive(:tables).and_return(['table1', 'table2']) expect(Rake::Task['db:migrate']).to receive(:invoke) expect(Rake::Task['db:schema:load']).not_to receive(:invoke) expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) @@ -35,6 +35,14 @@ describe 'gitlab:db namespace rake task' do expect { run_rake_task('gitlab:db:configure') }.not_to raise_error end + it 'invokes db:shema:load and db:seed_fu when there is only a single table present' do + allow(ActiveRecord::Base.connection).to receive(:tables).and_return(['default']) + expect(Rake::Task['db:schema:load']).to receive(:invoke) + expect(Rake::Task['db:seed_fu']).to receive(:invoke) + expect(Rake::Task['db:migrate']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.not_to raise_error + end + it 'does not invoke any other rake tasks during an error' do allow(ActiveRecord::Base).to receive(:connection).and_raise(RuntimeError, 'error') expect(Rake::Task['db:migrate']).not_to receive(:invoke) -- cgit v1.2.1 From 2efe4a13b6be650258ade395438600c64820cb19 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 27 Jun 2018 14:09:06 -0700 Subject: Fix static analysis failure --- spec/tasks/gitlab/db_rake_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb index 81b9ac94ff3..b81aea23306 100644 --- a/spec/tasks/gitlab/db_rake_spec.rb +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -20,7 +20,7 @@ describe 'gitlab:db namespace rake task' do describe 'configure' do it 'invokes db:migrate when schema has already been loaded' do - allow(ActiveRecord::Base.connection).to receive(:tables).and_return(['table1', 'table2']) + allow(ActiveRecord::Base.connection).to receive(:tables).and_return(%w[table1 table2]) expect(Rake::Task['db:migrate']).to receive(:invoke) expect(Rake::Task['db:schema:load']).not_to receive(:invoke) expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) -- cgit v1.2.1 From fbf747194f2a655aab3670c30cc6f054c29bbfe7 Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Wed, 27 Jun 2018 21:20:41 +0000 Subject: Componentize diff lines and diff comments --- .../components/diff_line_gutter_content_spec.js | 47 +---- .../diffs/components/inline_diff_view_spec.js | 66 +------ .../diffs/components/parallel_diff_view_spec.js | 195 --------------------- 3 files changed, 2 insertions(+), 306 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js index cce10c4083c..2d136a63c52 100644 --- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js +++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js @@ -2,12 +2,6 @@ import Vue from 'vue'; import DiffLineGutterContent from '~/diffs/components/diff_line_gutter_content.vue'; import store from '~/mr_notes/stores'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; -import { - MATCH_LINE_TYPE, - CONTEXT_LINE_TYPE, - OLD_NO_NEW_LINE_TYPE, - NEW_NO_NEW_LINE_TYPE, -} from '~/diffs/constants'; import discussionsMockData from '../mock_data/diff_discussions'; import diffFileMockData from '../mock_data/diff_file'; @@ -31,45 +25,6 @@ describe('DiffLineGutterContent', () => { }; describe('computed', () => { - describe('isMatchLine', () => { - it('should return true for match line type', () => { - const component = createComponent({ lineType: MATCH_LINE_TYPE }); - expect(component.isMatchLine).toEqual(true); - }); - - it('should return false for non-match line type', () => { - const component = createComponent({ lineType: CONTEXT_LINE_TYPE }); - expect(component.isMatchLine).toEqual(false); - }); - }); - - describe('isContextLine', () => { - it('should return true for context line type', () => { - const component = createComponent({ lineType: CONTEXT_LINE_TYPE }); - expect(component.isContextLine).toEqual(true); - }); - - it('should return false for non-context line type', () => { - const component = createComponent({ lineType: MATCH_LINE_TYPE }); - expect(component.isContextLine).toEqual(false); - }); - }); - - describe('isMetaLine', () => { - it('should return true for meta line type', () => { - const component = createComponent({ lineType: NEW_NO_NEW_LINE_TYPE }); - expect(component.isMetaLine).toEqual(true); - - const component2 = createComponent({ lineType: OLD_NO_NEW_LINE_TYPE }); - expect(component2.isMetaLine).toEqual(true); - }); - - it('should return false for non-meta line type', () => { - const component = createComponent({ lineType: MATCH_LINE_TYPE }); - expect(component.isMetaLine).toEqual(false); - }); - }); - describe('lineHref', () => { it('should prepend # to lineCode', () => { const lineCode = 'LC_42'; @@ -109,7 +64,7 @@ describe('DiffLineGutterContent', () => { describe('template', () => { it('should render three dots for context lines', () => { const component = createComponent({ - lineType: MATCH_LINE_TYPE, + isMatchLine: true, }); expect(component.$el.querySelector('span').classList.contains('context-cell')).toEqual(true); diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js index 0d5a3576204..e1adf60962e 100644 --- a/spec/javascripts/diffs/components/inline_diff_view_spec.js +++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js @@ -1,7 +1,6 @@ import Vue from 'vue'; import InlineDiffView from '~/diffs/components/inline_diff_view.vue'; import store from '~/mr_notes/stores'; -import * as constants from '~/diffs/constants'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import diffFileMockData from '../mock_data/diff_file'; import discussionsMockData from '../mock_data/diff_discussions'; @@ -14,58 +13,13 @@ describe('InlineDiffView', () => { beforeEach(() => { const diffFile = getDiffFileMock(); + store.dispatch('setInlineDiffViewType'); component = createComponentWithStore(Vue.extend(InlineDiffView), store, { diffFile, diffLines: diffFile.highlightedDiffLines, }).$mount(); }); - describe('methods', () => { - describe('handleMouse', () => { - it('should set hoveredLineCode', () => { - expect(component.hoveredLineCode).toEqual(null); - - component.handleMouse('lineCode1', true); - expect(component.hoveredLineCode).toEqual('lineCode1'); - - component.handleMouse('lineCode1', false); - expect(component.hoveredLineCode).toEqual(null); - }); - }); - - describe('getLineClass', () => { - it('should return line class object', () => { - const { LINE_HOVER_CLASS_NAME, LINE_UNFOLD_CLASS_NAME } = constants; - const { MATCH_LINE_TYPE, NEW_LINE_TYPE } = constants; - - expect(component.getLineClass(component.diffLines[0])).toEqual({ - [NEW_LINE_TYPE]: NEW_LINE_TYPE, - [LINE_UNFOLD_CLASS_NAME]: false, - [LINE_HOVER_CLASS_NAME]: false, - }); - - component.handleMouse(component.diffLines[0].lineCode, true); - Object.defineProperty(component, 'isLoggedIn', { - get() { - return true; - }, - }); - - expect(component.getLineClass(component.diffLines[0])).toEqual({ - [NEW_LINE_TYPE]: NEW_LINE_TYPE, - [LINE_UNFOLD_CLASS_NAME]: false, - [LINE_HOVER_CLASS_NAME]: true, - }); - - expect(component.getLineClass(component.diffLines[5])).toEqual({ - [MATCH_LINE_TYPE]: MATCH_LINE_TYPE, - [LINE_UNFOLD_CLASS_NAME]: true, - [LINE_HOVER_CLASS_NAME]: false, - }); - }); - }); - }); - describe('template', () => { it('should have rendered diff lines', () => { const el = component.$el; @@ -89,23 +43,5 @@ describe('InlineDiffView', () => { done(); }); }); - - it('should render new discussion forms', done => { - const el = component.$el; - const lines = getDiffFileMock().highlightedDiffLines; - - component.handleShowCommentForm({ lineCode: lines[0].lineCode }); - component.handleShowCommentForm({ lineCode: lines[1].lineCode }); - - Vue.nextTick(() => { - expect(el.querySelectorAll('.js-vue-markdown-field').length).toEqual(2); - expect(el.querySelectorAll('tr')[1].classList.contains('notes_holder')).toEqual(true); - expect(el.querySelectorAll('tr')[3].classList.contains('notes_holder')).toEqual(true); - - store.state.diffs.diffLineCommentForms = {}; - - done(); - }); - }); }); }); diff --git a/spec/javascripts/diffs/components/parallel_diff_view_spec.js b/spec/javascripts/diffs/components/parallel_diff_view_spec.js index cab533217c0..165e4b69b6c 100644 --- a/spec/javascripts/diffs/components/parallel_diff_view_spec.js +++ b/spec/javascripts/diffs/components/parallel_diff_view_spec.js @@ -4,12 +4,10 @@ import store from '~/mr_notes/stores'; import * as constants from '~/diffs/constants'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import diffFileMockData from '../mock_data/diff_file'; -import discussionsMockData from '../mock_data/diff_discussions'; describe('ParallelDiffView', () => { let component; const getDiffFileMock = () => Object.assign({}, diffFileMockData); - const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)]; beforeEach(() => { const diffFile = getDiffFileMock(); @@ -28,197 +26,4 @@ describe('ParallelDiffView', () => { }); }); }); - - describe('methods', () => { - describe('hasDiscussion', () => { - it('it should return true if there is a discussion either for left or right section', () => { - Object.defineProperty(component, 'discussionsByLineCode', { - get() { - return { line_42: true }; - }, - }); - - expect(component.hasDiscussion({ left: {}, right: {} })).toEqual(undefined); - expect(component.hasDiscussion({ left: { lineCode: 'line_42' }, right: {} })).toEqual(true); - expect(component.hasDiscussion({ left: {}, right: { lineCode: 'line_42' } })).toEqual(true); - }); - }); - - describe('getClassName', () => { - it('should return line class object', () => { - const { LINE_HOVER_CLASS_NAME, LINE_UNFOLD_CLASS_NAME } = constants; - const { MATCH_LINE_TYPE, NEW_LINE_TYPE, LINE_POSITION_RIGHT } = constants; - - expect(component.getClassName(component.diffLines[1], LINE_POSITION_RIGHT)).toEqual({ - [NEW_LINE_TYPE]: NEW_LINE_TYPE, - [LINE_UNFOLD_CLASS_NAME]: false, - [LINE_HOVER_CLASS_NAME]: false, - }); - - const eventMock = { - target: component.$refs.rightLines[1], - }; - - component.handleMouse(eventMock, component.diffLines[1], true); - Object.defineProperty(component, 'isLoggedIn', { - get() { - return true; - }, - }); - - expect(component.getClassName(component.diffLines[1], LINE_POSITION_RIGHT)).toEqual({ - [NEW_LINE_TYPE]: NEW_LINE_TYPE, - [LINE_UNFOLD_CLASS_NAME]: false, - [LINE_HOVER_CLASS_NAME]: true, - }); - - expect(component.getClassName(component.diffLines[5], LINE_POSITION_RIGHT)).toEqual({ - [MATCH_LINE_TYPE]: MATCH_LINE_TYPE, - [LINE_UNFOLD_CLASS_NAME]: true, - [LINE_HOVER_CLASS_NAME]: false, - }); - }); - }); - - describe('handleMouse', () => { - it('should set hovered line code and line section to null when isHover is false', () => { - const rightLineEventMock = { target: component.$refs.rightLines[1] }; - expect(component.hoveredLineCode).toEqual(null); - expect(component.hoveredSection).toEqual(null); - - component.handleMouse(rightLineEventMock, null, false); - expect(component.hoveredLineCode).toEqual(null); - expect(component.hoveredSection).toEqual(null); - }); - - it('should set hovered line code and line section for right section', () => { - const rightLineEventMock = { target: component.$refs.rightLines[1] }; - component.handleMouse(rightLineEventMock, component.diffLines[1], true); - expect(component.hoveredLineCode).toEqual(component.diffLines[1].right.lineCode); - expect(component.hoveredSection).toEqual(constants.LINE_POSITION_RIGHT); - }); - - it('should set hovered line code and line section for left section', () => { - const leftLineEventMock = { target: component.$refs.leftLines[2] }; - component.handleMouse(leftLineEventMock, component.diffLines[2], true); - expect(component.hoveredLineCode).toEqual(component.diffLines[2].left.lineCode); - expect(component.hoveredSection).toEqual(constants.LINE_POSITION_LEFT); - }); - }); - - describe('shouldRenderDiscussions', () => { - it('should return true if there is a discussion on left side and it is expanded', () => { - const line = { left: { lineCode: 'lineCode1' } }; - spyOn(component, 'isDiscussionExpanded').and.returnValue(true); - Object.defineProperty(component, 'discussionsByLineCode', { - get() { - return { - [line.left.lineCode]: true, - }; - }, - }); - - expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_LEFT)).toEqual(true); - expect(component.isDiscussionExpanded).toHaveBeenCalledWith(line.left.lineCode); - }); - - it('should return false if there is a discussion on left side but it is collapsed', () => { - const line = { left: { lineCode: 'lineCode1' } }; - spyOn(component, 'isDiscussionExpanded').and.returnValue(false); - Object.defineProperty(component, 'discussionsByLineCode', { - get() { - return { - [line.left.lineCode]: true, - }; - }, - }); - - expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_LEFT)).toEqual( - false, - ); - }); - - it('should return false for discussions on the right side if there is no line type', () => { - const CUSTOM_RIGHT_LINE_TYPE = 'CUSTOM_RIGHT_LINE_TYPE'; - const line = { right: { lineCode: 'lineCode1', type: CUSTOM_RIGHT_LINE_TYPE } }; - spyOn(component, 'isDiscussionExpanded').and.returnValue(true); - Object.defineProperty(component, 'discussionsByLineCode', { - get() { - return { - [line.right.lineCode]: true, - }; - }, - }); - - expect(component.shouldRenderDiscussions(line, constants.LINE_POSITION_RIGHT)).toEqual( - CUSTOM_RIGHT_LINE_TYPE, - ); - }); - }); - - describe('hasAnyExpandedDiscussion', () => { - const LINE_CODE_LEFT = 'LINE_CODE_LEFT'; - const LINE_CODE_RIGHT = 'LINE_CODE_RIGHT'; - - it('should return true if there is a discussion either on the left or the right side', () => { - const mockLineOne = { - right: { lineCode: LINE_CODE_RIGHT }, - left: {}, - }; - const mockLineTwo = { - left: { lineCode: LINE_CODE_LEFT }, - right: {}, - }; - - spyOn(component, 'isDiscussionExpanded').and.callFake(lc => lc === LINE_CODE_RIGHT); - expect(component.hasAnyExpandedDiscussion(mockLineOne)).toEqual(true); - expect(component.hasAnyExpandedDiscussion(mockLineTwo)).toEqual(false); - }); - }); - }); - - describe('template', () => { - it('should have rendered diff lines', () => { - const el = component.$el; - - expect(el.querySelectorAll('tr.line_holder.parallel').length).toEqual(6); - expect(el.querySelectorAll('td.empty-cell').length).toEqual(4); - expect(el.querySelectorAll('td.line_content.parallel.right-side').length).toEqual(6); - expect(el.querySelectorAll('td.line_content.parallel.left-side').length).toEqual(6); - expect(el.querySelectorAll('td.match').length).toEqual(4); - expect(el.textContent.indexOf('Bad dates') > -1).toEqual(true); - }); - - it('should render discussions', done => { - const el = component.$el; - component.$store.dispatch('setInitialNotes', getDiscussionsMockData()); - - Vue.nextTick(() => { - expect(el.querySelectorAll('.notes_holder').length).toEqual(1); - expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5); - expect(el.innerText.indexOf('comment 5') > -1).toEqual(true); - component.$store.dispatch('setInitialNotes', []); - - done(); - }); - }); - - it('should render new discussion forms', done => { - const el = component.$el; - const lines = getDiffFileMock().parallelDiffLines; - - component.handleShowCommentForm({ lineCode: lines[0].lineCode }); - component.handleShowCommentForm({ lineCode: lines[1].lineCode }); - - Vue.nextTick(() => { - expect(el.querySelectorAll('.js-vue-markdown-field').length).toEqual(2); - expect(el.querySelectorAll('tr')[1].classList.contains('notes_holder')).toEqual(true); - expect(el.querySelectorAll('tr')[3].classList.contains('notes_holder')).toEqual(true); - - store.state.diffs.diffLineCommentForms = {}; - - done(); - }); - }); - }); }); -- cgit v1.2.1 From 80f4e7575e99c4dfe1cf7a6ccf1b055e7654be4b Mon Sep 17 00:00:00 2001 From: Ahmet Demir Date: Thu, 28 Jun 2018 06:10:51 +0000 Subject: Add SHA256 and HEAD on File API --- spec/requests/api/files_spec.rb | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'spec') diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index d8fdfd6dee1..4bc5d3ee899 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -21,6 +21,89 @@ describe API::Files do "/projects/#{project.id}/repository/files/#{file_path}" end + describe "HEAD /projects/:id/repository/files/:file_path" do + shared_examples_for 'repository files' do + it 'returns file attributes in headers' do + head api(route(file_path), current_user), params + + expect(response).to have_gitlab_http_status(200) + expect(response.headers['X-Gitlab-File-Path']).to eq(CGI.unescape(file_path)) + expect(response.headers['X-Gitlab-File-Name']).to eq('popen.rb') + expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + expect(response.headers['X-Gitlab-Content-Sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887') + end + + it 'returns file by commit sha' do + # This file is deleted on HEAD + file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee" + params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9" + + head api(route(file_path), current_user), params + + expect(response).to have_gitlab_http_status(200) + expect(response.headers['X-Gitlab-File-Name']).to eq('commit.js.coffee') + expect(response.headers['X-Gitlab-Content-Sha256']).to eq('08785f04375b47f81f46e68cc125d5ef368aa20576ddb53f91f4d83f1d04b929') + end + + context 'when mandatory params are not given' do + it "responds with a 400 status" do + head api(route("any%2Ffile"), current_user) + + expect(response).to have_gitlab_http_status(400) + end + end + + context 'when file_path does not exist' do + it "responds with a 404 status" do + params[:ref] = 'master' + + head api(route('app%2Fmodels%2Fapplication%2Erb'), current_user), params + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when file_path does not exist' do + include_context 'disabled repository' + + it "responds with a 403 status" do + head api(route(file_path), current_user), params + + expect(response).to have_gitlab_http_status(403) + end + end + end + + context 'when unauthenticated', 'and project is public' do + it_behaves_like 'repository files' do + let(:project) { create(:project, :public, :repository) } + let(:current_user) { nil } + end + end + + context 'when unauthenticated', 'and project is private' do + it "responds with a 404 status" do + current_user = nil + + head api(route(file_path), current_user), params + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when authenticated', 'as a developer' do + it_behaves_like 'repository files' do + let(:current_user) { user } + end + end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { head api(route(file_path), guest), params } + end + end + end + describe "GET /projects/:id/repository/files/:file_path" do shared_examples_for 'repository files' do it 'returns file attributes as json' do @@ -30,6 +113,7 @@ describe API::Files do expect(json_response['file_path']).to eq(CGI.unescape(file_path)) expect(json_response['file_name']).to eq('popen.rb') expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + expect(json_response['content_sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887') expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") end @@ -51,6 +135,7 @@ describe API::Files do expect(response).to have_gitlab_http_status(200) expect(json_response['file_name']).to eq('commit.js.coffee') + expect(json_response['content_sha256']).to eq('08785f04375b47f81f46e68cc125d5ef368aa20576ddb53f91f4d83f1d04b929') expect(Base64.decode64(json_response['content']).lines.first).to eq("class Commit\n") end -- cgit v1.2.1 From 591edb439c2608f7448d7c3d5d2fc35e0ad5e8c1 Mon Sep 17 00:00:00 2001 From: Maciej Nowak Date: Sat, 23 Jun 2018 21:39:11 +0200 Subject: Allow straight diff in Compare API Repository compare API now allows choosing straight (from..to) or merge-base diff (from...to) --- spec/requests/api/repositories_spec.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'spec') diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index cd135dfc32a..28f8564ae92 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -288,6 +288,9 @@ describe API::Repositories do shared_examples_for 'repository compare' do it "compares branches" do + expect(::Gitlab::Git::Compare).to receive(:new).with(anything, anything, anything, { + straight: false + }).and_call_original get api(route, current_user), from: 'master', to: 'feature' expect(response).to have_gitlab_http_status(200) @@ -295,6 +298,28 @@ describe API::Repositories do expect(json_response['diffs']).to be_present end + it "compares branches with explicit merge-base mode" do + expect(::Gitlab::Git::Compare).to receive(:new).with(anything, anything, anything, { + straight: false + }).and_call_original + get api(route, current_user), from: 'master', to: 'feature', straight: false + + expect(response).to have_gitlab_http_status(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present + end + + it "compares branches with explicit straight mode" do + expect(::Gitlab::Git::Compare).to receive(:new).with(anything, anything, anything, { + straight: true + }).and_call_original + get api(route, current_user), from: 'master', to: 'feature', straight: true + + expect(response).to have_gitlab_http_status(200) + expect(json_response['commits']).to be_present + expect(json_response['diffs']).to be_present + end + it "compares tags" do get api(route, current_user), from: 'v1.0.0', to: 'v1.1.0' -- cgit v1.2.1 From 080cbda585eef9df4934f9670c4967d26eb42545 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 28 Jun 2018 12:01:16 +0200 Subject: Drop the spec in YamlProcessor that reads current .gitlab-ci.yaml. If the .yaml file is not valid, specs would not run in CI. --- spec/lib/gitlab/ci/yaml_processor_spec.rb | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index fa5327c26f0..e73cdc54a15 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -5,16 +5,6 @@ module Gitlab describe YamlProcessor do subject { described_class.new(config) } - describe 'our current .gitlab-ci.yml' do - let(:config) { File.read("#{Rails.root}/.gitlab-ci.yml") } - - it 'is valid' do - error_message = described_class.validation_message(config) - - expect(error_message).to be_nil - end - end - describe '#build_attributes' do subject { described_class.new(config).build_attributes(:rspec) } -- cgit v1.2.1 From f369da063f4a07b6c30852a29e5ce1071e77b86a Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 28 Jun 2018 09:36:57 +0200 Subject: Commits between in Gitaly only Prior to this change, most the commits counted were done through Gitaly. This removes the last point where this wasn't the case. This makes the `rugged_count_commits` method obsolete, with its tests. Closes https://gitlab.com/gitlab-org/gitaly/issues/315 --- spec/lib/gitlab/git/repository_spec.rb | 40 ---------------------------------- 1 file changed, 40 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index b78fe4ba310..6ec4b90d70c 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -996,46 +996,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe "#rugged_commits_between" do - around do |example| - # TODO #rugged_commits_between will be removed, has been migrated to gitaly - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - example.run - end - end - - context 'two SHAs' do - let(:first_sha) { 'b0e52af38d7ea43cf41d8a6f2471351ac036d6c9' } - let(:second_sha) { '0e50ec4d3c7ce42ab74dda1d422cb2cbffe1e326' } - - it 'returns the number of commits between' do - expect(repository.rugged_commits_between(first_sha, second_sha).count).to eq(3) - end - end - - context 'SHA and master branch' do - let(:sha) { 'b0e52af38d7ea43cf41d8a6f2471351ac036d6c9' } - let(:branch) { 'master' } - - it 'returns the number of commits between a sha and a branch' do - expect(repository.rugged_commits_between(sha, branch).count).to eq(5) - end - - it 'returns the number of commits between a branch and a sha' do - expect(repository.rugged_commits_between(branch, sha).count).to eq(0) # sha is before branch - end - end - - context 'two branches' do - let(:first_branch) { 'feature' } - let(:second_branch) { 'master' } - - it 'returns the number of commits between' do - expect(repository.rugged_commits_between(first_branch, second_branch).count).to eq(17) - end - end - end - describe '#count_commits_between' do subject { repository.count_commits_between('feature', 'master') } -- cgit v1.2.1 From ec78d29a6eee9b91299cbaef4e7497f392af0310 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 28 Jun 2018 13:41:59 +0200 Subject: FindAllCommits mandatory Closes https://gitlab.com/gitlab-org/gitaly/issues/326 --- spec/lib/gitlab/git/commit_spec.rb | 102 +++++++++++++------------------------ 1 file changed, 36 insertions(+), 66 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 0d7c930127c..ee74c2769eb 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -331,84 +331,54 @@ describe Gitlab::Git::Commit, seed_helper: true do end describe '.find_all' do - shared_examples 'finding all commits' do - it 'should return a return a collection of commits' do - commits = described_class.find_all(repository) + it 'should return a return a collection of commits' do + commits = described_class.find_all(repository) - expect(commits).to all( be_a_kind_of(described_class) ) - end - - context 'max_count' do - subject do - commits = described_class.find_all( - repository, - max_count: 50 - ) - - commits.map(&:id) - end + expect(commits).to all( be_a_kind_of(described_class) ) + end - it 'has 34 elements' do - expect(subject.size).to eq(34) - end + context 'max_count' do + subject do + commits = described_class.find_all( + repository, + max_count: 50 + ) - it 'includes the expected commits' do - expect(subject).to include( - SeedRepo::Commit::ID, - SeedRepo::Commit::PARENT_ID, - SeedRepo::FirstCommit::ID - ) - end + commits.map(&:id) end - context 'ref + max_count + skip' do - subject do - commits = described_class.find_all( - repository, - ref: 'master', - max_count: 50, - skip: 1 - ) - - commits.map(&:id) - end - - it 'has 24 elements' do - expect(subject.size).to eq(24) - end - - it 'includes the expected commits' do - expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID) - expect(subject).not_to include(SeedRepo::LastCommit::ID) - end + it 'has 34 elements' do + expect(subject.size).to eq(34) end - end - context 'when Gitaly find_all_commits feature is enabled' do - it_behaves_like 'finding all commits' + it 'includes the expected commits' do + expect(subject).to include( + SeedRepo::Commit::ID, + SeedRepo::Commit::PARENT_ID, + SeedRepo::FirstCommit::ID + ) + end end - context 'when Gitaly find_all_commits feature is disabled', :skip_gitaly_mock do - it_behaves_like 'finding all commits' - - context 'while applying a sort order based on the `order` option' do - it "allows ordering topologically (no parents shown before their children)" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO) - - described_class.find_all(repository, order: :topo) - end - - it "allows ordering by date" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO) + context 'ref + max_count + skip' do + subject do + commits = described_class.find_all( + repository, + ref: 'master', + max_count: 50, + skip: 1 + ) - described_class.find_all(repository, order: :date) - end + commits.map(&:id) + end - it "applies no sorting by default" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) + it 'has 24 elements' do + expect(subject.size).to eq(24) + end - described_class.find_all(repository) - end + it 'includes the expected commits' do + expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID) + expect(subject).not_to include(SeedRepo::LastCommit::ID) end end end -- cgit v1.2.1 From 54b56f20b7a70d3e6284c8105eb3d4a568e255b0 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 25 Jun 2018 10:59:00 +0200 Subject: Expose permissions on types in GraphQL This adds a reusable way to expose permissions for a user to types in GraphQL. --- spec/graphql/types/merge_request_type_spec.rb | 5 ++ .../permission_types/base_permission_type_spec.rb | 47 +++++++++++++++ .../types/permission_types/merge_request_spec.rb | 13 ++++ .../permission_types/merge_request_type_spec.rb | 5 ++ .../graphql/types/permission_types/project_spec.rb | 18 ++++++ spec/graphql/types/project_type_spec.rb | 2 + .../api/graphql/project/merge_request_spec.rb | 70 ++++++++++++++++++++++ spec/requests/api/graphql/project_query_spec.rb | 44 -------------- spec/support/matchers/graphql_matchers.rb | 29 ++++++++- .../requests/graphql_shared_examples.rb | 4 +- 10 files changed, 189 insertions(+), 48 deletions(-) create mode 100644 spec/graphql/types/merge_request_type_spec.rb create mode 100644 spec/graphql/types/permission_types/base_permission_type_spec.rb create mode 100644 spec/graphql/types/permission_types/merge_request_spec.rb create mode 100644 spec/graphql/types/permission_types/merge_request_type_spec.rb create mode 100644 spec/graphql/types/permission_types/project_spec.rb create mode 100644 spec/requests/api/graphql/project/merge_request_spec.rb (limited to 'spec') diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb new file mode 100644 index 00000000000..6e57122867a --- /dev/null +++ b/spec/graphql/types/merge_request_type_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Types::MergeRequestType do + it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::MergeRequest) } +end diff --git a/spec/graphql/types/permission_types/base_permission_type_spec.rb b/spec/graphql/types/permission_types/base_permission_type_spec.rb new file mode 100644 index 00000000000..a7e51797047 --- /dev/null +++ b/spec/graphql/types/permission_types/base_permission_type_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe Types::PermissionTypes::BasePermissionType do + let(:permitable) { double('permittable') } + let(:current_user) { build(:user) } + let(:context) { { current_user: current_user } } + subject(:test_type) do + Class.new(described_class) do + graphql_name 'TestClass' + + permission_field :do_stuff, resolve: -> (_, _, _) { true } + ability_field(:read_issue) + abilities :admin_issue + end + end + + describe '.permission_field' do + it 'adds a field for the required permission' do + is_expected.to have_graphql_field(:do_stuff) + end + end + + describe '.ability_field' do + it 'adds a field for the required permission' do + is_expected.to have_graphql_field(:read_issue) + end + + it 'does not add a resolver block if another resolving param is passed' do + expected_keywords = { + name: :resolve_using_hash, + hash_key: :the_key, + type: GraphQL::BOOLEAN_TYPE, + description: "custom description", + null: false + } + expect(test_type).to receive(:field).with(expected_keywords) + + test_type.ability_field :resolve_using_hash, hash_key: :the_key, description: "custom description" + end + end + + describe '.abilities' do + it 'adds a field for the passed permissions' do + is_expected.to have_graphql_field(:admin_issue) + end + end +end diff --git a/spec/graphql/types/permission_types/merge_request_spec.rb b/spec/graphql/types/permission_types/merge_request_spec.rb new file mode 100644 index 00000000000..e1026b01a74 --- /dev/null +++ b/spec/graphql/types/permission_types/merge_request_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe Types::PermissionTypes::MergeRequest do + it do + expected_permissions = [ + :read_merge_request, :admin_merge_request, :update_merge_request, + :create_note, :push_to_source_branch, :remove_source_branch, + :cherry_pick_on_current_merge_request, :revert_on_current_merge_request + ] + + expect(described_class).to have_graphql_fields(expected_permissions) + end +end diff --git a/spec/graphql/types/permission_types/merge_request_type_spec.rb b/spec/graphql/types/permission_types/merge_request_type_spec.rb new file mode 100644 index 00000000000..6e57122867a --- /dev/null +++ b/spec/graphql/types/permission_types/merge_request_type_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Types::MergeRequestType do + it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::MergeRequest) } +end diff --git a/spec/graphql/types/permission_types/project_spec.rb b/spec/graphql/types/permission_types/project_spec.rb new file mode 100644 index 00000000000..89eecef096e --- /dev/null +++ b/spec/graphql/types/permission_types/project_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Types::PermissionTypes::Project do + it do + expected_permissions = [ + :change_namespace, :change_visibility_level, :rename_project, :remove_project, :archive_project, + :remove_fork_project, :remove_pages, :read_project, :create_merge_request_in, + :read_wiki, :read_project_member, :create_issue, :upload_file, :read_cycle_analytics, + :download_code, :download_wiki_code, :fork_project, :create_project_snippet, + :read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule, + :create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch, + :admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label, + :update_wiki, :destroy_wiki, :create_pages, :destroy_pages + ] + + expect(described_class).to have_graphql_fields(expected_permissions) + end +end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index b4eeca2e3f1..7b5bc335511 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe GitlabSchema.types['Project'] do + it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Project) } + it { expect(described_class.graphql_name).to eq('Project') } describe 'nested merge request' do diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb new file mode 100644 index 00000000000..ad57c43bc87 --- /dev/null +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +describe 'getting merge request information nested in a project' do + include GraphqlHelpers + + let(:project) { create(:project, :repository, :public) } + let(:current_user) { create(:user) } + let(:merge_request_graphql_data) { graphql_data['project']['mergeRequest'] } + let!(:merge_request) { create(:merge_request, source_project: project) } + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('mergeRequest', iid: merge_request.iid) + ) + end + + it_behaves_like 'a working graphql query' do + before do + post_graphql(query, current_user: current_user) + end + end + + it 'contains merge request information' do + post_graphql(query, current_user: current_user) + + expect(merge_request_graphql_data).not_to be_nil + end + + # This is a field coming from the `MergeRequestPresenter` + it 'includes a web_url' do + post_graphql(query, current_user: current_user) + + expect(merge_request_graphql_data['webUrl']).to be_present + end + + context 'permissions on the merge request' do + it 'includes the permissions for the current user on a public project' do + expected_permissions = { + 'readMergeRequest' => true, + 'adminMergeRequest' => false, + 'createNote' => true, + 'pushToSourceBranch' => false, + 'removeSourceBranch' => false, + 'cherryPickOnCurrentMergeRequest' => false, + 'revertOnCurrentMergeRequest' => false, + 'updateMergeRequest' => false + } + post_graphql(query, current_user: current_user) + + permission_data = merge_request_graphql_data['userPermissions'] + + expect(permission_data).to be_present + expect(permission_data).to eq(expected_permissions) + end + end + + context 'when the user does not have access to the merge request' do + let(:project) { create(:project, :public, :repository) } + + it 'returns nil' do + project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE) + + post_graphql(query) + + expect(merge_request_graphql_data).to be_nil + end + end +end diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb index 796ffc9d569..a2b3dc5d121 100644 --- a/spec/requests/api/graphql/project_query_spec.rb +++ b/spec/requests/api/graphql/project_query_spec.rb @@ -26,50 +26,6 @@ describe 'getting project information' do post_graphql(query, current_user: current_user) end end - - context 'when requesting a nested merge request' do - let(:merge_request) { create(:merge_request, source_project: project) } - let(:merge_request_graphql_data) { graphql_data['project']['mergeRequest'] } - - let(:query) do - graphql_query_for( - 'project', - { 'fullPath' => project.full_path }, - query_graphql_field('mergeRequest', iid: merge_request.iid) - ) - end - - it_behaves_like 'a working graphql query' do - before do - post_graphql(query, current_user: current_user) - end - end - - it 'contains merge request information' do - post_graphql(query, current_user: current_user) - - expect(merge_request_graphql_data).not_to be_nil - end - - # This is a field coming from the `MergeRequestPresenter` - it 'includes a web_url' do - post_graphql(query, current_user: current_user) - - expect(merge_request_graphql_data['webUrl']).to be_present - end - - context 'when the user does not have access to the merge request' do - let(:project) { create(:project, :public, :repository) } - - it 'returns nil' do - project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE) - - post_graphql(query) - - expect(merge_request_graphql_data).to be_nil - end - end - end end context 'when the user does not have access to the project' do diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb index d23cbaf4beb..be6fa4c71a0 100644 --- a/spec/support/matchers/graphql_matchers.rb +++ b/spec/support/matchers/graphql_matchers.rb @@ -7,9 +7,24 @@ RSpec::Matchers.define :require_graphql_authorizations do |*expected| end RSpec::Matchers.define :have_graphql_fields do |*expected| + def expected_field_names + expected.map { |name| GraphqlHelpers.fieldnamerize(name) } + end + match do |kls| - field_names = expected.map { |name| GraphqlHelpers.fieldnamerize(name) } - expect(kls.fields.keys).to contain_exactly(*field_names) + expect(kls.fields.keys).to contain_exactly(*expected_field_names) + end + + failure_message do |kls| + missing = expected_field_names - kls.fields.keys + extra = kls.fields.keys - expected_field_names + + message = [] + + message << "is missing fields: <#{missing.inspect}>" if missing.any? + message << "contained unexpected fields: <#{extra.inspect}>" if extra.any? + + message.join("\n") end end @@ -44,3 +59,13 @@ RSpec::Matchers.define :have_graphql_resolver do |expected| end end end + +RSpec::Matchers.define :expose_permissions_using do |expected| + match do |type| + permission_field = type.fields['userPermissions'] + + expect(permission_field).not_to be_nil + expect(permission_field.type).to be_non_null + expect(permission_field.type.of_type.graphql_name).to eq(expected.graphql_name) + end +end diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb index 9b2b74593a5..fe7b7bc306f 100644 --- a/spec/support/shared_examples/requests/graphql_shared_examples.rb +++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb @@ -3,8 +3,8 @@ require 'spec_helper' shared_examples 'a working graphql query' do include GraphqlHelpers - it 'is returns a successfull response', :aggregate_failures do - expect(response).to be_success + it 'returns a successful response', :aggregate_failures do + expect(response).to have_gitlab_http_status(:success) expect(graphql_errors['errors']).to be_nil expect(json_response.keys).to include('data') end -- cgit v1.2.1 From fae42270c4cb4ac4c9b637dac00a585988e3151e Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 28 Jun 2018 13:24:54 +0000 Subject: Resolve "No UI element to edit group label" --- spec/features/groups/labels/index_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 spec/features/groups/labels/index_spec.rb (limited to 'spec') diff --git a/spec/features/groups/labels/index_spec.rb b/spec/features/groups/labels/index_spec.rb new file mode 100644 index 00000000000..6c1b43a9013 --- /dev/null +++ b/spec/features/groups/labels/index_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +feature 'Group labels' do + let(:user) { create(:user) } + let(:group) { create(:group) } + let!(:label) { create(:group_label, group: group) } + + background do + group.add_owner(user) + sign_in(user) + visit group_labels_path(group) + end + + scenario 'label has edit button', :js do + expect(page).to have_selector('.label-action.edit') + end +end -- cgit v1.2.1 From 904b6dd0834868ec260f40077623463926114373 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Wed, 13 Jun 2018 22:32:21 +0200 Subject: Don't hash user ID in OIDC subject claim --- spec/requests/openid_connect_spec.rb | 110 +++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 49 deletions(-) (limited to 'spec') diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index bcb8d6c2bfc..b14d4b8fb6e 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -1,11 +1,49 @@ require 'spec_helper' describe 'OpenID Connect requests' do - let(:user) { create :user } + let(:user) do + create( + :user, + name: 'Alice', + username: 'alice', + email: 'private@example.com', + emails: [public_email], + public_email: public_email.email, + website_url: 'https://example.com', + avatar: fixture_file_upload('spec/fixtures/dk.png') + ) + end + + let(:public_email) { build :email, email: 'public@example.com' } + let(:access_grant) { create :oauth_access_grant, application: application, resource_owner_id: user.id } let(:access_token) { create :oauth_access_token, application: application, resource_owner_id: user.id } - def request_access_token + let(:hashed_subject) do + Digest::SHA256.hexdigest("#{user.id}-#{Rails.application.secrets.secret_key_base}") + end + + let(:id_token_claims) do + { + 'sub' => user.id.to_s, + 'sub_legacy' => hashed_subject + } + end + + let(:user_info_claims) do + { + 'name' => 'Alice', + 'nickname' => 'alice', + 'email' => 'public@example.com', + 'email_verified' => true, + 'website' => 'https://example.com', + 'profile' => 'http://localhost/alice', + 'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png", + 'groups' => kind_of(Array) + } + end + + def request_access_token! login_as user post '/oauth/token', @@ -16,26 +54,22 @@ describe 'OpenID Connect requests' do client_secret: application.secret end - def request_user_info + def request_user_info! get '/oauth/userinfo', nil, 'Authorization' => "Bearer #{access_token.token}" end - def hashed_subject - Digest::SHA256.hexdigest("#{user.id}-#{Rails.application.secrets.secret_key_base}") - end - context 'Application without OpenID scope' do let(:application) { create :oauth_application, scopes: 'api' } it 'token response does not include an ID token' do - request_access_token + request_access_token! expect(json_response).to include 'access_token' expect(json_response).not_to include 'id_token' end it 'userinfo response is unauthorized' do - request_user_info + request_user_info! expect(response).to have_gitlab_http_status 403 expect(response.body).to be_blank @@ -46,28 +80,12 @@ describe 'OpenID Connect requests' do let(:application) { create :oauth_application, scopes: 'openid' } it 'token response includes an ID token' do - request_access_token + request_access_token! expect(json_response).to include 'id_token' end context 'UserInfo payload' do - let(:user) do - create( - :user, - name: 'Alice', - username: 'alice', - emails: [private_email, public_email], - email: private_email.email, - public_email: public_email.email, - website_url: 'https://example.com', - avatar: fixture_file_upload('spec/fixtures/dk.png') - ) - end - - let!(:public_email) { build :email, email: 'public@example.com' } - let!(:private_email) { build :email, email: 'private@example.com' } - let!(:group1) { create :group } let!(:group2) { create :group } let!(:group3) { create :group, parent: group2 } @@ -76,41 +94,35 @@ describe 'OpenID Connect requests' do before do group1.add_user(user, GroupMember::OWNER) group3.add_user(user, Gitlab::Access::DEVELOPER) + + request_user_info! end it 'includes all user information and group memberships' do - request_user_info - - expect(json_response).to match(a_hash_including({ - 'sub' => hashed_subject, - 'name' => 'Alice', - 'nickname' => 'alice', - 'email' => 'public@example.com', - 'email_verified' => true, - 'website' => 'https://example.com', - 'profile' => 'http://localhost/alice', - 'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png", - 'groups' => anything - })) + expect(json_response).to match(id_token_claims.merge(user_info_claims)) expected_groups = [group1.full_path, group3.full_path] expected_groups << group4.full_path if Group.supports_nested_groups? expect(json_response['groups']).to match_array(expected_groups) end + + it 'does not include any unknown claims' do + expect(json_response.keys).to eq %w[sub sub_legacy] + user_info_claims.keys + end end context 'ID token payload' do before do - request_access_token + request_access_token! @payload = JSON::JWT.decode(json_response['id_token'], :skip_verification) end - it 'includes the Gitlab root URL' do - expect(@payload['iss']).to eq Gitlab.config.gitlab.url + it 'includes the subject claims' do + expect(@payload).to match(a_hash_including(id_token_claims)) end - it 'includes the hashed user ID' do - expect(@payload['sub']).to eq hashed_subject + it 'includes the Gitlab root URL' do + expect(@payload['iss']).to eq Gitlab.config.gitlab.url end it 'includes the time of the last authentication', :clean_gitlab_redis_shared_state do @@ -118,7 +130,7 @@ describe 'OpenID Connect requests' do end it 'does not include any unknown properties' do - expect(@payload.keys).to eq %w[iss sub aud exp iat auth_time] + expect(@payload.keys).to eq %w[iss sub aud exp iat auth_time sub_legacy] end end @@ -134,10 +146,10 @@ describe 'OpenID Connect requests' do context 'when user is blocked' do it 'returns authentication error' do access_grant - user.block + user.block! expect do - request_access_token + request_access_token! end.to raise_error UncaughtThrowError end end @@ -145,10 +157,10 @@ describe 'OpenID Connect requests' do context 'when user is ldap_blocked' do it 'returns authentication error' do access_grant - user.ldap_block + user.ldap_block! expect do - request_access_token + request_access_token! end.to raise_error UncaughtThrowError end end -- cgit v1.2.1 From 31d8464f67f498fdba3dd1055463b647ad9ef767 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Mon, 25 Jun 2018 17:49:53 -0300 Subject: Schedule workers to delete non-latest diffs in post-migration --- .../background_migration/delete_diff_files_spec.rb | 69 ++++++++++++++++++++++ .../enqueue_delete_diff_files_workers_spec.rb | 48 +++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 spec/lib/gitlab/background_migration/delete_diff_files_spec.rb create mode 100644 spec/migrations/enqueue_delete_diff_files_workers_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb new file mode 100644 index 00000000000..a251ab323d8 --- /dev/null +++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do + describe '#perform' do + context 'when diff files can be deleted' do + let(:merge_request) { create(:merge_request, :merged) } + let(:merge_request_diff) do + merge_request.create_merge_request_diff + merge_request.merge_request_diffs.first + end + + it 'deletes all merge request diff files' do + expect { described_class.new.perform(merge_request_diff.id) } + .to change { merge_request_diff.merge_request_diff_files.count } + .from(20).to(0) + end + + it 'updates state to without_files' do + expect { described_class.new.perform(merge_request_diff.id) } + .to change { merge_request_diff.reload.state } + .from('collected').to('without_files') + end + + it 'rollsback if something goes wrong' do + expect(MergeRequestDiffFile).to receive_message_chain(:where, :delete_all) + .and_raise + + expect { described_class.new.perform(merge_request_diff.id) } + .to raise_error + + merge_request_diff.reload + + expect(merge_request_diff.state).to eq('collected') + expect(merge_request_diff.merge_request_diff_files.count).to eq(20) + end + end + + it 'deletes no merge request diff files when MR is not merged' do + merge_request = create(:merge_request, :opened) + merge_request.create_merge_request_diff + merge_request_diff = merge_request.merge_request_diffs.first + + expect { described_class.new.perform(merge_request_diff.id) } + .not_to change { merge_request_diff.merge_request_diff_files.count } + .from(20) + end + + it 'deletes no merge request diff files when diff is marked as "without_files"' do + merge_request = create(:merge_request, :merged) + merge_request.create_merge_request_diff + merge_request_diff = merge_request.merge_request_diffs.first + + merge_request_diff.clean! + + expect { described_class.new.perform(merge_request_diff.id) } + .not_to change { merge_request_diff.merge_request_diff_files.count } + .from(20) + end + + it 'deletes no merge request diff files when diff is the latest' do + merge_request = create(:merge_request, :merged) + merge_request_diff = merge_request.merge_request_diff + + expect { described_class.new.perform(merge_request_diff.id) } + .not_to change { merge_request_diff.merge_request_diff_files.count } + .from(20) + end + end +end diff --git a/spec/migrations/enqueue_delete_diff_files_workers_spec.rb b/spec/migrations/enqueue_delete_diff_files_workers_spec.rb new file mode 100644 index 00000000000..686027822b8 --- /dev/null +++ b/spec/migrations/enqueue_delete_diff_files_workers_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20180619121030_enqueue_delete_diff_files_workers.rb') + +describe EnqueueDeleteDiffFilesWorkers, :migration, :sidekiq do + let(:merge_request_diffs) { table(:merge_request_diffs) } + let(:merge_requests) { table(:merge_requests) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + + before do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + + namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab') + projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab') + + merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged') + + merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected') + merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'without_files') + merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'collected') + merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected') + merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'empty') + merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected') + + merge_requests.update(1, latest_merge_request_diff_id: 6) + end + + it 'correctly schedules diff file deletion workers' do + Sidekiq::Testing.fake! do + Timecop.freeze do + migrate! + + # 1st batch + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(8.minutes, 1) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(9.minutes, 3) + # 2nd batch + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(16.minutes, 4) + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(17.minutes, 6) + expect(BackgroundMigrationWorker.jobs.size).to eq(4) + end + end + end + + it 'migrates the data' do + expect { migrate! }.to change { merge_request_diffs.where(state: 'without_files').count } + .from(1).to(4) + end +end -- cgit v1.2.1 From c2b622b2a40bdf01f3f71a8adf9aff31d535149f Mon Sep 17 00:00:00 2001 From: Chantal Rollison Date: Tue, 26 Jun 2018 12:30:29 -0700 Subject: Add locked state to merge request API --- spec/finders/merge_requests_finder_spec.rb | 14 +++++++++++--- spec/requests/api/merge_requests_spec.rb | 15 ++++++++++++--- .../requests/api/merge_requests_list.rb | 18 +++++++++--------- 3 files changed, 32 insertions(+), 15 deletions(-) (limited to 'spec') diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index c8a43ddf410..669ec602f11 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -19,7 +19,7 @@ describe MergeRequestsFinder do let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) } let!(:merge_request2) { create(:merge_request, :conflict, author: user, source_project: project2, target_project: project1, state: 'closed') } - let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) } + let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked') } let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) } let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4) } @@ -35,7 +35,7 @@ describe MergeRequestsFinder do it 'filters by scope' do params = { scope: 'authored', state: 'opened' } merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(4) + expect(merge_requests.size).to eq(3) end it 'filters by project' do @@ -90,6 +90,14 @@ describe MergeRequestsFinder do expect(merge_requests).to contain_exactly(merge_request2) end + it 'filters by state' do + params = { state: 'locked' } + + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request3) + end + context 'filtering by group milestone' do let!(:group) { create(:group, :public) } let(:group_milestone) { create(:milestone, group: group) } @@ -199,7 +207,7 @@ describe MergeRequestsFinder do it 'returns the number of rows for the default state' do finder = described_class.new(user) - expect(finder.row_count).to eq(4) + expect(finder.row_count).to eq(3) end it 'returns the number of rows for a given state' do diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index d4ebfc3f782..eba39bb6ccc 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -14,6 +14,7 @@ describe API::MergeRequests do let!(:merge_request) { create(:merge_request, :simple, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) } let!(:merge_request_closed) { create(:merge_request, state: "closed", milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) } let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds, merge_commit_sha: '9999999999999999999999999999999999999999') } + let!(:merge_request_locked) { create(:merge_request, state: "locked", milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Locked test", created_at: base_time + 1.second) } let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") } let!(:label) do @@ -85,7 +86,7 @@ describe API::MergeRequests do get api('/merge_requests', user), scope: :all - expect_response_contain_exactly(merge_request2, merge_request_merged, merge_request_closed, merge_request) + expect_response_contain_exactly(merge_request2, merge_request_merged, merge_request_closed, merge_request, merge_request_locked) expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request3.id) end @@ -158,7 +159,7 @@ describe API::MergeRequests do it 'returns merge requests with the given source branch' do get api('/merge_requests', user), source_branch: merge_request_closed.source_branch, state: 'all' - expect_response_contain_exactly(merge_request_closed, merge_request_merged) + expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked) end end @@ -166,7 +167,7 @@ describe API::MergeRequests do it 'returns merge requests with the given target branch' do get api('/merge_requests', user), target_branch: merge_request_closed.target_branch, state: 'all' - expect_response_contain_exactly(merge_request_closed, merge_request_merged) + expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked) end end @@ -219,6 +220,14 @@ describe API::MergeRequests do expect_response_ordered_exactly(merge_request) end end + + context 'state param' do + it 'returns merge requests with the given state' do + get api('/merge_requests', user), state: 'locked' + + expect_response_contain_exactly(merge_request_locked) + end + end end end diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb index d5e22b8cb56..a401f7541f0 100644 --- a/spec/support/shared_examples/requests/api/merge_requests_list.rb +++ b/spec/support/shared_examples/requests/api/merge_requests_list.rb @@ -29,7 +29,7 @@ shared_examples 'merge requests list' do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect(json_response.length).to eq(4) expect(json_response.last['title']).to eq(merge_request.title) expect(json_response.last).to have_key('web_url') expect(json_response.last['sha']).to eq(merge_request.diff_head_sha) @@ -53,7 +53,7 @@ shared_examples 'merge requests list' do expect(response).to include_pagination_headers expect(json_response.last.keys).to match_array(%w(id iid title web_url created_at description project_id state updated_at)) expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect(json_response.length).to eq(4) expect(json_response.last['iid']).to eq(merge_request.iid) expect(json_response.last['title']).to eq(merge_request.title) expect(json_response.last).to have_key('web_url') @@ -70,7 +70,7 @@ shared_examples 'merge requests list' do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect(json_response.length).to eq(4) expect(json_response.last['title']).to eq(merge_request.title) end @@ -216,7 +216,7 @@ shared_examples 'merge requests list' do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect(json_response.length).to eq(4) response_dates = json_response.map { |merge_request| merge_request['created_at'] } expect(response_dates).to eq(response_dates.sort) end @@ -229,7 +229,7 @@ shared_examples 'merge requests list' do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect(json_response.length).to eq(4) response_dates = json_response.map { |merge_request| merge_request['created_at'] } expect(response_dates).to eq(response_dates.sort.reverse) end @@ -242,7 +242,7 @@ shared_examples 'merge requests list' do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect(json_response.length).to eq(4) response_dates = json_response.map { |merge_request| merge_request['updated_at'] } expect(response_dates).to eq(response_dates.sort.reverse) end @@ -255,7 +255,7 @@ shared_examples 'merge requests list' do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect(json_response.length).to eq(4) response_dates = json_response.map { |merge_request| merge_request['created_at'] } expect(response_dates).to eq(response_dates.sort) end @@ -265,7 +265,7 @@ shared_examples 'merge requests list' do it 'returns merge requests with the given source branch' do get api(endpoint_path, user), source_branch: merge_request_closed.source_branch, state: 'all' - expect_response_contain_exactly(merge_request_closed, merge_request_merged) + expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked) end end @@ -273,7 +273,7 @@ shared_examples 'merge requests list' do it 'returns merge requests with the given target branch' do get api(endpoint_path, user), target_branch: merge_request_closed.target_branch, state: 'all' - expect_response_contain_exactly(merge_request_closed, merge_request_merged) + expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked) end end end -- cgit v1.2.1 From 26a8472d6d0aa1eb40285105a0f55f2f7d439897 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Thu, 28 Jun 2018 22:49:54 +0200 Subject: Don't add bottom 'match' line for deleted files If a file is deleted, its new_pos is 0 (less than total_blob_lines), but there is no reason to add the bottom 'match' line in this case because there is not extra content which could be expanded. --- spec/lib/gitlab/diff/file_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'spec') diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index 5dfbb8e71f8..ebeb05d6e02 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -26,6 +26,21 @@ describe Gitlab::Diff::File do end end + describe '#diff_lines_for_serializer' do + it 'includes bottom match line if not in the end' do + expect(diff_file.diff_lines_for_serializer.last.type).to eq('match') + end + + context 'when deleted' do + let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') } + let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') } + + it 'does not include bottom match line' do + expect(diff_file.diff_lines_for_serializer.last.type).not_to eq('match') + end + end + end + describe '#mode_changed?' do it { expect(diff_file.mode_changed?).to be_falsey } end -- cgit v1.2.1 From 34dbccb24b38c5a7c52641e7008f3ab53b25c66a Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 28 Jun 2018 15:51:36 -0300 Subject: Add helper methods to stub Gitlab::ExclusiveLease --- spec/lib/mattermost/session_spec.rb | 20 +++----- spec/models/ci/build_trace_chunk_spec.rb | 4 +- spec/models/concerns/reactive_caching_spec.rb | 11 ++-- .../check_ingress_ip_address_service_spec.rb | 17 +++--- .../refresh_authorized_projects_service_spec.rb | 10 ++-- spec/support/helpers/exclusive_lease_helpers.rb | 36 +++++++++++++ .../shared_examples/ci_trace_shared_examples.rb | 4 +- .../uploaders/object_storage_shared_examples.rb | 8 ++- spec/workers/project_cache_worker_spec.rb | 15 +++--- .../project_migrate_hashed_storage_worker_spec.rb | 60 ++++++++++------------ .../propagate_service_template_worker_spec.rb | 38 +++++++------- .../repository_remove_remote_worker_spec.rb | 57 +++++++++++--------- spec/workers/stuck_ci_jobs_worker_spec.rb | 38 ++++++++++---- 13 files changed, 183 insertions(+), 135 deletions(-) create mode 100644 spec/support/helpers/exclusive_lease_helpers.rb (limited to 'spec') diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb index 5410bfbeb31..b7687d48c68 100644 --- a/spec/lib/mattermost/session_spec.rb +++ b/spec/lib/mattermost/session_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Mattermost::Session, type: :request do + include ExclusiveLeaseHelpers + let(:user) { create(:user) } let(:gitlab_url) { "http://gitlab.com" } @@ -97,26 +99,20 @@ describe Mattermost::Session, type: :request do end end - context 'with lease' do - before do - allow(subject).to receive(:lease_try_obtain).and_return('aldkfjsldfk') - end + context 'exclusive lease' do + let(:lease_key) { 'mattermost:session' } it 'tries to obtain a lease' do - expect(subject).to receive(:lease_try_obtain) - expect(Gitlab::ExclusiveLease).to receive(:cancel) + expect_to_obtain_exclusive_lease(lease_key, 'uuid') + expect_to_cancel_exclusive_lease(lease_key, 'uuid') # Cannot setup a session, but we should still cancel the lease expect { subject.with_session }.to raise_error(Mattermost::NoSessionError) end - end - context 'without lease' do - before do - allow(subject).to receive(:lease_try_obtain).and_return(nil) - end + it 'returns a NoSessionError error without lease' do + stub_exclusive_lease_taken(lease_key) - it 'returns a NoSessionError error' do expect { subject.with_session }.to raise_error(Mattermost::NoSessionError) end end diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index c5d550cba1b..464897de306 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do + include ExclusiveLeaseHelpers + set(:build) { create(:ci_build, :running) } let(:chunk_index) { 0 } let(:data_store) { :redis } @@ -322,7 +324,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do describe 'ExclusiveLock' do before do - allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) { nil } + stub_exclusive_lease_taken stub_const('Ci::BuildTraceChunk::WRITE_LOCK_RETRY', 1) end diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index f2a3df50c1a..0f156619e9e 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe ReactiveCaching, :use_clean_rails_memory_store_caching do + include ExclusiveLeaseHelpers include ReactiveCachingHelpers class CacheTest @@ -106,8 +107,8 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do end it 'takes and releases the lease' do - expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return("000000") - expect(Gitlab::ExclusiveLease).to receive(:cancel).with(cache_key, "000000") + expect_to_obtain_exclusive_lease(cache_key, 'uuid') + expect_to_cancel_exclusive_lease(cache_key, 'uuid') go! end @@ -153,11 +154,9 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do end context 'when the lease is already taken' do - before do - expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(nil) - end - it 'skips the calculation' do + stub_exclusive_lease_taken(cache_key) + expect(instance).to receive(:calculate_reactive_cache).never go! diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb index bf038595a4d..eb0bdb61ee3 100644 --- a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb +++ b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb @@ -1,11 +1,13 @@ require 'spec_helper' describe Clusters::Applications::CheckIngressIpAddressService do + include ExclusiveLeaseHelpers + let(:application) { create(:clusters_applications_ingress, :installed) } let(:service) { described_class.new(application) } let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) } let(:ingress) { [{ ip: '111.222.111.222' }] } - let(:exclusive_lease) { instance_double(Gitlab::ExclusiveLease, try_obtain: true) } + let(:lease_key) { "check_ingress_ip_address_service:#{application.id}" } let(:kube_service) do ::Kubeclient::Resource.new( @@ -22,11 +24,8 @@ describe Clusters::Applications::CheckIngressIpAddressService do subject { service.execute } before do + stub_exclusive_lease(lease_key, timeout: 15.seconds.to_i) allow(application.cluster).to receive(:kubeclient).and_return(kubeclient) - allow(Gitlab::ExclusiveLease) - .to receive(:new) - .with("check_ingress_ip_address_service:#{application.id}", timeout: 15.seconds.to_i) - .and_return(exclusive_lease) end describe '#execute' do @@ -47,13 +46,9 @@ describe Clusters::Applications::CheckIngressIpAddressService do end context 'when the exclusive lease cannot be obtained' do - before do - allow(exclusive_lease) - .to receive(:try_obtain) - .and_return(false) - end - it 'does not call kubeclient' do + stub_exclusive_lease_taken(lease_key, timeout: 15.seconds.to_i) + subject expect(kubeclient).not_to have_received(:get_service) diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb index 08fd26d67fd..e5fde07a6eb 100644 --- a/spec/services/users/refresh_authorized_projects_service_spec.rb +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Users::RefreshAuthorizedProjectsService do + include ExclusiveLeaseHelpers + # We're using let! here so that any expectations for the service class are not # triggered twice. let!(:project) { create(:project) } @@ -10,12 +12,10 @@ describe Users::RefreshAuthorizedProjectsService do describe '#execute', :clean_gitlab_redis_shared_state do it 'refreshes the authorizations using a lease' do - expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) - .and_return('foo') - - expect(Gitlab::ExclusiveLease).to receive(:cancel) - .with(an_instance_of(String), 'foo') + lease_key = "refresh_authorized_projects:#{user.id}" + expect_to_obtain_exclusive_lease(lease_key, 'uuid') + expect_to_cancel_exclusive_lease(lease_key, 'uuid') expect(service).to receive(:execute_without_lease) service.execute diff --git a/spec/support/helpers/exclusive_lease_helpers.rb b/spec/support/helpers/exclusive_lease_helpers.rb new file mode 100644 index 00000000000..383cc7dee81 --- /dev/null +++ b/spec/support/helpers/exclusive_lease_helpers.rb @@ -0,0 +1,36 @@ +module ExclusiveLeaseHelpers + def stub_exclusive_lease(key = nil, uuid = 'uuid', renew: false, timeout: nil) + key ||= instance_of(String) + timeout ||= instance_of(Integer) + + lease = instance_double( + Gitlab::ExclusiveLease, + try_obtain: uuid, + exists?: true, + renew: renew + ) + + allow(Gitlab::ExclusiveLease) + .to receive(:new) + .with(key, timeout: timeout) + .and_return(lease) + + lease + end + + def stub_exclusive_lease_taken(key = nil, timeout: nil) + stub_exclusive_lease(key, nil, timeout: timeout) + end + + def expect_to_obtain_exclusive_lease(key, uuid = 'uuid', timeout: nil) + lease = stub_exclusive_lease(key, uuid, timeout: timeout) + + expect(lease).to receive(:try_obtain) + end + + def expect_to_cancel_exclusive_lease(key, uuid) + expect(Gitlab::ExclusiveLease) + .to receive(:cancel) + .with(key, uuid) + end +end diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index 6dbe0f6f980..db723a323f8 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -247,8 +247,10 @@ shared_examples_for 'common trace features' do end context 'when another process has already been archiving', :clean_gitlab_redis_shared_state do + include ExclusiveLeaseHelpers + before do - Gitlab::ExclusiveLease.new("trace:archive:#{trace.job.id}", timeout: 1.hour).try_obtain + stub_exclusive_lease_taken("trace:archive:#{trace.job.id}", timeout: 1.hour) end it 'blocks concurrent archiving' do diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb index 19800c6638f..1bd176280c5 100644 --- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb +++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb @@ -76,8 +76,10 @@ shared_examples "migrates" do |to_store:, from_store: nil| end context 'when migrate! is occupied by another process' do + include ExclusiveLeaseHelpers + before do - @uuid = Gitlab::ExclusiveLease.new(subject.exclusive_lease_key, timeout: 1.hour.to_i).try_obtain + stub_exclusive_lease_taken(subject.exclusive_lease_key, timeout: 1.hour.to_i) end it 'does not execute migrate!' do @@ -91,10 +93,6 @@ shared_examples "migrates" do |to_store:, from_store: nil| expect { subject.use_file }.to raise_error(ObjectStorage::ExclusiveLeaseTaken) end - - after do - Gitlab::ExclusiveLease.cancel(subject.exclusive_lease_key, @uuid) - end end context 'migration is unsuccessful' do diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index 6b1f2ff3227..b9b5445562f 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -1,14 +1,17 @@ require 'spec_helper' describe ProjectCacheWorker do + include ExclusiveLeaseHelpers + let(:worker) { described_class.new } let(:project) { create(:project, :repository) } let(:statistics) { project.statistics } + let(:lease_key) { "project_cache_worker:#{project.id}:update_statistics" } + let(:lease_timeout) { ProjectCacheWorker::LEASE_TIMEOUT } describe '#perform' do before do - allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) - .and_return(true) + stub_exclusive_lease(lease_key, timeout: lease_timeout) end context 'with a non-existing project' do @@ -63,9 +66,7 @@ describe ProjectCacheWorker do describe '#update_statistics' do context 'when a lease could not be obtained' do it 'does not update the repository size' do - allow(worker).to receive(:try_obtain_lease_for) - .with(project.id, :update_statistics) - .and_return(false) + stub_exclusive_lease_taken(lease_key, timeout: lease_timeout) expect(statistics).not_to receive(:refresh!) @@ -75,9 +76,7 @@ describe ProjectCacheWorker do context 'when a lease could be obtained' do it 'updates the project statistics' do - allow(worker).to receive(:try_obtain_lease_for) - .with(project.id, :update_statistics) - .and_return(true) + stub_exclusive_lease(lease_key, timeout: lease_timeout) expect(statistics).to receive(:refresh!) .with(only: %i(repository_size)) diff --git a/spec/workers/project_migrate_hashed_storage_worker_spec.rb b/spec/workers/project_migrate_hashed_storage_worker_spec.rb index 2e3951e7afc..9551e358af1 100644 --- a/spec/workers/project_migrate_hashed_storage_worker_spec.rb +++ b/spec/workers/project_migrate_hashed_storage_worker_spec.rb @@ -1,53 +1,47 @@ require 'spec_helper' describe ProjectMigrateHashedStorageWorker, :clean_gitlab_redis_shared_state do + include ExclusiveLeaseHelpers + describe '#perform' do let(:project) { create(:project, :empty_repo) } - let(:pending_delete_project) { create(:project, :empty_repo, pending_delete: true) } + let(:lease_key) { "project_migrate_hashed_storage_worker:#{project.id}" } + let(:lease_timeout) { ProjectMigrateHashedStorageWorker::LEASE_TIMEOUT } + + it 'skips when project no longer exists' do + expect(::Projects::HashedStorageMigrationService).not_to receive(:new) + + subject.perform(-1) + end - context 'when have exclusive lease' do - before do - lease = subject.lease_for(project.id) + it 'skips when project is pending delete' do + pending_delete_project = create(:project, :empty_repo, pending_delete: true) - allow(Gitlab::ExclusiveLease).to receive(:new).and_return(lease) - allow(lease).to receive(:try_obtain).and_return(true) - end + expect(::Projects::HashedStorageMigrationService).not_to receive(:new) - it 'skips when project no longer exists' do - nonexistent_id = 999999999999 + subject.perform(pending_delete_project.id) + end - expect(::Projects::HashedStorageMigrationService).not_to receive(:new) - subject.perform(nonexistent_id) - end + it 'delegates removal to service class when have exclusive lease' do + stub_exclusive_lease(lease_key, 'uuid', timeout: lease_timeout) - it 'skips when project is pending delete' do - expect(::Projects::HashedStorageMigrationService).not_to receive(:new) + migration_service = spy - subject.perform(pending_delete_project.id) - end + allow(::Projects::HashedStorageMigrationService) + .to receive(:new).with(project, subject.logger) + .and_return(migration_service) - it 'delegates removal to service class' do - service = double('service') - expect(::Projects::HashedStorageMigrationService).to receive(:new).with(project, subject.logger).and_return(service) - expect(service).to receive(:execute) + subject.perform(project.id) - subject.perform(project.id) - end + expect(migration_service).to have_received(:execute) end - context 'when dont have exclusive lease' do - before do - lease = subject.lease_for(project.id) - - allow(Gitlab::ExclusiveLease).to receive(:new).and_return(lease) - allow(lease).to receive(:try_obtain).and_return(false) - end + it 'skips when dont have lease when dont have exclusive lease' do + stub_exclusive_lease_taken(lease_key, timeout: lease_timeout) - it 'skips when dont have lease' do - expect(::Projects::HashedStorageMigrationService).not_to receive(:new) + expect(::Projects::HashedStorageMigrationService).not_to receive(:new) - subject.perform(project.id) - end + subject.perform(project.id) end end end diff --git a/spec/workers/propagate_service_template_worker_spec.rb b/spec/workers/propagate_service_template_worker_spec.rb index b8b65ead9b3..af1fb80a51d 100644 --- a/spec/workers/propagate_service_template_worker_spec.rb +++ b/spec/workers/propagate_service_template_worker_spec.rb @@ -1,29 +1,29 @@ require 'spec_helper' describe PropagateServiceTemplateWorker do - let!(:service_template) do - PushoverService.create( - template: true, - active: true, - properties: { - device: 'MyDevice', - sound: 'mic', - priority: 4, - user_key: 'asdf', - api_key: '123456789' - }) - end - - before do - allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) - .and_return(true) - end + include ExclusiveLeaseHelpers describe '#perform' do it 'calls the propagate service with the template' do - expect(Projects::PropagateServiceTemplate).to receive(:propagate).with(service_template) + template = PushoverService.create( + template: true, + active: true, + properties: { + device: 'MyDevice', + sound: 'mic', + priority: 4, + user_key: 'asdf', + api_key: '123456789' + }) + + stub_exclusive_lease("propagate_service_template_worker:#{template.id}", + timeout: PropagateServiceTemplateWorker::LEASE_TIMEOUT) + + expect(Projects::PropagateServiceTemplate) + .to receive(:propagate) + .with(template) - subject.perform(service_template.id) + subject.perform(template.id) end end end diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb index 5968c5da3c9..a653f6f926c 100644 --- a/spec/workers/repository_remove_remote_worker_spec.rb +++ b/spec/workers/repository_remove_remote_worker_spec.rb @@ -1,44 +1,50 @@ require 'rails_helper' describe RepositoryRemoveRemoteWorker do - subject(:worker) { described_class.new } + include ExclusiveLeaseHelpers describe '#perform' do - let(:remote_name) { 'joe'} let!(:project) { create(:project, :repository) } + let(:remote_name) { 'joe'} + let(:lease_key) { "remove_remote_#{project.id}_#{remote_name}" } + let(:lease_timeout) { RepositoryRemoveRemoteWorker::LEASE_TIMEOUT } - context 'when it cannot obtain lease' do - it 'logs error' do - allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) { nil } - - expect_any_instance_of(Repository).not_to receive(:remove_remote) - expect(worker).to receive(:log_error).with('Cannot obtain an exclusive lease. There must be another instance already in execution.') - - worker.perform(project.id, remote_name) - end + it 'returns nil when project does not exist' do + expect(subject.perform(-1, 'remote_name')).to be_nil end - context 'when it gets the lease' do + context 'when project exists' do before do - allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(true) + allow(Project) + .to receive(:find_by) + .with(id: project.id) + .and_return(project) end - context 'when project does not exist' do - it 'returns nil' do - expect(worker.perform(-1, 'remote_name')).to be_nil - end - end + it 'does not remove remote when cannot obtain lease' do + stub_exclusive_lease_taken(lease_key, timeout: lease_timeout) + + expect(project.repository) + .not_to receive(:remove_remote) - context 'when project exists' do - it 'removes remote from repository' do - masterrev = project.repository.find_branch('master').dereferenced_target + expect(subject) + .to receive(:log_error) + .with('Cannot obtain an exclusive lease. There must be another instance already in execution.') - create_remote_branch(remote_name, 'remote_branch', masterrev) + subject.perform(project.id, remote_name) + end - expect_any_instance_of(Repository).to receive(:remove_remote).with(remote_name).and_call_original + it 'removes remote from repository when obtain a lease' do + stub_exclusive_lease(lease_key, timeout: lease_timeout) + masterrev = project.repository.find_branch('master').dereferenced_target + create_remote_branch(remote_name, 'remote_branch', masterrev) - worker.perform(project.id, remote_name) - end + expect(project.repository) + .to receive(:remove_remote) + .with(remote_name) + .and_call_original + + subject.perform(project.id, remote_name) end end end @@ -47,6 +53,7 @@ describe RepositoryRemoveRemoteWorker do rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do project.repository.rugged end + rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) end end diff --git a/spec/workers/stuck_ci_jobs_worker_spec.rb b/spec/workers/stuck_ci_jobs_worker_spec.rb index 2605c14334f..856886e3df5 100644 --- a/spec/workers/stuck_ci_jobs_worker_spec.rb +++ b/spec/workers/stuck_ci_jobs_worker_spec.rb @@ -1,14 +1,21 @@ require 'spec_helper' describe StuckCiJobsWorker do + include ExclusiveLeaseHelpers + let!(:runner) { create :ci_runner } let!(:job) { create :ci_build, runner: runner } - let(:worker) { described_class.new } - let(:exclusive_lease_uuid) { SecureRandom.uuid } + let(:trace_lease_key) { "trace:archive:#{job.id}" } + let(:trace_lease_uuid) { SecureRandom.uuid } + let(:worker_lease_key) { StuckCiJobsWorker::EXCLUSIVE_LEASE_KEY } + let(:worker_lease_uuid) { SecureRandom.uuid } + + subject(:worker) { described_class.new } before do + stub_exclusive_lease(worker_lease_key, worker_lease_uuid) + stub_exclusive_lease(trace_lease_key, trace_lease_uuid) job.update!(status: status, updated_at: updated_at) - allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(exclusive_lease_uuid) end shared_examples 'job is dropped' do @@ -44,16 +51,19 @@ describe StuckCiJobsWorker do context 'when job was not updated for more than 1 day ago' do let(:updated_at) { 2.days.ago } + it_behaves_like 'job is dropped' end context 'when job was updated in less than 1 day ago' do let(:updated_at) { 6.hours.ago } + it_behaves_like 'job is unchanged' end context 'when job was not updated for more than 1 hour ago' do let(:updated_at) { 2.hours.ago } + it_behaves_like 'job is unchanged' end end @@ -65,11 +75,14 @@ describe StuckCiJobsWorker do context 'when job was not updated for more than 1 hour ago' do let(:updated_at) { 2.hours.ago } + it_behaves_like 'job is dropped' end - context 'when job was updated in less than 1 hour ago' do + context 'when job was updated in less than 1 + hour ago' do let(:updated_at) { 30.minutes.ago } + it_behaves_like 'job is unchanged' end end @@ -80,11 +93,13 @@ describe StuckCiJobsWorker do context 'when job was not updated for more than 1 hour ago' do let(:updated_at) { 2.hours.ago } + it_behaves_like 'job is dropped' end context 'when job was updated in less than 1 hour ago' do let(:updated_at) { 30.minutes.ago } + it_behaves_like 'job is unchanged' end end @@ -93,6 +108,7 @@ describe StuckCiJobsWorker do context "when job is #{status}" do let(:status) { status } let(:updated_at) { 2.days.ago } + it_behaves_like 'job is unchanged' end end @@ -119,23 +135,27 @@ describe StuckCiJobsWorker do it 'is guard by exclusive lease when executed concurrently' do expect(worker).to receive(:drop).at_least(:once).and_call_original expect(worker2).not_to receive(:drop) + worker.perform - allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(false) + + stub_exclusive_lease_taken(worker_lease_key) + worker2.perform end it 'can be executed in sequence' do expect(worker).to receive(:drop).at_least(:once).and_call_original expect(worker2).to receive(:drop).at_least(:once).and_call_original + worker.perform worker2.perform end - it 'cancels exclusive lease after worker perform' do - worker.perform + it 'cancels exclusive leases after worker perform' do + expect_to_cancel_exclusive_lease(trace_lease_key, trace_lease_uuid) + expect_to_cancel_exclusive_lease(worker_lease_key, worker_lease_uuid) - expect(Gitlab::ExclusiveLease.new(described_class::EXCLUSIVE_LEASE_KEY, timeout: 1.hour)) - .not_to be_exists + worker.perform end end end -- cgit v1.2.1 From d690cd9992dbd6a0d231f6c7ea1688ef90f9fc2e Mon Sep 17 00:00:00 2001 From: Fatih Acet Date: Fri, 29 Jun 2018 07:22:07 +0000 Subject: Prevent fetching diffs and discussions data unnecessarily on MR page --- spec/javascripts/diffs/store/actions_spec.js | 17 ----------------- spec/javascripts/notes/stores/actions_spec.js | 13 +++++++++++++ spec/javascripts/notes/stores/getters_spec.js | 7 +++++++ spec/javascripts/notes/stores/mutation_spec.js | 11 +++++++++++ 4 files changed, 31 insertions(+), 17 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js index f0bd098f698..6829c1e956a 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -5,7 +5,6 @@ import { INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE, } from '~/diffs/constants'; -import store from '~/diffs/store'; import * as actions from '~/diffs/store/actions'; import * as types from '~/diffs/store/mutation_types'; import axios from '~/lib/utils/axios_utils'; @@ -28,22 +27,6 @@ describe('DiffsStoreActions', () => { }); }); - describe('setLoadingState', () => { - it('should set loading state', done => { - expect(store.state.diffs.isLoading).toEqual(true); - const loadingState = false; - - testAction( - actions.setLoadingState, - loadingState, - {}, - [{ type: types.SET_LOADING, payload: loadingState }], - [], - done, - ); - }); - }); - describe('fetchDiffFiles', () => { it('should fetch diff files', done => { const endpoint = '/fetch/diff/files'; diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js index 985c2f81ef3..71ef3aa9b03 100644 --- a/spec/javascripts/notes/stores/actions_spec.js +++ b/spec/javascripts/notes/stores/actions_spec.js @@ -291,4 +291,17 @@ describe('Actions Notes Store', () => { .catch(done.fail); }); }); + + describe('setNotesFetchedState', () => { + it('should set notes fetched state', done => { + testAction( + actions.setNotesFetchedState, + true, + {}, + [{ type: 'SET_NOTES_FETCHED_STATE', payload: true }], + [], + done, + ); + }); + }); }); diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js index 5501e50e97b..815cc09621f 100644 --- a/spec/javascripts/notes/stores/getters_spec.js +++ b/spec/javascripts/notes/stores/getters_spec.js @@ -15,6 +15,7 @@ describe('Getters Notes Store', () => { discussions: [individualNote], targetNoteHash: 'hash', lastFetchedAt: 'timestamp', + isNotesFetched: false, notesData: notesDataMock, userData: userDataMock, @@ -84,4 +85,10 @@ describe('Getters Notes Store', () => { expect(getters.openState(state)).toEqual(noteableDataMock.state); }); }); + + describe('isNotesFetched', () => { + it('should return the state for the fetching notes', () => { + expect(getters.isNotesFetched(state)).toBeFalsy(); + }); + }); }); diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js index 556a1c244c0..ccc7328447b 100644 --- a/spec/javascripts/notes/stores/mutation_spec.js +++ b/spec/javascripts/notes/stores/mutation_spec.js @@ -318,4 +318,15 @@ describe('Notes Store mutations', () => { expect(state.isToggleStateButtonLoading).toEqual(false); }); }); + + describe('SET_NOTES_FETCHING_STATE', () => { + it('should set the given state', () => { + const state = { + isNotesFetched: false, + }; + + mutations.SET_NOTES_FETCHED_STATE(state, true); + expect(state.isNotesFetched).toEqual(true); + }); + }); }); -- cgit v1.2.1 From 7a0bb214f33bf25af7e5a53981f93b95ac6a4235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Fri, 29 Jun 2018 08:15:42 +0000 Subject: Fix OAuth application authorization screen to appear with every access --- .../oauth/authorizations_controller_spec.rb | 34 +++++++++++++++------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'spec') diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index 149b690ff70..8c10ea53a7a 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -2,19 +2,12 @@ require 'spec_helper' describe Oauth::AuthorizationsController do let(:user) { create(:user) } - - let(:doorkeeper) do - Doorkeeper::Application.create( - name: "MyApp", - redirect_uri: 'http://example.com', - scopes: "") - end - + let!(:application) { create(:oauth_application, scopes: 'api read_user', redirect_uri: 'http://example.com') } let(:params) do { response_type: "code", - client_id: doorkeeper.uid, - redirect_uri: doorkeeper.redirect_uri, + client_id: application.uid, + redirect_uri: application.redirect_uri, state: 'state' } end @@ -44,7 +37,7 @@ describe Oauth::AuthorizationsController do end it 'deletes session.user_return_to and redirects when skip authorization' do - doorkeeper.update(trusted: true) + application.update(trusted: true) request.session['user_return_to'] = 'http://example.com' get :new, params @@ -52,6 +45,25 @@ describe Oauth::AuthorizationsController do expect(request.session['user_return_to']).to be_nil expect(response).to have_gitlab_http_status(302) end + + context 'when there is already an access token for the application' do + context 'when the request scope matches any of the created token scopes' do + before do + scopes = Doorkeeper::OAuth::Scopes.from_string('api') + + allow(Doorkeeper.configuration).to receive(:scopes).and_return(scopes) + + create :oauth_access_token, application: application, resource_owner_id: user.id, scopes: scopes + end + + it 'authorizes the request and redirects' do + get :new, params + + expect(request.session['user_return_to']).to be_nil + expect(response).to have_gitlab_http_status(302) + end + end + end end end end -- cgit v1.2.1 From 7a6da82a83c4e183da42c2639d58fc6893735d1f Mon Sep 17 00:00:00 2001 From: Sam Beckham Date: Fri, 29 Jun 2018 08:26:56 +0000 Subject: Resolve "Remove "environment scope" field on cluster creation form for core/starter plans" --- spec/features/projects/clusters/gcp_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'spec') diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index c85b82b2090..3db384e5b65 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -157,6 +157,19 @@ feature 'Gcp Cluster', :js do end end + context 'when a user cannot edit the environment scope' do + before do + visit project_clusters_path(project) + + click_link 'Add Kubernetes cluster' + click_link 'Add an existing Kubernetes cluster' + end + + it 'user does not see the "Environment scope" field' do + expect(page).not_to have_css('#cluster_environment_scope') + end + end + context 'when user has not dismissed GCP signup offer' do before do visit project_clusters_path(project) -- cgit v1.2.1 From bfc7ef1eecd4a031e8d4d027489fa77ea59a7506 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Jun 2018 12:15:07 +0100 Subject: Improve error messaging across various IDE actions --- spec/javascripts/ide/stores/actions/tree_spec.js | 68 ------------------------ 1 file changed, 68 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index 612a439cfbf..c5b0c002b15 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -168,72 +168,4 @@ describe('Multi-file store tree actions', () => { ); }); }); - - describe('getLastCommitData', () => { - beforeEach(() => { - spyOn(service, 'getTreeLastCommit').and.returnValue( - Promise.resolve({ - headers: { - 'more-logs-url': null, - }, - json: () => - Promise.resolve([ - { - type: 'tree', - file_name: 'testing', - commit: { - message: 'commit message', - authored_date: '123', - }, - }, - ]), - }), - ); - - store.state.trees['abcproject/mybranch'] = { - tree: [], - }; - - projectTree = store.state.trees['abcproject/mybranch']; - projectTree.tree.push(file('testing', '1', 'tree')); - projectTree.lastCommitPath = 'lastcommitpath'; - }); - - it('calls service with lastCommitPath', done => { - store - .dispatch('getLastCommitData', projectTree) - .then(() => { - expect(service.getTreeLastCommit).toHaveBeenCalledWith('lastcommitpath'); - - done(); - }) - .catch(done.fail); - }); - - it('updates trees last commit data', done => { - store - .dispatch('getLastCommitData', projectTree) - .then(Vue.nextTick) - .then(() => { - expect(projectTree.tree[0].lastCommit.message).toBe('commit message'); - - done(); - }) - .catch(done.fail); - }); - - it('does not update entry if not found', done => { - projectTree.tree[0].name = 'a'; - - store - .dispatch('getLastCommitData', projectTree) - .then(Vue.nextTick) - .then(() => { - expect(projectTree.tree[0].lastCommit.message).not.toBe('commit message'); - - done(); - }) - .catch(done.fail); - }); - }); }); -- cgit v1.2.1 From 8b687e07d7a87c7cab2b5d8e5b04a5c705a4eb5e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Jun 2018 12:36:06 +0100 Subject: :white_check_mark: fixed eslint --- spec/javascripts/ide/stores/actions/tree_spec.js | 1 - 1 file changed, 1 deletion(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index c5b0c002b15..3ef2a2da880 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -1,5 +1,4 @@ import MockAdapter from 'axios-mock-adapter'; -import Vue from 'vue'; import testAction from 'spec/helpers/vuex_action_helper'; import { showTreeEntry, getFiles } from '~/ide/stores/actions/tree'; import * as types from '~/ide/stores/mutation_types'; -- cgit v1.2.1 From d255fd4f4650107cef5326db72064588eabc4557 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Jun 2018 12:41:53 +0100 Subject: fixed error_message_spec --- spec/javascripts/ide/components/error_message_spec.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/error_message_spec.js b/spec/javascripts/ide/components/error_message_spec.js index 70d13a6ceb2..430e8e2baa3 100644 --- a/spec/javascripts/ide/components/error_message_spec.js +++ b/spec/javascripts/ide/components/error_message_spec.js @@ -36,13 +36,15 @@ describe('IDE error message component', () => { }); describe('with action', () => { + let actionSpy; + beforeEach(done => { - vm.message.action = 'testAction'; + actionSpy = jasmine.createSpy('action').and.returnValue(Promise.resolve()); + + vm.message.action = actionSpy; vm.message.actionText = 'test action'; vm.message.actionPayload = 'testActionPayload'; - spyOn(vm.$store, 'dispatch').and.returnValue(Promise.resolve()); - vm.$nextTick(done); }); @@ -63,7 +65,7 @@ describe('IDE error message component', () => { vm.$el.querySelector('.flash-action').click(); vm.$nextTick(() => { - expect(vm.$store.dispatch).toHaveBeenCalledWith('testAction', 'testActionPayload'); + expect(actionSpy).toHaveBeenCalledWith('testActionPayload'); done(); }); @@ -74,7 +76,7 @@ describe('IDE error message component', () => { vm.$el.querySelector('.flash-action').click(); - expect(vm.$store.dispatch).not.toHaveBeenCalledWith(); + expect(actionSpy).not.toHaveBeenCalledWith(); }); it('resets isLoading after click', done => { -- cgit v1.2.1 From fa8c82c7ec6bb380c1b90421e99b6e8cdee3d790 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Jun 2018 15:09:21 +0100 Subject: :white_check_mark: added specs for file actions in this it also removes Vue Resource from the IDE, axios is nicer to test --- .../ide/components/repo_commit_section_spec.js | 17 -- spec/javascripts/ide/stores/actions/file_spec.js | 286 +++++++++++++-------- .../javascripts/ide/stores/actions/project_spec.js | 2 +- 3 files changed, 186 insertions(+), 119 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js index 6bf309fb4bf..8df48d8aeae 100644 --- a/spec/javascripts/ide/components/repo_commit_section_spec.js +++ b/spec/javascripts/ide/components/repo_commit_section_spec.js @@ -68,23 +68,6 @@ describe('RepoCommitSection', () => { vm.$mount(); - spyOn(service, 'getTreeData').and.returnValue( - Promise.resolve({ - headers: { - 'page-title': 'test', - }, - json: () => - Promise.resolve({ - last_commit_path: 'last_commit_path', - parent_tree_url: 'parent_tree_url', - path: '/', - trees: [{ name: 'tree' }], - blobs: [{ name: 'blob' }], - submodules: [{ name: 'submodule' }], - }), - }), - ); - Vue.nextTick(done); }); diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index 5746683917e..36f21f22d62 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -1,5 +1,8 @@ import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import store from '~/ide/stores'; +import { getFileData, getRawFileData } from '~/ide/stores/actions/file'; import * as actions from '~/ide/stores/actions/file'; import * as types from '~/ide/stores/mutation_types'; import service from '~/ide/services'; @@ -9,11 +12,16 @@ import { file, resetStore } from '../../helpers'; import testAction from '../../../helpers/vuex_action_helper'; describe('IDE store file actions', () => { + let mock; + beforeEach(() => { + mock = new MockAdapter(axios); + spyOn(router, 'push'); }); afterEach(() => { + mock.restore(); resetStore(store); }); @@ -183,94 +191,131 @@ describe('IDE store file actions', () => { let localFile; beforeEach(() => { - spyOn(service, 'getFileData').and.returnValue( - Promise.resolve({ - headers: { - 'page-title': 'testing getFileData', - }, - json: () => - Promise.resolve({ - blame_path: 'blame_path', - commits_path: 'commits_path', - permalink: 'permalink', - raw_path: 'raw_path', - binary: false, - html: '123', - render_error: '', - }), - }), - ); + spyOn(service, 'getFileData').and.callThrough(); localFile = file(`newCreate-${Math.random()}`); - localFile.url = 'getFileDataURL'; + localFile.url = `${gl.TEST_HOST}/getFileDataURL`; store.state.entries[localFile.path] = localFile; }); - it('calls the service', done => { - store - .dispatch('getFileData', { path: localFile.path }) - .then(() => { - expect(service.getFileData).toHaveBeenCalledWith('getFileDataURL'); + describe('success', () => { + beforeEach(() => { + mock.onGet(`${gl.TEST_HOST}/getFileDataURL`).replyOnce( + 200, + { + blame_path: 'blame_path', + commits_path: 'commits_path', + permalink: 'permalink', + raw_path: 'raw_path', + binary: false, + html: '123', + render_error: '', + }, + { + 'page-title': 'testing getFileData', + }, + ); + }); - done(); - }) - .catch(done.fail); - }); + it('calls the service', done => { + store + .dispatch('getFileData', { path: localFile.path }) + .then(() => { + expect(service.getFileData).toHaveBeenCalledWith(`${gl.TEST_HOST}/getFileDataURL`); - it('sets the file data', done => { - store - .dispatch('getFileData', { path: localFile.path }) - .then(() => { - expect(localFile.blamePath).toBe('blame_path'); + done(); + }) + .catch(done.fail); + }); - done(); - }) - .catch(done.fail); - }); + it('sets the file data', done => { + store + .dispatch('getFileData', { path: localFile.path }) + .then(() => { + expect(localFile.blamePath).toBe('blame_path'); - it('sets document title', done => { - store - .dispatch('getFileData', { path: localFile.path }) - .then(() => { - expect(document.title).toBe('testing getFileData'); + done(); + }) + .catch(done.fail); + }); - done(); - }) - .catch(done.fail); - }); + it('sets document title', done => { + store + .dispatch('getFileData', { path: localFile.path }) + .then(() => { + expect(document.title).toBe('testing getFileData'); - it('sets the file as active', done => { - store - .dispatch('getFileData', { path: localFile.path }) - .then(() => { - expect(localFile.active).toBeTruthy(); + done(); + }) + .catch(done.fail); + }); - done(); - }) - .catch(done.fail); - }); + it('sets the file as active', done => { + store + .dispatch('getFileData', { path: localFile.path }) + .then(() => { + expect(localFile.active).toBeTruthy(); - it('sets the file not as active if we pass makeFileActive false', done => { - store - .dispatch('getFileData', { path: localFile.path, makeFileActive: false }) - .then(() => { - expect(localFile.active).toBeFalsy(); + done(); + }) + .catch(done.fail); + }); - done(); - }) - .catch(done.fail); + it('sets the file not as active if we pass makeFileActive false', done => { + store + .dispatch('getFileData', { path: localFile.path, makeFileActive: false }) + .then(() => { + expect(localFile.active).toBeFalsy(); + + done(); + }) + .catch(done.fail); + }); + + it('adds the file to open files', done => { + store + .dispatch('getFileData', { path: localFile.path }) + .then(() => { + expect(store.state.openFiles.length).toBe(1); + expect(store.state.openFiles[0].name).toBe(localFile.name); + + done(); + }) + .catch(done.fail); + }); }); - it('adds the file to open files', done => { - store - .dispatch('getFileData', { path: localFile.path }) - .then(() => { - expect(store.state.openFiles.length).toBe(1); - expect(store.state.openFiles[0].name).toBe(localFile.name); + describe('error', () => { + beforeEach(() => { + mock.onGet(`${gl.TEST_HOST}/getFileDataURL`).networkError(); + }); - done(); - }) - .catch(done.fail); + it('dispatches error action', done => { + const dispatch = jasmine.createSpy('dispatch'); + + getFileData( + { + state: store.state, + commit() {}, + dispatch, + }, + { path: localFile.path }, + ) + .then(() => { + expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { + text: 'An error occured whilst loading the file.', + action: jasmine.any(Function), + actionText: 'Please try again', + actionPayload: { + path: localFile.path, + makeFileActive: true, + }, + }); + + done(); + }) + .catch(done.fail); + }); }); }); @@ -278,48 +323,87 @@ describe('IDE store file actions', () => { let tmpFile; beforeEach(() => { - spyOn(service, 'getRawFileData').and.returnValue(Promise.resolve('raw')); + spyOn(service, 'getRawFileData').and.callThrough(); tmpFile = file('tmpFile'); store.state.entries[tmpFile.path] = tmpFile; }); - it('calls getRawFileData service method', done => { - store - .dispatch('getRawFileData', { path: tmpFile.path }) - .then(() => { - expect(service.getRawFileData).toHaveBeenCalledWith(tmpFile); + describe('success', () => { + beforeEach(() => { + mock.onGet(/(.*)/).replyOnce(200, 'raw'); + }); - done(); - }) - .catch(done.fail); - }); + it('calls getRawFileData service method', done => { + store + .dispatch('getRawFileData', { path: tmpFile.path }) + .then(() => { + expect(service.getRawFileData).toHaveBeenCalledWith(tmpFile); - it('updates file raw data', done => { - store - .dispatch('getRawFileData', { path: tmpFile.path }) - .then(() => { - expect(tmpFile.raw).toBe('raw'); + done(); + }) + .catch(done.fail); + }); - done(); - }) - .catch(done.fail); - }); + it('updates file raw data', done => { + store + .dispatch('getRawFileData', { path: tmpFile.path }) + .then(() => { + expect(tmpFile.raw).toBe('raw'); - it('calls also getBaseRawFileData service method', done => { - spyOn(service, 'getBaseRawFileData').and.returnValue(Promise.resolve('baseraw')); + done(); + }) + .catch(done.fail); + }); - tmpFile.mrChange = { new_file: false }; + it('calls also getBaseRawFileData service method', done => { + spyOn(service, 'getBaseRawFileData').and.returnValue(Promise.resolve('baseraw')); - store - .dispatch('getRawFileData', { path: tmpFile.path, baseSha: 'SHA' }) - .then(() => { - expect(service.getBaseRawFileData).toHaveBeenCalledWith(tmpFile, 'SHA'); - expect(tmpFile.baseRaw).toBe('baseraw'); + tmpFile.mrChange = { new_file: false }; - done(); - }) - .catch(done.fail); + store + .dispatch('getRawFileData', { path: tmpFile.path, baseSha: 'SHA' }) + .then(() => { + expect(service.getBaseRawFileData).toHaveBeenCalledWith(tmpFile, 'SHA'); + expect(tmpFile.baseRaw).toBe('baseraw'); + + done(); + }) + .catch(done.fail); + }); + }); + + describe('error', () => { + beforeEach(() => { + mock.onGet(/(.*)/).networkError(); + }); + + it('dispatches error action', done => { + const dispatch = jasmine.createSpy('dispatch'); + + getRawFileData( + { + state: store.state, + commit() {}, + dispatch, + }, + { path: tmpFile.path, baseSha: tmpFile.baseSha }, + ) + .then(done.fail) + .catch(() => { + expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { + text: 'An error occured whilst loading the file content.', + action: jasmine.any(Function), + actionText: 'Please try again', + actionPayload: { + path: tmpFile.path, + baseSha: tmpFile.baseSha, + }, + }); + + done(); + }); + }); }); }); diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js index c1f9fc4f148..ca79edafb7e 100644 --- a/spec/javascripts/ide/stores/actions/project_spec.js +++ b/spec/javascripts/ide/stores/actions/project_spec.js @@ -110,7 +110,7 @@ describe('IDE store project actions', () => { type: 'setErrorMessage', payload: { text: "Branch master was not found in this project's repository.", - action: 'createNewBranchFromDefault', + action: jasmine.any(Function), actionText: 'Create branch', actionPayload: 'master', }, -- cgit v1.2.1 From d4fce4dc2f50b7cf8e0de009384f313f07b0156a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Jun 2018 15:32:54 +0100 Subject: fixed eslint --- .../ide/components/repo_commit_section_spec.js | 1 - spec/javascripts/ide/stores/actions/file_spec.js | 35 +++++++++++----------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js index 8df48d8aeae..30cd92b2ca4 100644 --- a/spec/javascripts/ide/components/repo_commit_section_spec.js +++ b/spec/javascripts/ide/components/repo_commit_section_spec.js @@ -1,6 +1,5 @@ import Vue from 'vue'; import store from '~/ide/stores'; -import service from '~/ide/services'; import router from '~/ide/ide_router'; import repoCommitSection from '~/ide/components/repo_commit_section.vue'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index 36f21f22d62..0dd3f7b5374 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -2,7 +2,6 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import store from '~/ide/stores'; -import { getFileData, getRawFileData } from '~/ide/stores/actions/file'; import * as actions from '~/ide/stores/actions/file'; import * as types from '~/ide/stores/mutation_types'; import service from '~/ide/services'; @@ -293,14 +292,15 @@ describe('IDE store file actions', () => { it('dispatches error action', done => { const dispatch = jasmine.createSpy('dispatch'); - getFileData( - { - state: store.state, - commit() {}, - dispatch, - }, - { path: localFile.path }, - ) + actions + .getFileData( + { + state: store.state, + commit() {}, + dispatch, + }, + { path: localFile.path }, + ) .then(() => { expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { text: 'An error occured whilst loading the file.', @@ -381,14 +381,15 @@ describe('IDE store file actions', () => { it('dispatches error action', done => { const dispatch = jasmine.createSpy('dispatch'); - getRawFileData( - { - state: store.state, - commit() {}, - dispatch, - }, - { path: tmpFile.path, baseSha: tmpFile.baseSha }, - ) + actions + .getRawFileData( + { + state: store.state, + commit() {}, + dispatch, + }, + { path: tmpFile.path, baseSha: tmpFile.baseSha }, + ) .then(done.fail) .catch(() => { expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { -- cgit v1.2.1 From 47fe2a5248d4b391084908f685bcc42ee3e8d24d Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Jun 2018 16:56:58 +0100 Subject: more specs! --- .../ide/stores/actions/merge_request_spec.js | 267 +++++++++++++++------ spec/javascripts/ide/stores/actions/tree_spec.js | 34 +++ 2 files changed, 232 insertions(+), 69 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js index b4ec4a0b173..c99ccc70c6a 100644 --- a/spec/javascripts/ide/stores/actions/merge_request_spec.js +++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js @@ -1,110 +1,239 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import store from '~/ide/stores'; +import { + getMergeRequestData, + getMergeRequestChanges, + getMergeRequestVersions, +} from '~/ide/stores/actions/merge_request'; import service from '~/ide/services'; import { resetStore } from '../../helpers'; describe('IDE store merge request actions', () => { + let mock; + beforeEach(() => { + mock = new MockAdapter(axios); + store.state.projects.abcproject = { mergeRequests: {}, }; }); afterEach(() => { + mock.restore(); resetStore(store); }); describe('getMergeRequestData', () => { - beforeEach(() => { - spyOn(service, 'getProjectMergeRequestData').and.returnValue( - Promise.resolve({ data: { title: 'mergerequest' } }), - ); + describe('success', () => { + beforeEach(() => { + spyOn(service, 'getProjectMergeRequestData').and.callThrough(); + + mock + .onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1/) + .reply(200, { title: 'mergerequest' }); + }); + + it('calls getProjectMergeRequestData service method', done => { + store + .dispatch('getMergeRequestData', { projectId: 'abcproject', mergeRequestId: 1 }) + .then(() => { + expect(service.getProjectMergeRequestData).toHaveBeenCalledWith('abcproject', 1); + + done(); + }) + .catch(done.fail); + }); + + it('sets the Merge Request Object', done => { + store + .dispatch('getMergeRequestData', { projectId: 'abcproject', mergeRequestId: 1 }) + .then(() => { + expect(store.state.projects.abcproject.mergeRequests['1'].title).toBe('mergerequest'); + expect(store.state.currentMergeRequestId).toBe(1); + + done(); + }) + .catch(done.fail); + }); }); - it('calls getProjectMergeRequestData service method', done => { - store - .dispatch('getMergeRequestData', { projectId: 'abcproject', mergeRequestId: 1 }) - .then(() => { - expect(service.getProjectMergeRequestData).toHaveBeenCalledWith('abcproject', 1); - - done(); - }) - .catch(done.fail); - }); - - it('sets the Merge Request Object', done => { - store - .dispatch('getMergeRequestData', { projectId: 'abcproject', mergeRequestId: 1 }) - .then(() => { - expect(store.state.projects.abcproject.mergeRequests['1'].title).toBe('mergerequest'); - expect(store.state.currentMergeRequestId).toBe(1); - - done(); - }) - .catch(done.fail); + describe('error', () => { + beforeEach(() => { + mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1/).networkError(); + }); + + it('dispatches error action', done => { + const dispatch = jasmine.createSpy('dispatch'); + + getMergeRequestData( + { + commit() {}, + dispatch, + state: store.state, + }, + { projectId: 'abcproject', mergeRequestId: 1 }, + ) + .then(done.fail) + .catch(() => { + expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { + text: 'An error occured whilst loading the merge request.', + action: jasmine.any(Function), + actionText: 'Please try again', + actionPayload: { + projectId: 'abcproject', + mergeRequestId: 1, + force: false, + }, + }); + + done(); + }); + }); }); }); describe('getMergeRequestChanges', () => { beforeEach(() => { - spyOn(service, 'getProjectMergeRequestChanges').and.returnValue( - Promise.resolve({ data: { title: 'mergerequest' } }), - ); - store.state.projects.abcproject.mergeRequests['1'] = { changes: [] }; }); - it('calls getProjectMergeRequestChanges service method', done => { - store - .dispatch('getMergeRequestChanges', { projectId: 'abcproject', mergeRequestId: 1 }) - .then(() => { - expect(service.getProjectMergeRequestChanges).toHaveBeenCalledWith('abcproject', 1); - - done(); - }) - .catch(done.fail); + describe('success', () => { + beforeEach(() => { + spyOn(service, 'getProjectMergeRequestChanges').and.callThrough(); + + mock + .onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/changes/) + .reply(200, { title: 'mergerequest' }); + }); + + it('calls getProjectMergeRequestChanges service method', done => { + store + .dispatch('getMergeRequestChanges', { projectId: 'abcproject', mergeRequestId: 1 }) + .then(() => { + expect(service.getProjectMergeRequestChanges).toHaveBeenCalledWith('abcproject', 1); + + done(); + }) + .catch(done.fail); + }); + + it('sets the Merge Request Changes Object', done => { + store + .dispatch('getMergeRequestChanges', { projectId: 'abcproject', mergeRequestId: 1 }) + .then(() => { + expect(store.state.projects.abcproject.mergeRequests['1'].changes.title).toBe( + 'mergerequest', + ); + done(); + }) + .catch(done.fail); + }); }); - it('sets the Merge Request Changes Object', done => { - store - .dispatch('getMergeRequestChanges', { projectId: 'abcproject', mergeRequestId: 1 }) - .then(() => { - expect(store.state.projects.abcproject.mergeRequests['1'].changes.title).toBe( - 'mergerequest', - ); - done(); - }) - .catch(done.fail); + describe('error', () => { + beforeEach(() => { + mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/changes/).networkError(); + }); + + it('dispatches error action', done => { + const dispatch = jasmine.createSpy('dispatch'); + + getMergeRequestChanges( + { + commit() {}, + dispatch, + state: store.state, + }, + { projectId: 'abcproject', mergeRequestId: 1 }, + ) + .then(done.fail) + .catch(() => { + expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { + text: 'An error occured whilst loading the merge request changes.', + action: jasmine.any(Function), + actionText: 'Please try again', + actionPayload: { + projectId: 'abcproject', + mergeRequestId: 1, + force: false, + }, + }); + + done(); + }); + }); }); }); describe('getMergeRequestVersions', () => { beforeEach(() => { - spyOn(service, 'getProjectMergeRequestVersions').and.returnValue( - Promise.resolve({ data: [{ id: 789 }] }), - ); - store.state.projects.abcproject.mergeRequests['1'] = { versions: [] }; }); - it('calls getProjectMergeRequestVersions service method', done => { - store - .dispatch('getMergeRequestVersions', { projectId: 'abcproject', mergeRequestId: 1 }) - .then(() => { - expect(service.getProjectMergeRequestVersions).toHaveBeenCalledWith('abcproject', 1); - - done(); - }) - .catch(done.fail); + describe('success', () => { + beforeEach(() => { + mock + .onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/versions/) + .reply(200, [{ id: 789 }]); + spyOn(service, 'getProjectMergeRequestVersions').and.callThrough(); + }); + + it('calls getProjectMergeRequestVersions service method', done => { + store + .dispatch('getMergeRequestVersions', { projectId: 'abcproject', mergeRequestId: 1 }) + .then(() => { + expect(service.getProjectMergeRequestVersions).toHaveBeenCalledWith('abcproject', 1); + + done(); + }) + .catch(done.fail); + }); + + it('sets the Merge Request Versions Object', done => { + store + .dispatch('getMergeRequestVersions', { projectId: 'abcproject', mergeRequestId: 1 }) + .then(() => { + expect(store.state.projects.abcproject.mergeRequests['1'].versions.length).toBe(1); + done(); + }) + .catch(done.fail); + }); }); - it('sets the Merge Request Versions Object', done => { - store - .dispatch('getMergeRequestVersions', { projectId: 'abcproject', mergeRequestId: 1 }) - .then(() => { - expect(store.state.projects.abcproject.mergeRequests['1'].versions.length).toBe(1); - done(); - }) - .catch(done.fail); + describe('error', () => { + beforeEach(() => { + mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/versions/).networkError(); + }); + + it('dispatches error action', done => { + const dispatch = jasmine.createSpy('dispatch'); + + getMergeRequestVersions( + { + commit() {}, + dispatch, + state: store.state, + }, + { projectId: 'abcproject', mergeRequestId: 1 }, + ) + .then(done.fail) + .catch(() => { + expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { + text: 'An error occured whilst loading the merge request version data.', + action: jasmine.any(Function), + actionText: 'Please try again', + actionPayload: { + projectId: 'abcproject', + mergeRequestId: 1, + force: false, + }, + }); + + done(); + }); + }); }); }); }); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index 3ef2a2da880..6860e6cdb91 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -116,6 +116,40 @@ describe('Multi-file store tree actions', () => { done(); }); }); + + it('dispatches error action', done => { + const dispatch = jasmine.createSpy('dispatchSpy'); + + store.state.projects = { + 'abc/def': { + web_url: `${gl.TEST_HOST}/files`, + }, + }; + + mock.onGet(/(.*)/).replyOnce(500); + + getFiles( + { + commit() {}, + dispatch, + state: store.state, + }, + { + projectId: 'abc/def', + branchId: 'master-testing', + }, + ) + .then(done.fail) + .catch(() => { + expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { + text: 'An error occured whilst loading all the files.', + action: jasmine.any(Function), + actionText: 'Please try again', + actionPayload: { projectId: 'abc/def', branchId: 'master-testing' }, + }); + done(); + }); + }); }); }); -- cgit v1.2.1 From 8bb58fa53345c24621bc060012dafa4a3d3d0f2f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 28 Jun 2018 17:31:59 +0100 Subject: fixed eslint --- spec/javascripts/ide/stores/actions/file_spec.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'spec') diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index 0dd3f7b5374..58d3ffc6d94 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -293,14 +293,7 @@ describe('IDE store file actions', () => { const dispatch = jasmine.createSpy('dispatch'); actions - .getFileData( - { - state: store.state, - commit() {}, - dispatch, - }, - { path: localFile.path }, - ) + .getFileData({ state: store.state, commit() {}, dispatch }, { path: localFile.path }) .then(() => { expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { text: 'An error occured whilst loading the file.', @@ -383,11 +376,7 @@ describe('IDE store file actions', () => { actions .getRawFileData( - { - state: store.state, - commit() {}, - dispatch, - }, + { state: store.state, commit() {}, dispatch }, { path: tmpFile.path, baseSha: tmpFile.baseSha }, ) .then(done.fail) -- cgit v1.2.1 From 047ca033e564c815b6503e46f07f1a0f65903317 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 29 Jun 2018 10:45:13 +0200 Subject: Add simple test for a default repository commit ref --- spec/models/repository_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'spec') diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 27a14ff5d5b..6b67ce69dc0 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -479,6 +479,14 @@ describe Repository do end end + context 'when ref is not specified' do + it 'is using a root ref' do + expect(repository).to receive(:find_commit).with('master') + + repository.commit + end + end + context 'when ref is not valid' do context 'when preceding tree element exists' do it 'returns nil' do -- cgit v1.2.1 From ee2a9e0af0c91e4cc0d1c24acf35ff1eba1cc8db Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 29 Jun 2018 11:21:13 +0200 Subject: Remove unused asset host logic from GfmPipeline and EmojiFilter spec --- spec/lib/banzai/filter/emoji_filter_spec.rb | 9 --------- 1 file changed, 9 deletions(-) (limited to 'spec') diff --git a/spec/lib/banzai/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb index 10910f22d4a..85a4619e33d 100644 --- a/spec/lib/banzai/filter/emoji_filter_spec.rb +++ b/spec/lib/banzai/filter/emoji_filter_spec.rb @@ -3,15 +3,6 @@ require 'spec_helper' describe Banzai::Filter::EmojiFilter do include FilterSpecHelper - before do - @original_asset_host = ActionController::Base.asset_host - ActionController::Base.asset_host = 'https://foo.com' - end - - after do - ActionController::Base.asset_host = @original_asset_host - end - it 'replaces supported name emoji' do doc = filter('

    :heart:

    ') expect(doc.css('gl-emoji').first.text).to eq '❤' -- cgit v1.2.1 From f83f1db4bde285ff8539e798a5b15383b06caff6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 29 Jun 2018 11:21:51 +0200 Subject: Read asset host from ActionController::Base instead of application config --- spec/lib/gitlab/favicon_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 122dcd9634c..68abcb3520a 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -32,7 +32,7 @@ RSpec.describe Gitlab::Favicon, :request_store do end it 'returns a full url when the asset host is configured' do - allow(Gitlab::Application.config).to receive(:asset_host).and_return('http://assets.local') + allow(ActionController::Base).to receive(:asset_host).and_return('http://assets.local') expect(described_class.main).to match %r{^http://localhost/assets/favicon-(?:\h+).png$} end end -- cgit v1.2.1 From 02709334d4013073dffe5b6c2fd6ff7377b87ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Fri, 29 Jun 2018 09:40:41 +0000 Subject: Enabling Doorkeeper reuse_access_token option --- spec/requests/oauth_tokens_spec.rb | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 spec/requests/oauth_tokens_spec.rb (limited to 'spec') diff --git a/spec/requests/oauth_tokens_spec.rb b/spec/requests/oauth_tokens_spec.rb new file mode 100644 index 00000000000..000c3a2b868 --- /dev/null +++ b/spec/requests/oauth_tokens_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe 'OAuth Tokens requests' do + let(:user) { create :user } + let(:application) { create :oauth_application, scopes: 'api' } + + def request_access_token(user) + post '/oauth/token', + grant_type: 'authorization_code', + code: generate_access_grant(user).token, + redirect_uri: application.redirect_uri, + client_id: application.uid, + client_secret: application.secret + end + + def generate_access_grant(user) + create :oauth_access_grant, application: application, resource_owner_id: user.id + end + + context 'when there is already a token for the application' do + let!(:existing_token) { create :oauth_access_token, application: application, resource_owner_id: user.id } + + context 'and the request is done by the resource owner' do + it 'reuses and returns the stored token' do + expect do + request_access_token(user) + end.not_to change { Doorkeeper::AccessToken.count } + + expect(json_response['access_token']).to eq existing_token.token + end + end + + context 'and the request is done by a different user' do + let(:other_user) { create :user } + + it 'generates and returns a different token for a different owner' do + expect do + request_access_token(other_user) + end.to change { Doorkeeper::AccessToken.count }.by(1) + + expect(json_response['access_token']).not_to be_nil + end + end + end + + context 'when there is no token stored for the application' do + it 'generates and returns a new token' do + expect do + request_access_token(user) + end.to change { Doorkeeper::AccessToken.count }.by(1) + + expect(json_response['access_token']).not_to be_nil + end + end +end -- cgit v1.2.1 From eb0d6551161552d33cdc1cfc9dd61fc970158fb5 Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Thu, 28 Jun 2018 14:48:10 +0200 Subject: Fix ambiguous due_date column for Issue scopes --- spec/features/ics/dashboard_issues_spec.rb | 37 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'spec') diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb index 90d02f7e40f..a4d05c25a90 100644 --- a/spec/features/ics/dashboard_issues_spec.rb +++ b/spec/features/ics/dashboard_issues_spec.rb @@ -5,6 +5,7 @@ describe 'Dashboard Issues Calendar Feed' do let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } let!(:project) { create(:project) } + let(:milestone) { create(:milestone, project_id: project.id, title: 'v1.0') } before do project.add_master(user) @@ -14,7 +15,9 @@ describe 'Dashboard Issues Calendar Feed' do context 'with no referer' do it 'renders calendar feed' do sign_in user - visit issues_dashboard_path(:ics) + visit issues_dashboard_path(:ics, + due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name, + sort: 'closest_future_date') expect(response_headers['Content-Type']).to have_content('text/calendar') expect(body).to have_text('BEGIN:VCALENDAR') @@ -25,19 +28,37 @@ describe 'Dashboard Issues Calendar Feed' do it 'renders calendar feed as text/plain' do sign_in user page.driver.header('Referer', issues_dashboard_url(host: Settings.gitlab.base_url)) - visit issues_dashboard_path(:ics) + visit issues_dashboard_path(:ics, + due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name, + sort: 'closest_future_date') expect(response_headers['Content-Type']).to have_content('text/plain') expect(body).to have_text('BEGIN:VCALENDAR') end end + + context 'when filtered by milestone' do + it 'renders calendar feed' do + sign_in user + visit issues_dashboard_path(:ics, + due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name, + sort: 'closest_future_date', + milestone_title: milestone.title) + + expect(response_headers['Content-Type']).to have_content('text/calendar') + expect(body).to have_text('BEGIN:VCALENDAR') + end + end end context 'when authenticated via personal access token' do it 'renders calendar feed' do personal_access_token = create(:personal_access_token, user: user) - visit issues_dashboard_path(:ics, private_token: personal_access_token.token) + visit issues_dashboard_path(:ics, + due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name, + sort: 'closest_future_date', + private_token: personal_access_token.token) expect(response_headers['Content-Type']).to have_content('text/calendar') expect(body).to have_text('BEGIN:VCALENDAR') @@ -46,7 +67,10 @@ describe 'Dashboard Issues Calendar Feed' do context 'when authenticated via feed token' do it 'renders calendar feed' do - visit issues_dashboard_path(:ics, feed_token: user.feed_token) + visit issues_dashboard_path(:ics, + due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name, + sort: 'closest_future_date', + feed_token: user.feed_token) expect(response_headers['Content-Type']).to have_content('text/calendar') expect(body).to have_text('BEGIN:VCALENDAR') @@ -60,7 +84,10 @@ describe 'Dashboard Issues Calendar Feed' do end it 'renders issue fields' do - visit issues_dashboard_path(:ics, feed_token: user.feed_token) + visit issues_dashboard_path(:ics, + due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name, + sort: 'closest_future_date', + feed_token: user.feed_token) expect(body).to have_text("SUMMARY:test title (in #{project.full_path})") # line length for ics is 75 chars -- cgit v1.2.1 From 2c2422d54e4b12471dbc25dbc90cbbffe4fb1c2b Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 29 Jun 2018 14:25:35 +0100 Subject: Fix MR diffs created with gitaly_diff_between enabled When we save merge request diffs to the database, we need to expand the diff before doing so. That's so that we can expand diffs (within the normal limits) without hitting the repository, but just by going to the database. This is done implicitly - diffs are expanded unless we say otherwise. However, we have another option we can pass, that lets us enforce diff size limits, that defaults to true. Prior to this commit: - The Rugged code path defaulted to setting `expanded: true` and `enforce_limits: true`. - The Gitaly code path defaulted to setting `expanded: false` and `enforce_limits: true`. This was introduced by eb36fa17a6ae5cda8950904b5a52e6aa365ae591, which implemented the initial feature. Since then, if the `gitaly_diff_between` feature flag was enabled, MRs would have diffs that could not be expanded in some cases, with no fix other than to disable the feature flag and force push to the MR to refresh the diff in the database. --- spec/lib/gitlab/gitaly_client/commit_service_spec.rb | 4 ++-- spec/models/merge_request_diff_spec.rb | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 7951cbe7b1d..54f2ea33f90 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -17,7 +17,7 @@ describe Gitlab::GitalyClient::CommitService do repository: repository_message, left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660', right_commit_id: commit.id, - collapse_diffs: true, + collapse_diffs: false, enforce_limits: true, **Gitlab::Git::DiffCollection.collection_limits.to_h ) @@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::CommitService do repository: repository_message, left_commit_id: Gitlab::Git::EMPTY_TREE_ID, right_commit_id: initial_commit.id, - collapse_diffs: true, + collapse_diffs: false, enforce_limits: true, **Gitlab::Git::DiffCollection.collection_limits.to_h ) diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 48c01fc4d4e..ccc3ff861c5 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -153,6 +153,13 @@ describe MergeRequestDiff do expect(mr_diff.empty?).to be_truthy end + it 'expands collapsed diffs before saving' do + mr_diff = create(:merge_request, source_branch: 'expand-collapse-lines', target_branch: 'master').merge_request_diff + diff_file = mr_diff.merge_request_diff_files.find_by(new_path: 'expand-collapse/file-5.txt') + + expect(diff_file.diff).not_to be_empty + end + it 'saves binary diffs correctly' do path = 'files/images/icn-time-tracking.pdf' mr_diff = create(:merge_request, source_branch: 'add-pdf-text-binary', target_branch: 'master').merge_request_diff -- cgit v1.2.1 From 90a99ff3aaa8f70b21369f976b075174e71f0a93 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 29 Jun 2018 13:57:52 +0000 Subject: Add pipeline stages position clean-up migration --- .../import_export/test_project_export.tar.gz | Bin 343091 -> 343136 bytes spec/fixtures/exported-project.gz | Bin 2560 -> 0 bytes spec/lib/gitlab/import_export/importer_spec.rb | 4 +- .../cleanup_stages_position_migration_spec.rb | 67 +++++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) delete mode 100644 spec/fixtures/exported-project.gz create mode 100644 spec/migrations/cleanup_stages_position_migration_spec.rb (limited to 'spec') diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz index 72ab2d71f35..ceba4dfec57 100644 Binary files a/spec/features/projects/import_export/test_project_export.tar.gz and b/spec/features/projects/import_export/test_project_export.tar.gz differ diff --git a/spec/fixtures/exported-project.gz b/spec/fixtures/exported-project.gz deleted file mode 100644 index bef7e2ff8ee..00000000000 Binary files a/spec/fixtures/exported-project.gz and /dev/null differ diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb index 991e354f499..c074e61da26 100644 --- a/spec/lib/gitlab/import_export/importer_spec.rb +++ b/spec/lib/gitlab/import_export/importer_spec.rb @@ -4,14 +4,14 @@ describe Gitlab::ImportExport::Importer do let(:user) { create(:user) } let(:test_path) { "#{Dir.tmpdir}/importer_spec" } let(:shared) { project.import_export_shared } - let(:project) { create(:project, import_source: File.join(test_path, 'exported-project.gz')) } + let(:project) { create(:project, import_source: File.join(test_path, 'test_project_export.tar.gz')) } subject(:importer) { described_class.new(project) } before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path) FileUtils.mkdir_p(shared.export_path) - FileUtils.cp(Rails.root.join('spec', 'fixtures', 'exported-project.gz'), test_path) + FileUtils.cp(Rails.root.join('spec/features/projects/import_export/test_project_export.tar.gz'), test_path) allow(subject).to receive(:remove_import_file) end diff --git a/spec/migrations/cleanup_stages_position_migration_spec.rb b/spec/migrations/cleanup_stages_position_migration_spec.rb new file mode 100644 index 00000000000..dde5a777487 --- /dev/null +++ b/spec/migrations/cleanup_stages_position_migration_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20180604123514_cleanup_stages_position_migration.rb') + +describe CleanupStagesPositionMigration, :migration, :sidekiq, :redis do + let(:migration) { spy('migration') } + + before do + allow(Gitlab::BackgroundMigration::MigrateStageIndex) + .to receive(:new).and_return(migration) + end + + context 'when there are pending background migrations' do + it 'processes pending jobs synchronously' do + Sidekiq::Testing.disable! do + BackgroundMigrationWorker + .perform_in(2.minutes, 'MigrateStageIndex', [1, 1]) + BackgroundMigrationWorker + .perform_async('MigrateStageIndex', [1, 1]) + + migrate! + + expect(migration).to have_received(:perform).with(1, 1).twice + end + end + end + + context 'when there are no background migrations pending' do + it 'does nothing' do + Sidekiq::Testing.disable! do + migrate! + + expect(migration).not_to have_received(:perform) + end + end + end + + context 'when there are still unmigrated stages present' do + let(:stages) { table('ci_stages') } + let(:builds) { table('ci_builds') } + + let!(:entities) do + %w[build test broken].map do |name| + stages.create(name: name) + end + end + + before do + stages.update_all(position: nil) + + builds.create(name: 'unit', stage_id: entities.first.id, stage_idx: 1, ref: 'master') + builds.create(name: 'unit', stage_id: entities.second.id, stage_idx: 1, ref: 'master') + end + + it 'migrates stages sequentially for every stage' do + expect(stages.all).to all(have_attributes(position: nil)) + + migrate! + + expect(migration).to have_received(:perform) + .with(entities.first.id, entities.first.id) + expect(migration).to have_received(:perform) + .with(entities.second.id, entities.second.id) + expect(migration).not_to have_received(:perform) + .with(entities.third.id, entities.third.id) + end + end +end -- cgit v1.2.1 From d4d6528c863d48c9e2d30e6b5927ef01ec35e0e3 Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Fri, 22 Jun 2018 14:59:36 +0200 Subject: Expire correct method caches after HEAD changed --- spec/lib/gitlab/repository_cache_adapter_spec.rb | 12 ++++++++-- spec/models/repository_spec.rb | 28 ++++++++++++++++-------- 2 files changed, 29 insertions(+), 11 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb index 85971f2a7ef..5bd4d6c6a48 100644 --- a/spec/lib/gitlab/repository_cache_adapter_spec.rb +++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb @@ -67,10 +67,18 @@ describe Gitlab::RepositoryCacheAdapter do describe '#expire_method_caches' do it 'expires the caches of the given methods' do - expect(cache).to receive(:expire).with(:readme) + expect(cache).to receive(:expire).with(:rendered_readme) expect(cache).to receive(:expire).with(:gitignore) - repository.expire_method_caches(%i(readme gitignore)) + repository.expire_method_caches(%i(rendered_readme gitignore)) + end + + it 'does not expire caches for non-existent methods' do + expect(cache).not_to receive(:expire).with(:nonexistent) + expect(Rails.logger).to( + receive(:error).with("Requested to expire non-existent method 'nonexistent' for Repository")) + + repository.expire_method_caches(%i(nonexistent)) end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index d817a8376f4..4c4d61218e9 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1699,19 +1699,29 @@ describe Repository do end describe '#after_change_head' do - it 'flushes the readme cache' do + it 'flushes the method caches' do expect(repository).to receive(:expire_method_caches).with([ - :readme, + :size, + :commit_count, + :rendered_readme, + :contribution_guide, :changelog, - :license, - :contributing, + :license_blob, + :license_key, :gitignore, - :koding, - :gitlab_ci, + :koding_yml, + :gitlab_ci_yml, + :branch_names, + :tag_names, + :branch_count, + :tag_count, :avatar, - :issue_template, - :merge_request_template, - :xcode_config + :exists?, + :root_ref, + :has_visible_content?, + :issue_template_names, + :merge_request_template_names, + :xcode_project? ]) repository.after_change_head -- cgit v1.2.1 From 5870d5e4d481ed1a129d8b35c96b912b809da9d1 Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Fri, 29 Jun 2018 20:30:00 +0300 Subject: Update create issue test to check for input placeholders --- spec/features/projects/issues/user_creates_issue_spec.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'spec') diff --git a/spec/features/projects/issues/user_creates_issue_spec.rb b/spec/features/projects/issues/user_creates_issue_spec.rb index e76f7c5589d..5e8662100c5 100644 --- a/spec/features/projects/issues/user_creates_issue_spec.rb +++ b/spec/features/projects/issues/user_creates_issue_spec.rb @@ -17,6 +17,9 @@ describe "User creates issue" do expect(page).to have_no_content("Assign to") .and have_no_content("Labels") .and have_no_content("Milestone") + + expect(page.find('#issue_title')['placeholder']).to eq 'Title' + expect(page.find('#issue_description')['placeholder']).to eq 'Write a comment or drag your files here…' end issue_title = "500 error on profile" -- cgit v1.2.1 From e64d4fd36f0d0b3c5be6e5f666af72ebd58a4293 Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Fri, 29 Jun 2018 20:27:15 +0000 Subject: Resolve "Tooltips scroll along the list within Pipeline jobs dropdown on MR widget" --- .../pipelines/graph/job_component_spec.js | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'spec') diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js index 073dae56c25..9c55a19ebc7 100644 --- a/spec/javascripts/pipelines/graph/job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_component_spec.js @@ -135,4 +135,34 @@ describe('pipeline graph job component', () => { expect(component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title')).toEqual('test - success'); }); }); + + describe('tooltip placement', () => { + const tooltipBoundary = 'a[data-boundary="viewport"]'; + + it('does not set tooltip boundary by default', () => { + component = mountComponent(JobComponent, { + job: mockJob, + }); + + expect(component.$el.querySelector(tooltipBoundary)).toBeNull(); + }); + + it('sets tooltip boundary to viewport for small dropdowns', () => { + component = mountComponent(JobComponent, { + job: mockJob, + dropdownLength: 1, + }); + + expect(component.$el.querySelector(tooltipBoundary)).not.toBeNull(); + }); + + it('does not set tooltip boundary for large lists', () => { + component = mountComponent(JobComponent, { + job: mockJob, + dropdownLength: 7, + }); + + expect(component.$el.querySelector(tooltipBoundary)).toBeNull(); + }); + }); }); -- cgit v1.2.1 From 05a9c6b21100729c49b122f3085e1df09f656fc6 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 29 Jun 2018 15:22:05 -0700 Subject: Fix Bamboo CI status not showing for branch plans The original API that queries by label (`/rest/api/latest/result?label=#{sha1}`) only works for results from main plans and not branch plans. The `/rest/api/latest/result/byChangeset/#{sha1}` endpoint gives results from branch plans but not for the first push to the branch. Subsequent pushes work. For more details, see https://jira.atlassian.com/browse/BAM-16121. Closes #1355 --- .../models/project_services/bamboo_service_spec.rb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'spec') diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 85baaccf035..f4f7afb1b92 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -120,6 +120,14 @@ describe BambooService, :use_clean_rails_memory_store_caching do end end + describe '#execute' do + it 'runs update and build action' do + stub_update_and_build_request + + subject.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA) + end + end + describe '#build_page' do it 'returns the contents of the reactive cache' do stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref') @@ -216,10 +224,20 @@ describe BambooService, :use_clean_rails_memory_store_caching do end end + def stub_update_and_build_request(status: 200, body: nil) + bamboo_full_url = 'http://gitlab.com/bamboo/updateAndBuild.action?buildKey=foo&os_authType=basic' + + stub_bamboo_request(bamboo_full_url, status, body) + end + def stub_request(status: 200, body: nil) - bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result?label=123&os_authType=basic' + bamboo_full_url = 'http://gitlab.com/bamboo/rest/api/latest/result/byChangeset/123?os_authType=basic' + + stub_bamboo_request(bamboo_full_url, status, body) + end - WebMock.stub_request(:get, bamboo_full_url).to_return( + def stub_bamboo_request(url, status, body) + WebMock.stub_request(:get, url).to_return( status: status, headers: { 'Content-Type' => 'application/json' }, body: body -- cgit v1.2.1 From ae86fd96ae0bda68c9efc9d73e5cadec837a21fe Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Fri, 22 Jun 2018 15:31:04 +0200 Subject: Cancel ExclusiveLease upon completion in ProjectCacheWorker --- spec/workers/project_cache_worker_spec.rb | 73 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 31 deletions(-) (limited to 'spec') diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index b9b5445562f..8c4daac5f80 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -9,44 +9,50 @@ describe ProjectCacheWorker do let(:lease_key) { "project_cache_worker:#{project.id}:update_statistics" } let(:lease_timeout) { ProjectCacheWorker::LEASE_TIMEOUT } - describe '#perform' do - before do - stub_exclusive_lease(lease_key, timeout: lease_timeout) - end + before do + stub_exclusive_lease(lease_key, timeout: lease_timeout) + + allow(Project).to receive(:find_by) + .with(id: project.id) + .and_return(project) + end + describe '#perform' do context 'with a non-existing project' do - it 'does nothing' do - expect(worker).not_to receive(:update_statistics) + it 'does not update statistic' do + allow(Project).to receive(:find_by).with(id: -1).and_return(nil) + + expect(subject).not_to receive(:update_statistics) - worker.perform(-1) + subject.perform(-1) end end context 'with an existing project without a repository' do - it 'does nothing' do - allow_any_instance_of(Repository).to receive(:exists?).and_return(false) + it 'does not update statistics' do + allow(project.repository).to receive(:exists?).and_return(false) - expect(worker).not_to receive(:update_statistics) + expect(subject).not_to receive(:update_statistics) - worker.perform(project.id) + subject.perform(project.id) end end context 'with an existing project' do it 'updates the project statistics' do - expect(worker).to receive(:update_statistics) - .with(kind_of(Project), %i(repository_size)) - .and_call_original + expect(subject).to receive(:update_statistics) + .with(%w(repository_size)) + .and_call_original - worker.perform(project.id, [], %w(repository_size)) + subject.perform(project.id, [], %w(repository_size)) end it 'refreshes the method caches' do - expect_any_instance_of(Repository).to receive(:refresh_method_caches) - .with(%i(readme)) - .and_call_original + expect(project.repository).to receive(:refresh_method_caches) + .with(%i(readme)) + .and_call_original - worker.perform(project.id, %w(readme)) + subject.perform(project.id, %w(readme)) end context 'with plain readme' do @@ -54,23 +60,22 @@ describe ProjectCacheWorker do allow(MarkupHelper).to receive(:gitlab_markdown?).and_return(false) allow(MarkupHelper).to receive(:plain?).and_return(true) - expect_any_instance_of(Repository).to receive(:refresh_method_caches) - .with(%i(readme)) - .and_call_original - worker.perform(project.id, %w(readme)) + expect(project.repository).to receive(:refresh_method_caches) + .with(%i(readme)) + .and_call_original + + subject.perform(project.id, %w(readme)) end end end - end - describe '#update_statistics' do context 'when a lease could not be obtained' do it 'does not update the repository size' do stub_exclusive_lease_taken(lease_key, timeout: lease_timeout) - expect(statistics).not_to receive(:refresh!) + expect(project.statistics).not_to receive(:refresh!) - worker.update_statistics(project) + subject.perform(project.id, [], %w(repository_size)) end end @@ -78,11 +83,17 @@ describe ProjectCacheWorker do it 'updates the project statistics' do stub_exclusive_lease(lease_key, timeout: lease_timeout) - expect(statistics).to receive(:refresh!) - .with(only: %i(repository_size)) - .and_call_original + expect(project.statistics).to receive(:refresh!) + .with(only: %i(repository_size)) + .and_call_original + + subject.perform(project.id, [], %i(repository_size)) + end + + it 'cancels the lease after statistics has been updated' do + expect(subject).to receive(:release_lease).with('uuid') - worker.update_statistics(project, %i(repository_size)) + subject.perform(project.id, [], %i(repository_size)) end end end -- cgit v1.2.1 From fb44cb3d904f487029c5d4ff955ed1a912908cc5 Mon Sep 17 00:00:00 2001 From: Tim Zallmann Date: Sat, 30 Jun 2018 13:17:46 +0000 Subject: Performance Improvements to Vue MR page --- spec/helpers/merge_requests_helper_spec.rb | 2 +- .../javascripts/helpers/init_vue_mr_page_helper.js | 12 +++++++---- spec/javascripts/merge_request_spec.js | 23 ++++++++++++++-------- spec/javascripts/merge_request_tabs_spec.js | 1 + 4 files changed, 25 insertions(+), 13 deletions(-) (limited to 'spec') diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 3008528e60c..885204062fe 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -54,7 +54,7 @@ describe MergeRequestsHelper do let(:options) { { force_link: true } } it 'removes the data-toggle attributes' do - is_expected.not_to match(/data-toggle="tab"/) + is_expected.not_to match(/data-toggle="tabvue"/) end end end diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js index 05c6d587e9c..fc4288eb15b 100644 --- a/spec/javascripts/helpers/init_vue_mr_page_helper.js +++ b/spec/javascripts/helpers/init_vue_mr_page_helper.js @@ -5,12 +5,16 @@ import { userDataMock, notesDataMock, noteableDataMock } from '../notes/mock_dat import diffFileMockData from '../diffs/mock_data/diff_file'; export default function initVueMRPage() { + const mrTestEl = document.createElement('div'); + mrTestEl.className = 'js-merge-request-test'; + document.body.appendChild(mrTestEl); + const diffsAppEndpoint = '/diffs/app/endpoint'; const diffsAppProjectPath = 'testproject'; const mrEl = document.createElement('div'); mrEl.className = 'merge-request fixture-mr'; mrEl.setAttribute('data-mr-action', 'diffs'); - document.body.appendChild(mrEl); + mrTestEl.appendChild(mrEl); const mrDiscussionsEl = document.createElement('div'); mrDiscussionsEl.id = 'js-vue-mr-discussions'; @@ -18,18 +22,18 @@ export default function initVueMRPage() { mrDiscussionsEl.setAttribute('data-noteable-data', JSON.stringify(noteableDataMock)); mrDiscussionsEl.setAttribute('data-notes-data', JSON.stringify(notesDataMock)); mrDiscussionsEl.setAttribute('data-noteable-type', 'merge-request'); - document.body.appendChild(mrDiscussionsEl); + mrTestEl.appendChild(mrDiscussionsEl); const discussionCounterEl = document.createElement('div'); discussionCounterEl.id = 'js-vue-discussion-counter'; - document.body.appendChild(discussionCounterEl); + mrTestEl.appendChild(discussionCounterEl); const diffsAppEl = document.createElement('div'); diffsAppEl.id = 'js-diffs-app'; diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint); diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath); diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); - document.body.appendChild(diffsAppEl); + mrTestEl.appendChild(diffsAppEl); const mock = new MockAdapter(axios); mock.onGet(diffsAppEndpoint).reply(200, { diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 22eb0ad7143..7502f1fa2e1 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -19,9 +19,11 @@ import IssuablesHelper from '~/helpers/issuables_helper'; spyOn(axios, 'patch').and.callThrough(); mock = new MockAdapter(axios); - mock.onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`).reply(200, {}); + mock + .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`) + .reply(200, {}); - return this.merge = new MergeRequest(); + return (this.merge = new MergeRequest()); }); afterEach(() => { @@ -32,17 +34,22 @@ import IssuablesHelper from '~/helpers/issuables_helper'; spyOn($, 'ajax').and.stub(); const changeEvent = document.createEvent('HTMLEvents'); changeEvent.initEvent('change', true, true); - $('input[type=checkbox]').attr('checked', true)[0].dispatchEvent(changeEvent); + $('input[type=checkbox]') + .attr('checked', true)[0] + .dispatchEvent(changeEvent); return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); }); - it('submits an ajax request on tasklist:changed', (done) => { + it('submits an ajax request on tasklist:changed', done => { $('.js-task-list-field').trigger('tasklist:changed'); setTimeout(() => { - expect(axios.patch).toHaveBeenCalledWith(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, { - merge_request: { description: '- [ ] Task List Item' }, - }); + expect(axios.patch).toHaveBeenCalledWith( + `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, + { + merge_request: { description: '- [ ] Task List Item' }, + }, + ); done(); }); }); @@ -119,4 +126,4 @@ import IssuablesHelper from '~/helpers/issuables_helper'; }); }); }); -}).call(window); +}.call(window)); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index 08928e13985..7251ce19a90 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -40,6 +40,7 @@ describe('MergeRequestTabs', function() { this.class.unbindEvents(); this.class.destroyPipelinesView(); mrPageMock.restore(); + $('.js-merge-request-test').remove(); }); describe('opensInNewTab', function() { -- cgit v1.2.1 From 26bea57883f01ef304465a93d3a8a9b611abc178 Mon Sep 17 00:00:00 2001 From: Ash McKenzie Date: Wed, 13 Jun 2018 12:20:10 +1000 Subject: Better route matching for read-only detection --- spec/lib/gitlab/middleware/read_only_spec.rb | 35 +++++++++++++++------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'spec') diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb index 39ec2f37a83..5c398bc2063 100644 --- a/spec/lib/gitlab/middleware/read_only_spec.rb +++ b/spec/lib/gitlab/middleware/read_only_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Gitlab::Middleware::ReadOnly do include Rack::Test::Methods + using RSpec::Parameterized::TableSyntax RSpec::Matchers.define :be_a_redirect do match do |response| @@ -117,39 +118,41 @@ describe Gitlab::Middleware::ReadOnly do context 'whitelisted requests' do it 'expects a POST internal request to be allowed' do expect(Rails.application.routes).not_to receive(:recognize_path) - response = request.post("/api/#{API::API.version}/internal") expect(response).not_to be_a_redirect expect(subject).not_to disallow_request end - it 'expects a POST LFS request to batch URL to be allowed' do - expect(Rails.application.routes).to receive(:recognize_path).and_call_original - response = request.post('/root/rouge.git/info/lfs/objects/batch') + it 'expects requests to sidekiq admin to be allowed' do + response = request.post('/admin/sidekiq') expect(response).not_to be_a_redirect expect(subject).not_to disallow_request - end - it 'expects a POST request to git-upload-pack URL to be allowed' do - expect(Rails.application.routes).to receive(:recognize_path).and_call_original - response = request.post('/root/rouge.git/git-upload-pack') + response = request.get('/admin/sidekiq') expect(response).not_to be_a_redirect expect(subject).not_to disallow_request end - it 'expects requests to sidekiq admin to be allowed' do - response = request.post('/admin/sidekiq') - - expect(response).not_to be_a_redirect - expect(subject).not_to disallow_request + where(:description, :path) do + 'LFS request to batch' | '/root/rouge.git/info/lfs/objects/batch' + 'LFS request to locks verify' | '/root/rouge.git/info/lfs/locks/verify' + 'LFS request to locks create' | '/root/rouge.git/info/lfs/locks' + 'LFS request to locks unlock' | '/root/rouge.git/info/lfs/locks/1/unlock' + 'request to git-upload-pack' | '/root/rouge.git/git-upload-pack' + 'request to git-receive-pack' | '/root/rouge.git/git-receive-pack' + end - response = request.get('/admin/sidekiq') + with_them do + it "expects a POST #{description} URL to be allowed" do + expect(Rails.application.routes).to receive(:recognize_path).and_call_original + response = request.post(path) - expect(response).not_to be_a_redirect - expect(subject).not_to disallow_request + expect(response).not_to be_a_redirect + expect(subject).not_to disallow_request + end end end end -- cgit v1.2.1 From 352e322291fe2ebccf055d55b4040677c106b6e1 Mon Sep 17 00:00:00 2001 From: Jan Beckmann Date: Mon, 2 Jul 2018 08:04:43 +0000 Subject: Resolve "404 when trying to create a new issue from the group" --- spec/features/groups/empty_states_spec.rb | 30 +++++++++++++++++++++-------- spec/features/groups/issues_spec.rb | 22 +++++++++++++++++++++ spec/features/groups/merge_requests_spec.rb | 17 ++++++++++++++++ spec/javascripts/api_spec.js | 2 +- 4 files changed, 62 insertions(+), 9 deletions(-) (limited to 'spec') diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb index 04217fec06c..5828d833ae9 100644 --- a/spec/features/groups/empty_states_spec.rb +++ b/spec/features/groups/empty_states_spec.rb @@ -59,6 +59,18 @@ feature 'Group empty states' do end end + shared_examples "no projects" do + it 'displays an empty state' do + expect(page).to have_selector('.empty-state') + end + + it "does not show a new #{issuable_name} button" do + within '.empty-state' do + expect(page).not_to have_link("create #{issuable_name}") + end + end + end + context 'group without a project' do context 'group has a subgroup', :nested_groups do let(:subgroup) { create(:group, parent: group) } @@ -92,16 +104,18 @@ feature 'Group empty states' do visit path end - it 'displays an empty state' do - expect(page).to have_selector('.empty-state') - end + it_behaves_like "no projects" + end + end - it "shows a new #{issuable_name} button" do - within '.empty-state' do - expect(page).not_to have_link("create #{issuable_name}") - end - end + context 'group has only a project with issues disabled' do + let(:project_with_issues_disabled) { create(:empty_project, :issues_disabled, group: group) } + + before do + visit path end + + it_behaves_like "no projects" end end end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index 111a24c0d94..e131ded3688 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -5,6 +5,7 @@ feature 'Group issues page' do let(:group) { create(:group) } let(:project) { create(:project, :public, group: group)} + let(:project_with_issues_disabled) { create(:project, :issues_disabled, group: group) } let(:path) { issues_group_path(group) } context 'with shared examples' do @@ -76,4 +77,25 @@ feature 'Group issues page' do end end end + + context 'projects with issues disabled' do + describe 'issue dropdown' do + let(:user_in_group) { create(:group_member, :master, user: create(:user), group: group ).user } + + before do + [project, project_with_issues_disabled].each { |project| project.add_master(user_in_group) } + sign_in(user_in_group) + visit issues_group_path(group) + end + + it 'shows projects only with issues feature enabled', :js do + find('.new-project-item-link').click + + page.within('.select2-results') do + expect(page).to have_content(project.full_name) + expect(page).not_to have_content(project_with_issues_disabled.full_name) + end + end + end + end end diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb index 672ae785c2d..921a447f6ee 100644 --- a/spec/features/groups/merge_requests_spec.rb +++ b/spec/features/groups/merge_requests_spec.rb @@ -56,4 +56,21 @@ feature 'Group merge requests page' do expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name) end end + + describe 'new merge request dropdown' do + let(:project_with_merge_requests_disabled) { create(:project, :merge_requests_disabled, group: group) } + + before do + visit path + end + + it 'shows projects only with merge requests feature enabled', :js do + find('.new-project-item-link').click + + page.within('.select2-results') do + expect(page).to have_content(project.name_with_namespace) + expect(page).not_to have_content(project_with_merge_requests_disabled.name_with_namespace) + end + end + end end diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js index c53b6da4b48..54cb6d84109 100644 --- a/spec/javascripts/api_spec.js +++ b/spec/javascripts/api_spec.js @@ -242,7 +242,7 @@ describe('Api', () => { }, ]); - Api.groupProjects(groupId, query, response => { + Api.groupProjects(groupId, query, {}, response => { expect(response.length).toBe(1); expect(response[0].name).toBe('test'); done(); -- cgit v1.2.1 From aac6b2b770119d54a005484be9958b6caf1bfe33 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Thu, 28 Jun 2018 17:15:11 +0530 Subject: Update tests to check emoji autocompletion --- spec/features/projects/milestones/new_spec.rb | 4 ++-- spec/features/projects/wiki/user_creates_wiki_page_spec.rb | 4 ++-- spec/features/projects/wiki/user_updates_wiki_page_spec.rb | 4 ++-- spec/features/tags/master_creates_tag_spec.rb | 4 ++-- spec/features/tags/master_updates_tag_spec.rb | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'spec') diff --git a/spec/features/projects/milestones/new_spec.rb b/spec/features/projects/milestones/new_spec.rb index f7900210fe6..6595bff549b 100644 --- a/spec/features/projects/milestones/new_spec.rb +++ b/spec/features/projects/milestones/new_spec.rb @@ -9,9 +9,9 @@ feature 'Creating a new project milestone', :js do visit new_project_milestone_path(project) end - it 'description has autocomplete' do + it 'description has emoji autocomplete' do find('#milestone_description').native.send_keys('') - fill_in 'milestone_description', with: '@' + fill_in 'milestone_description', with: ':' expect(page).to have_selector('.atwho-view') end diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index 706894f4b32..733e6c89de7 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -242,7 +242,7 @@ describe "User creates wiki page" do end end - it "shows the autocompletion dropdown" do + it "shows the emoji autocompletion dropdown" do click_link("New page") page.within("#modal-new-wiki") do @@ -254,7 +254,7 @@ describe "User creates wiki page" do page.within(".wiki-form") do find("#wiki_content").native.send_keys("") - fill_in(:wiki_content, with: "@") + fill_in(:wiki_content, with: ":") end expect(page).to have_selector(".atwho-view") diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb index 272dac127dd..2ccbc15b6da 100644 --- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -96,11 +96,11 @@ describe 'User updates wiki page' do expect(find('textarea#wiki_content').value).to eq('') end - it 'shows the autocompletion dropdown', :js do + it 'shows the emoji autocompletion dropdown', :js do click_link('Edit') find('#wiki_content').native.send_keys('') - fill_in(:wiki_content, with: '@') + fill_in(:wiki_content, with: ':') expect(page).to have_selector('.atwho-view') end diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb index 8a8f6933fa5..6701f575a23 100644 --- a/spec/features/tags/master_creates_tag_spec.rb +++ b/spec/features/tags/master_creates_tag_spec.rb @@ -75,9 +75,9 @@ feature 'Master creates tag' do visit new_project_tag_path(project) end - it 'description has autocomplete', :js do + it 'description has emoji autocomplete', :js do find('#release_description').native.send_keys('') - fill_in 'release_description', with: '@' + fill_in 'release_description', with: ':' expect(page).to have_selector('.atwho-view') end diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb index 1c370a99b13..26f51bee887 100644 --- a/spec/features/tags/master_updates_tag_spec.rb +++ b/spec/features/tags/master_updates_tag_spec.rb @@ -25,13 +25,13 @@ feature 'Master updates tag' do expect(page).to have_content 'Awesome release notes' end - scenario 'description has autocomplete', :js do + scenario 'description has emoji autocomplete', :js do page.within(first('.content-list .controls')) do click_link 'Edit release notes' end find('#release_description').native.send_keys('') - fill_in 'release_description', with: '@' + fill_in 'release_description', with: ':' expect(page).to have_selector('.atwho-view') end -- cgit v1.2.1 From 75316348c50d03d9aed760e9b603275995e4e3e3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 26 Jun 2018 18:24:42 +0200 Subject: Prune web hook logs older than 90 days This adds a recurring Sidekiq job that removes up to 50 000 old web hook logs per hour, if they are older than 90 days. This will prevent the web_hook_logs table from growing indefinitely. Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/46120 --- spec/workers/prune_web_hook_logs_worker_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 spec/workers/prune_web_hook_logs_worker_spec.rb (limited to 'spec') diff --git a/spec/workers/prune_web_hook_logs_worker_spec.rb b/spec/workers/prune_web_hook_logs_worker_spec.rb new file mode 100644 index 00000000000..d7d64a1f641 --- /dev/null +++ b/spec/workers/prune_web_hook_logs_worker_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe PruneWebHookLogsWorker do + describe '#perform' do + before do + hook = create(:project_hook) + + 5.times do + create(:web_hook_log, web_hook: hook, created_at: 5.months.ago) + end + + create(:web_hook_log, web_hook: hook, response_status: '404') + end + + it 'removes all web hook logs older than one month' do + described_class.new.perform + + expect(WebHookLog.count).to eq(1) + expect(WebHookLog.first.response_status).to eq('404') + end + end +end -- cgit v1.2.1 From d907b894bed848e5c60f101a3cd3172df35dd27a Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Sat, 30 Jun 2018 12:31:36 +0800 Subject: Fix notify_conflict? raising exception when branches do not exist repository.can_be_merged? can raise error if diff_head_sha or target_branch are absent, therefore check those explicitly. --- spec/models/merge_request_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'spec') diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index ec72fefd137..8c6b411ec9a 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2190,6 +2190,22 @@ describe MergeRequest do end end end + + context 'source branch is missing' do + subject { create(:merge_request, :invalid, :opened, merge_status: :unchecked, target_branch: 'master') } + + before do + allow(subject.project.repository).to receive(:can_be_merged?).and_call_original + end + + it 'does not raise error' do + expect(notification_service).not_to receive(:merge_request_unmergeable) + expect(todo_service).not_to receive(:merge_request_became_unmergeable) + + expect { subject.mark_as_unmergeable }.not_to raise_error + expect(subject.cannot_be_merged?).to eq(true) + end + end end describe 'check_state?' do -- cgit v1.2.1 From 2b78f223123dbbeabafb8b5d07e367a5ebad4567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 3 Jul 2018 10:33:51 +0200 Subject: Fix link to job when creating a new issue from a failed job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/features/projects/jobs_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index d2aaf60e72c..d06abdd999b 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -165,7 +165,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do it 'links to issues/new with the title and description filled in' do button_title = "Job Failed ##{job.id}" - job_url = project_job_path(project, job) + job_url = project_job_url(project, job, host: page.server.host, port: page.server.port) options = { issue: { title: button_title, description: "Job [##{job.id}](#{job_url}) failed for #{job.sha}:\n" } } href = new_project_issue_path(project, options) -- cgit v1.2.1 From 15aadc665f266e8e974aded0fe1e0c7f1a9eb0fb Mon Sep 17 00:00:00 2001 From: "Jacob Vosmaer (GitLab)" Date: Tue, 3 Jul 2018 09:12:03 +0000 Subject: Make OperationService RPC's mandatory --- spec/features/tags/master_deletes_tag_spec.rb | 27 +--- spec/lib/gitlab/git/repository_spec.rb | 145 ++++++++++--------- spec/models/repository_spec.rb | 156 ++++----------------- spec/services/files/update_service_spec.rb | 12 -- .../services/merge_requests/rebase_service_spec.rb | 38 +---- .../services/merge_requests/squash_service_spec.rb | 45 ------ 6 files changed, 114 insertions(+), 309 deletions(-) (limited to 'spec') diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb index 9981bfa4609..1d4df2c55a7 100644 --- a/spec/features/tags/master_deletes_tag_spec.rb +++ b/spec/features/tags/master_deletes_tag_spec.rb @@ -35,30 +35,15 @@ feature 'Master deletes tag' do end context 'when pre-receive hook fails', :js do - context 'when Gitaly operation_user_delete_tag feature is enabled' do - before do - allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag) - .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags') - end - - scenario 'shows the error message' do - delete_first_tag - - expect(page).to have_content('Do not delete tags') - end + before do + allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag) + .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags') end - context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do - before do - allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) - .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags') - end - - scenario 'shows the error message' do - delete_first_tag + scenario 'shows the error message' do + delete_first_tag - expect(page).to have_content('Do not delete tags') - end + expect(page).to have_content('Do not delete tags') end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 6ec4b90d70c..615faa4e7c9 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1971,21 +1971,15 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - context 'with gitaly' do - it "calls Gitaly's OperationService" do - expect_any_instance_of(Gitlab::GitalyClient::OperationService) - .to receive(:user_ff_branch).with(user, source_sha, target_branch) - .and_return(nil) + it "calls Gitaly's OperationService" do + expect_any_instance_of(Gitlab::GitalyClient::OperationService) + .to receive(:user_ff_branch).with(user, source_sha, target_branch) + .and_return(nil) - subject - end - - it_behaves_like '#ff_merge' + subject end - context 'without gitaly', :skip_gitaly_mock do - it_behaves_like '#ff_merge' - end + it_behaves_like '#ff_merge' end describe '#delete_all_refs_except' do @@ -2308,92 +2302,95 @@ describe Gitlab::Git::Repository, seed_helper: true do expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error') end end + end - describe '#squash' do - let(:squash_id) { '1' } - let(:branch_name) { 'fix' } - let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' } - let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' } + describe '#squash' do + let(:squash_id) { '1' } + let(:branch_name) { 'fix' } + let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' } + let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' } - subject do - opts = { - branch: branch_name, - start_sha: start_sha, - end_sha: end_sha, - author: user, - message: 'Squash commit message' - } + subject do + opts = { + branch: branch_name, + start_sha: start_sha, + end_sha: end_sha, + author: user, + message: 'Squash commit message' + } - repository.squash(user, squash_id, opts) + repository.squash(user, squash_id, opts) + end + + # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234 + skip 'sparse checkout' do + let(:expected_files) { %w(files files/js files/js/application.js) } + + it 'checks out only the files in the diff' do + allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args| + m.call(*args) do + worktree_path = args[0] + files_pattern = File.join(worktree_path, '**', '*') + expected = expected_files.map do |path| + File.expand_path(path, worktree_path) + end + + expect(Dir[files_pattern]).to eq(expected) + end + end + + subject end - context 'sparse checkout', :skip_gitaly_mock do - let(:expected_files) { %w(files files/js files/js/application.js) } + context 'when the diff contains a rename' do + let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged } + let(:end_sha) { new_commit_move_file(repo).oid } - it 'checks out only the files in the diff' do + after do + # Erase our commits so other tests get the original repo + repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged + repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID) + end + + it 'does not include the renamed file in the sparse checkout' do allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args| m.call(*args) do worktree_path = args[0] files_pattern = File.join(worktree_path, '**', '*') - expected = expected_files.map do |path| - File.expand_path(path, worktree_path) - end - expect(Dir[files_pattern]).to eq(expected) + expect(Dir[files_pattern]).not_to include('CHANGELOG') + expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG') end end subject end - - context 'when the diff contains a rename' do - let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged } - let(:end_sha) { new_commit_move_file(repo).oid } - - after do - # Erase our commits so other tests get the original repo - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID) - end - - it 'does not include the renamed file in the sparse checkout' do - allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args| - m.call(*args) do - worktree_path = args[0] - files_pattern = File.join(worktree_path, '**', '*') - - expect(Dir[files_pattern]).not_to include('CHANGELOG') - expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG') - end - end - - subject - end - end end + end - context 'with an ASCII-8BIT diff', :skip_gitaly_mock do - let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" } + # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234 + skip 'with an ASCII-8BIT diff' do + let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" } - it 'applies a ASCII-8BIT diff' do - allow(repository).to receive(:run_git!).and_call_original - allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT')) + it 'applies a ASCII-8BIT diff' do + allow(repository).to receive(:run_git!).and_call_original + allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT')) - expect(subject).to match(/\h{40}/) - end + expect(subject).to match(/\h{40}/) end + end - context 'with trailing whitespace in an invalid patch', :skip_gitaly_mock do - let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" } + # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234 + skip 'with trailing whitespace in an invalid patch' do + let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" } - it 'does not include whitespace warnings in the error' do - allow(repository).to receive(:run_git!).and_call_original - allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT')) + it 'does not include whitespace warnings in the error' do + allow(repository).to receive(:run_git!).and_call_original + allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT')) - expect { subject }.to raise_error do |error| - expect(error).to be_a(described_class::GitError) - expect(error.message).not_to include('trailing whitespace') - end + expect { subject }.to raise_error do |error| + expect(error).to be_a(described_class::GitError) + expect(error.message).not_to include('trailing whitespace') end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index cfa78c4472c..d060ab923d1 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1861,155 +1861,61 @@ describe Repository do describe '#add_tag' do let(:user) { build_stubbed(:user) } - shared_examples 'adding tag' do - context 'with a valid target' do - it 'creates the tag' do - repository.add_tag(user, '8.5', 'master', 'foo') - - tag = repository.find_tag('8.5') - expect(tag).to be_present - expect(tag.message).to eq('foo') - expect(tag.dereferenced_target.id).to eq(repository.commit('master').id) - end - - it 'returns a Gitlab::Git::Tag object' do - tag = repository.add_tag(user, '8.5', 'master', 'foo') - - expect(tag).to be_a(Gitlab::Git::Tag) - end - end + context 'with a valid target' do + it 'creates the tag' do + repository.add_tag(user, '8.5', 'master', 'foo') - context 'with an invalid target' do - it 'returns false' do - expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false - end + tag = repository.find_tag('8.5') + expect(tag).to be_present + expect(tag.message).to eq('foo') + expect(tag.dereferenced_target.id).to eq(repository.commit('master').id) end - end - - context 'when Gitaly operation_user_add_tag feature is enabled' do - it_behaves_like 'adding tag' - end - - context 'when Gitaly operation_user_add_tag feature is disabled', :disable_gitaly do - it_behaves_like 'adding tag' - - it 'passes commit SHA to pre-receive and update hooks and tag SHA to post-receive hook' do - pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', project) - update_hook = Gitlab::Git::Hook.new('update', project) - post_receive_hook = Gitlab::Git::Hook.new('post-receive', project) - - allow(Gitlab::Git::Hook).to receive(:new) - .and_return(pre_receive_hook, update_hook, post_receive_hook) - - allow(pre_receive_hook).to receive(:trigger).and_call_original - allow(update_hook).to receive(:trigger).and_call_original - allow(post_receive_hook).to receive(:trigger).and_call_original + it 'returns a Gitlab::Git::Tag object' do tag = repository.add_tag(user, '8.5', 'master', 'foo') - commit_sha = repository.commit('master').id - tag_sha = tag.target + expect(tag).to be_a(Gitlab::Git::Tag) + end + end - expect(pre_receive_hook).to have_received(:trigger) - .with(anything, anything, anything, commit_sha, anything) - expect(update_hook).to have_received(:trigger) - .with(anything, anything, anything, commit_sha, anything) - expect(post_receive_hook).to have_received(:trigger) - .with(anything, anything, anything, tag_sha, anything) + context 'with an invalid target' do + it 'returns false' do + expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false end end end describe '#rm_branch' do - shared_examples "user deleting a branch" do - it 'removes a branch' do - expect(repository).to receive(:before_remove_branch) - expect(repository).to receive(:after_remove_branch) + it 'removes a branch' do + expect(repository).to receive(:before_remove_branch) + expect(repository).to receive(:after_remove_branch) - repository.rm_branch(user, 'feature') - end + repository.rm_branch(user, 'feature') end - context 'with gitaly enabled' do - it_behaves_like "user deleting a branch" - - context 'when pre hooks failed' do - before do - allow_any_instance_of(Gitlab::GitalyClient::OperationService) - .to receive(:user_delete_branch).and_raise(Gitlab::Git::PreReceiveError) - end - - it 'gets an error and does not delete the branch' do - expect do - repository.rm_branch(user, 'feature') - end.to raise_error(Gitlab::Git::PreReceiveError) - - expect(repository.find_branch('feature')).not_to be_nil - end - end - end - - context 'with gitaly disabled', :disable_gitaly do - it_behaves_like "user deleting a branch" - - let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature - let(:blank_sha) { '0000000000000000000000000000000000000000' } - - context 'when pre hooks were successful' do - it 'runs without errors' do - expect_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) - .with(git_user, repository.raw_repository, old_rev, blank_sha, 'refs/heads/feature') - - expect { repository.rm_branch(user, 'feature') }.not_to raise_error - end - - it 'deletes the branch' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) - - expect { repository.rm_branch(user, 'feature') }.not_to raise_error - - expect(repository.find_branch('feature')).to be_nil - end + context 'when pre hooks failed' do + before do + allow_any_instance_of(Gitlab::GitalyClient::OperationService) + .to receive(:user_delete_branch).and_raise(Gitlab::Git::PreReceiveError) end - context 'when pre hooks failed' do - it 'gets an error' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) - - expect do - repository.rm_branch(user, 'feature') - end.to raise_error(Gitlab::Git::PreReceiveError) - end - - it 'does not delete the branch' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) + it 'gets an error and does not delete the branch' do + expect do + repository.rm_branch(user, 'feature') + end.to raise_error(Gitlab::Git::PreReceiveError) - expect do - repository.rm_branch(user, 'feature') - end.to raise_error(Gitlab::Git::PreReceiveError) - expect(repository.find_branch('feature')).not_to be_nil - end + expect(repository.find_branch('feature')).not_to be_nil end end end describe '#rm_tag' do - shared_examples 'removing tag' do - it 'removes a tag' do - expect(repository).to receive(:before_remove_tag) + it 'removes a tag' do + expect(repository).to receive(:before_remove_tag) - repository.rm_tag(build_stubbed(:user), 'v1.1.0') - - expect(repository.find_tag('v1.1.0')).to be_nil - end - end - - context 'when Gitaly operation_user_delete_tag feature is enabled' do - it_behaves_like 'removing tag' - end + repository.rm_tag(build_stubbed(:user), 'v1.1.0') - context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do - it_behaves_like 'removing tag' + expect(repository.find_tag('v1.1.0')).to be_nil end end diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb index 16bfbdf3089..eaee89fb1a5 100644 --- a/spec/services/files/update_service_spec.rb +++ b/spec/services/files/update_service_spec.rb @@ -71,17 +71,5 @@ describe Files::UpdateService do expect(results.data).to eq(new_contents) end end - - context 'with gitaly disabled', :skip_gitaly_mock do - context 'when target branch is different than source branch' do - let(:branch_name) { "#{project.default_branch}-new" } - - it 'fires hooks only once' do - expect(Gitlab::Git::HooksService).to receive(:new).once.and_call_original - - subject.execute - end - end - end end end diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb index 757c31ab692..4daa25f8cf2 100644 --- a/spec/services/merge_requests/rebase_service_spec.rb +++ b/spec/services/merge_requests/rebase_service_spec.rb @@ -36,9 +36,9 @@ describe MergeRequests::RebaseService do end end - context 'when unexpected error occurs', :disable_gitaly do + context 'when unexpected error occurs' do before do - allow(repository).to receive(:run_git!).and_raise('Something went wrong') + allow(repository).to receive(:gitaly_operation_client).and_raise('Something went wrong') end it 'saves a generic error message' do @@ -53,9 +53,9 @@ describe MergeRequests::RebaseService do end end - context 'with git command failure', :disable_gitaly do + context 'with git command failure' do before do - allow(repository).to receive(:run_git!).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong') + allow(repository).to receive(:gitaly_operation_client).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong') end it 'saves a generic error message' do @@ -71,7 +71,7 @@ describe MergeRequests::RebaseService do end context 'valid params' do - shared_examples 'successful rebase' do + describe 'successful rebase' do before do service.execute(merge_request) end @@ -97,26 +97,8 @@ describe MergeRequests::RebaseService do end end - context 'when Gitaly rebase feature is enabled' do - it_behaves_like 'successful rebase' - end - - context 'when Gitaly rebase feature is disabled', :disable_gitaly do - it_behaves_like 'successful rebase' - end - - context 'git commands', :disable_gitaly do - it 'sets GL_REPOSITORY env variable when calling git commands' do - expect(repository).to receive(:popen).exactly(3) - .with(anything, anything, hash_including('GL_REPOSITORY'), anything) - .and_return(['', 0]) - - service.execute(merge_request) - end - end - context 'fork' do - shared_examples 'successful fork rebase' do + describe 'successful fork rebase' do let(:forked_project) do fork_project(project, user, repository: true) end @@ -140,14 +122,6 @@ describe MergeRequests::RebaseService do expect(parent_sha).to eq(target_branch_sha) end end - - context 'when Gitaly rebase feature is enabled' do - it_behaves_like 'successful fork rebase' - end - - context 'when Gitaly rebase feature is disabled', :disable_gitaly do - it_behaves_like 'successful fork rebase' - end end end end diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb index ded17fa92a4..8ab09412f55 100644 --- a/spec/services/merge_requests/squash_service_spec.rb +++ b/spec/services/merge_requests/squash_service_spec.rb @@ -124,51 +124,6 @@ describe MergeRequests::SquashService do message: a_string_including('squash')) end end - - context 'with Gitaly disabled', :skip_gitaly_mock do - stages = { - 'add worktree for squash' => 'worktree', - 'configure sparse checkout' => 'config', - 'get files in diff' => 'diff --name-only', - 'check out target branch' => 'checkout', - 'apply patch' => 'diff --binary', - 'commit squashed changes' => 'commit', - 'get SHA of squashed commit' => 'rev-parse' - } - - stages.each do |stage, command| - context "when the #{stage} stage fails" do - before do - git_command = a_collection_containing_exactly( - a_string_starting_with("#{Gitlab.config.git.bin_path} #{command}") - ).or( - a_collection_starting_with([Gitlab.config.git.bin_path] + command.split) - ) - - allow(repository).to receive(:popen).and_return(['', 0]) - allow(repository).to receive(:popen).with(git_command, anything, anything, anything).and_return([error, 1]) - end - - it 'logs the stage and output' do - expect(service).to receive(:log_error).with(log_error) - expect(service).to receive(:log_error).with(error) - - service.execute(merge_request) - end - - it 'returns an error' do - expect(service.execute(merge_request)).to match(status: :error, - message: a_string_including('squash')) - end - - it 'cleans up the temporary directory' do - expect(File.exist?(squash_dir_path)).to be(false) - - service.execute(merge_request) - end - end - end - end end context 'when any other exception is thrown' do -- cgit v1.2.1 From 15ec6a13eb4d839d252315bf5b0a50d28351cb5f Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Tue, 3 Jul 2018 11:08:14 +0100 Subject: Temporarily remove MR diffs removal migration We will re-add this with a more efficient bulk scheduling method. --- .../background_migration/delete_diff_files_spec.rb | 2 +- .../enqueue_delete_diff_files_workers_spec.rb | 48 ---------------------- 2 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 spec/migrations/enqueue_delete_diff_files_workers_spec.rb (limited to 'spec') diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb index a251ab323d8..1b3df7b20d4 100644 --- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb +++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do +describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180626125654 do describe '#perform' do context 'when diff files can be deleted' do let(:merge_request) { create(:merge_request, :merged) } diff --git a/spec/migrations/enqueue_delete_diff_files_workers_spec.rb b/spec/migrations/enqueue_delete_diff_files_workers_spec.rb deleted file mode 100644 index 686027822b8..00000000000 --- a/spec/migrations/enqueue_delete_diff_files_workers_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'spec_helper' -require Rails.root.join('db', 'post_migrate', '20180619121030_enqueue_delete_diff_files_workers.rb') - -describe EnqueueDeleteDiffFilesWorkers, :migration, :sidekiq do - let(:merge_request_diffs) { table(:merge_request_diffs) } - let(:merge_requests) { table(:merge_requests) } - let(:namespaces) { table(:namespaces) } - let(:projects) { table(:projects) } - - before do - stub_const("#{described_class.name}::BATCH_SIZE", 2) - - namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab') - projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab') - - merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged') - - merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected') - merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'without_files') - merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'collected') - merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected') - merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'empty') - merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected') - - merge_requests.update(1, latest_merge_request_diff_id: 6) - end - - it 'correctly schedules diff file deletion workers' do - Sidekiq::Testing.fake! do - Timecop.freeze do - migrate! - - # 1st batch - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(8.minutes, 1) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(9.minutes, 3) - # 2nd batch - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(16.minutes, 4) - expect(described_class::MIGRATION).to be_scheduled_delayed_migration(17.minutes, 6) - expect(BackgroundMigrationWorker.jobs.size).to eq(4) - end - end - end - - it 'migrates the data' do - expect { migrate! }.to change { merge_request_diffs.where(state: 'without_files').count } - .from(1).to(4) - end -end -- cgit v1.2.1 From 825b9435eddd86adb3fd76f87129c9bffba0315f Mon Sep 17 00:00:00 2001 From: Imre Farkas Date: Tue, 3 Jul 2018 11:58:03 +0000 Subject: Add readme button to non-empty project page --- .../show/user_sees_setup_shortcut_buttons_spec.rb | 60 +++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb index e44361fbe26..7b9242f0631 100644 --- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb +++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb @@ -5,6 +5,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do # see spec/features/projects/files/project_owner_creates_license_file_spec.rb # see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb + include FakeBlobHelpers + let(:user) { create(:user) } describe 'empty project' do @@ -141,11 +143,57 @@ describe 'Projects > Show > User sees setup shortcut buttons' do allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false) project.add_master(user) sign_in(user) + end - visit project_path(project) + context 'Readme button' do + before do + allow(Project).to receive(:find_by_full_path) + .with(project.full_path, follow_redirects: true) + .and_return(project) + end + + context 'when the project has a populated Readme' do + it 'show the "Readme" anchor' do + visit project_path(project) + + expect(project.repository.readme).not_to be_nil + + page.within('.project-stats') do + expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path) + expect(page).to have_link('Readme', href: presenter.readme_path) + end + end + + context 'when the project has an empty Readme' do + it 'show the "Readme" anchor' do + allow(project.repository).to receive(:readme).and_return(fake_blob(path: 'README.md', data: '', size: 0)) + + visit project_path(project) + + page.within('.project-stats') do + expect(page).not_to have_link('Add Readme', href: presenter.add_readme_path) + expect(page).to have_link('Readme', href: presenter.readme_path) + end + end + end + end + + context 'when the project does not have a Readme' do + it 'shows the "Add Readme" button' do + allow(project.repository).to receive(:readme).and_return(nil) + + visit project_path(project) + + page.within('.project-stats') do + expect(page).to have_link('Add Readme', href: presenter.add_readme_path) + end + end + end end it 'no "Add Changelog" button if the project already has a changelog' do + visit project_path(project) + expect(project.repository.changelog).not_to be_nil page.within('.project-stats') do @@ -154,6 +202,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do end it 'no "Add License" button if the project already has a license' do + visit project_path(project) + expect(project.repository.license_blob).not_to be_nil page.within('.project-stats') do @@ -162,6 +212,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do end it 'no "Add Contribution guide" button if the project already has a contribution guide' do + visit project_path(project) + expect(project.repository.contribution_guide).not_to be_nil page.within('.project-stats') do @@ -171,6 +223,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do describe 'GitLab CI configuration button' do it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do + visit project_path(project) + expect(project.repository.gitlab_ci_yml).to be_nil page.within('.project-stats') do @@ -211,6 +265,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do describe 'Auto DevOps button' do it '"Enable Auto DevOps" button linked to settings page' do + visit project_path(project) + page.within('.project-stats') do expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings')) end @@ -263,6 +319,8 @@ describe 'Projects > Show > User sees setup shortcut buttons' do describe 'Kubernetes cluster button' do it '"Add Kubernetes cluster" button linked to clusters page' do + visit project_path(project) + page.within('.project-stats') do expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project)) end -- cgit v1.2.1