diff options
author | Shinya Maeda <shinya@gitlab.com> | 2018-06-12 13:13:03 +0900 |
---|---|---|
committer | Shinya Maeda <shinya@gitlab.com> | 2018-06-12 13:13:03 +0900 |
commit | d7a3180d06e7b16728d4f23b1e68007c9c2f3b9a (patch) | |
tree | d111815ee462b6dd0b29af9c158d7d1abc24f505 /spec | |
parent | 644529590a263f8db215d288c2f59abbe632a09b (diff) | |
parent | 26c9d71666d0350b17431a75d8c31d0316bd7220 (diff) | |
download | gitlab-ce-d7a3180d06e7b16728d4f23b1e68007c9c2f3b9a.tar.gz |
Merge branch 'master' into build-chunks-on-object-storage
Diffstat (limited to 'spec')
138 files changed, 1652 insertions, 609 deletions
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 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/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 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/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 6e710c9b20b..22858de0475 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -701,7 +701,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 92886e93077..9618a8417ec 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -253,7 +253,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/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/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/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 376b229ffc9..1df2c954893 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 @@ -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 @@ -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 @@ -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 @@ -314,19 +314,19 @@ 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 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 @@ -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 @@ -560,5 +560,43 @@ describe UploadsController do end end end + + context 'original filename or a version filename must match' do + 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 + 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 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/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/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index 5ce1988c76f..b77f702f9e1 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -3,5 +3,14 @@ FactoryBot.define do project enabled true domain "example.com" + deploy_strategy :continuous + + trait :manual do + deploy_strategy :manual + end + + trait :disabled do + enabled false + end end end diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index d91dcf76191..a5e0ac592b9 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -76,6 +76,26 @@ 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('.appearance-light-logo-preview') + + click_link 'Remove favicon' + + expect(page).not_to have_css('.appearance-light-logo-preview') + + # 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, ico' + end + def expect_custom_sign_in_appearance(appearance) expect(page).to have_content appearance.title expect(page).to have_content appearance.description 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/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/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/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/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/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/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 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/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/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/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb index 70e8d436dcb..fafd338e448 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' @@ -68,7 +68,7 @@ feature 'Labels subscription' do find('.dropdown-group-label').click page.within('.dropdown-group-label') do - find('a.js-subscribe-button', text: text).click + find('.js-subscribe-button', text: text).click end end end 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 diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb index f4fda6de465..efa74015c6e 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,16 @@ 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 + 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") + 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 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/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 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/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 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/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( %{<img style="height: 50px" src="/uploads/-/system/appearance/header_logo/#{appearance.id}/dk.png" alt="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/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index b77114a8152..cf98eed27f1 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -40,23 +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 - 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/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index c9d2ec8a4ae..363ebc88afd 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -33,7 +33,7 @@ describe PreferencesHelper do it "returns user's theme's css_class" do stub_user(theme_id: 3) - 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/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', () => { 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`); }); }); }); 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(); }); }); 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', () => { 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..38ffa317e8e 100644 --- a/spec/javascripts/ide/lib/common/model_manager_spec.js +++ b/spec/javascripts/ide/lib/common/model_manager_spec.js @@ -1,18 +1,12 @@ -/* global monaco */ 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(); }); afterEach(() => { diff --git a/spec/javascripts/ide/lib/common/model_spec.js b/spec/javascripts/ide/lib/common/model_spec.js index c278bf92b08..f096e06f43c 100644 --- a/spec/javascripts/ide/lib/common/model_spec.js +++ b/spec/javascripts/ide/lib/common/model_spec.js @@ -1,23 +1,17 @@ -/* global monaco */ 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(f); }); afterEach(() => { @@ -38,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 e1c4ca570b6..a112361e0d1 100644 --- a/spec/javascripts/ide/lib/decorations/controller_spec.js +++ b/spec/javascripts/ide/lib/decorations/controller_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 DecorationsController from '~/ide/lib/decorations/controller'; import Model from '~/ide/lib/common/model'; import { file } from '../../helpers'; @@ -10,16 +8,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(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..96abd1dcd9e 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 { 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'; 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(); + 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(() => { @@ -170,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', diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js index b88a12264ca..c1932284d53 100644 --- a/spec/javascripts/ide/lib/editor_spec.js +++ b/spec/javascripts/ide/lib/editor_spec.js @@ -1,6 +1,5 @@ -/* global monaco */ -import monacoLoader from '~/ide/monaco_loader'; -import editor from '~/ide/lib/editor'; +import { editor as monacoEditor } from 'monaco-editor'; +import Editor from '~/ide/lib/editor'; import { file } from '../helpers'; describe('Multi-file editor library', () => { @@ -8,18 +7,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,20 +24,20 @@ 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', () => { 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', () => { @@ -60,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: { 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); - }); -}); 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' }, ); 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/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 27f06573432..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', () => { @@ -395,6 +396,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 +415,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,12 +423,43 @@ 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'); }); }); + 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; @@ -434,6 +467,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 +484,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, @@ -458,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/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js new file mode 100644 index 00000000000..c7f4092911c --- /dev/null +++ b/spec/javascripts/lib/utils/url_utility_spec.js @@ -0,0 +1,29 @@ +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( + '/-/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', + ); + }); + }); + }); +}); 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/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..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 @@ -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', @@ -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/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', () => { 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'); }); }); 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/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 diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb new file mode 100644 index 00000000000..08c4a474217 --- /dev/null +++ b/spec/lib/gitlab/favicon_spec.rb @@ -0,0 +1,52 @@ +require 'rails_helper' + +RSpec.describe Gitlab::Favicon, :request_store do + describe '.main' do + it 'defaults to favicon.png' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) + 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 match_asset_path '/assets/favicon-blue.png' + end + + it 'has yellow favicon for canary' do + stub_env('CANARY', 'true') + expect(described_class.main).to match_asset_path 'favicon-yellow.png' + end + + 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} + end + end + + describe '.status_overlay' do + subject { described_class.status_overlay('favicon_status_created') } + + it 'returns the overlay for the status' do + expect(subject).to match_asset_path '/assets/ci_favicons/favicon_status_created.png' + end + end + + 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/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<br>error message from the hook line 2<br>") + 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<br>world<br>') + end +end 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 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/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/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/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/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 5b289ceb3b2..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 @@ -540,6 +540,7 @@ ProjectAutoDevops: - id - enabled - domain +- deploy_strategy - project_id - created_at - updated_at 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/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb index ecacea6bb35..a8213988f70 100644 --- a/spec/lib/gitlab/themes_spec.rb +++ b/spec/lib/gitlab/themes_spec.rb @@ -5,9 +5,9 @@ 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 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/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 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 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/models/group_spec.rb b/spec/models/group_spec.rb index f83b52e8975..9fe1186a8c9 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) } @@ -240,7 +264,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/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/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 7545c0797e9..749b2094787 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) } @@ -67,8 +69,127 @@ describe ProjectAutoDevops do end end + context 'when deploy_strategy is manual' do + let(:domain) { 'example.com' } + + before do + auto_devops.deploy_strategy = 'manual' + end + + it do + expect(auto_devops.predefined_variables.map { |var| var[:key] }) + .to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED") + 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 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 diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 54ef0be67ff..b9f1c7dd5df 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,6 +165,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/#{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 +176,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: favicon_path }, status: { resolved: true } } ) @@ -464,4 +467,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('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/models/project_spec.rb b/spec/models/project_spec.rb index fe9d64c0e3b..926365e409a 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, @@ -960,7 +967,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 @@ -1693,6 +1700,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)) } @@ -1701,7 +1733,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 + 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) .and_call_original end @@ -3366,10 +3402,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) 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/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/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 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 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/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..16e6f19773f 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,6 +860,12 @@ describe API::Runner, :clean_gitlab_redis_shared_state do new_params = params.merge(token: token) put api("/jobs/#{job.id}"), new_params end + + def update_job_after_time(update_interval = 20.minutes, state = 'running') + Timecop.travel(job.updated_at + update_interval) do + update_job(job.token, state: state) + end + end end describe 'PATCH /api/v4/jobs/:id/trace' do @@ -979,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 @@ -1055,8 +1085,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/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 <br> (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 e0e6eecb300..eb4235e3ee6 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -179,7 +179,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..0b010ebd507 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -18,17 +18,7 @@ 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') - 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') - 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(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png') end end end 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 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/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb new file mode 100644 index 00000000000..7f536ce4e68 --- /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', :request_store do + Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher } + + service.build_new_note_recipients(note) + + control_count = ActiveRecord::QueryRecorder.new do + service.build_new_note_recipients(note) + end + + Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher } + + expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count) + 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 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/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 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..1bffeee6790 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -4,13 +4,13 @@ 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" + filename = "spec/fixtures/pages.#{extension}.meta" fixture_file_upload(filename) if File.exist?(filename) end @@ -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/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index e28b0ea5cf2..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,6 +770,8 @@ describe SystemNoteService do end describe "new reference" do + let(:favicon_path) { "http://localhost/assets/#{find_asset('favicon.png').digest_path}" } + before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) end @@ -789,7 +792,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: favicon_path }, status: { resolved: false } } ) @@ -815,7 +818,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: favicon_path }, status: { resolved: false } } ) @@ -841,7 +844,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: favicon_path }, status: { resolved: false } } ) 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') 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/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 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 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/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/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/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb new file mode 100644 index 00000000000..37deea8ab90 --- /dev/null +++ b/spec/uploaders/favicon_uploader_spec.rb @@ -0,0 +1,29 @@ +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 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/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 42036d67f3d..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(Rails.root.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 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 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 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 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 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 |