diff options
author | Phil Hughes <me@iamphill.com> | 2017-05-03 17:04:47 +0100 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-03 17:04:47 +0100 |
commit | f29e5d4d93123ea73e5f58be46020781af2ef7cb (patch) | |
tree | d08a8f8e5a9cf30dd4f238c187673ffe30ac39b3 /spec | |
parent | 4134d700623404948f163349882caf4a6d940cf3 (diff) | |
parent | 8f29bf96b9306fd95b9b98019e80173b47b3a6b8 (diff) | |
download | gitlab-ce-f29e5d4d93123ea73e5f58be46020781af2ef7cb.tar.gz |
Merge branch 'master' into deploy-keys-load-async
Diffstat (limited to 'spec')
181 files changed, 3970 insertions, 1063 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 760f33b09c1..1bf0533ca24 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -4,7 +4,7 @@ describe ApplicationController do let(:user) { create(:user) } describe '#check_password_expiration' do - let(:controller) { ApplicationController.new } + let(:controller) { described_class.new } it 'redirects if the user is over their password expiry' do user.password_expires_at = Time.new(2002) @@ -34,7 +34,7 @@ describe ApplicationController do describe "#authenticate_user_from_token!" do describe "authenticating a user from a private token" do - controller(ApplicationController) do + controller(described_class) do def index render text: "authenticated" end @@ -66,7 +66,7 @@ describe ApplicationController do end describe "authenticating a user from a personal access token" do - controller(ApplicationController) do + controller(described_class) do def index render text: 'authenticated' end @@ -115,7 +115,7 @@ describe ApplicationController do end context 'two-factor authentication' do - let(:controller) { ApplicationController.new } + let(:controller) { described_class.new } describe '#check_two_factor_requirement' do subject { controller.send :check_two_factor_requirement } diff --git a/spec/controllers/blob_controller_spec.rb b/spec/controllers/blob_controller_spec.rb deleted file mode 100644 index 44e011fd3a8..00000000000 --- a/spec/controllers/blob_controller_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'spec_helper' - -describe Projects::BlobController do - let(:project) { create(:project, :repository) } - let(:user) { create(:user) } - - before do - sign_in(user) - - project.team << [user, :master] - - allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz']) - allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0']) - controller.instance_variable_set(:@project, project) - end - - describe "GET show" do - render_views - - before do - get(:show, - namespace_id: project.namespace, - project_id: project, - id: id) - end - - context "valid branch, valid file" do - let(:id) { 'master/README.md' } - it { is_expected.to respond_with(:success) } - end - - context "valid branch, invalid file" do - let(:id) { 'master/invalid-path.rb' } - it { is_expected.to respond_with(:not_found) } - end - - context "invalid branch, valid file" do - let(:id) { 'invalid-branch/README.md' } - it { is_expected.to respond_with(:not_found) } - end - - context "binary file" do - let(:id) { 'binary-encoding/encoding/binary-1.bin' } - it { is_expected.to respond_with(:success) } - end - end - - describe 'GET show with tree path' do - render_views - - before do - get(:show, - namespace_id: project.namespace, - project_id: project, - id: id) - controller.instance_variable_set(:@blob, nil) - end - - context 'redirect to tree' do - let(:id) { 'markdown/doc' } - it 'redirects' do - expect(subject). - to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") - end - end - end -end diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb index 6e4b5f78e33..7cf2996ffd0 100644 --- a/spec/controllers/groups/milestones_controller_spec.rb +++ b/spec/controllers/groups/milestones_controller_spec.rb @@ -6,6 +6,16 @@ describe Groups::MilestonesController do let(:project2) { create(:empty_project, group: group) } let(:user) { create(:user) } let(:title) { '肯定不是中文的问题' } + let(:milestone) do + project_milestone = create(:milestone, project: project) + + GroupMilestone.build( + group, + [project], + project_milestone.title + ) + end + let(:milestone_path) { group_milestone_path(group, milestone.safe_title, title: milestone.title) } before do sign_in(user) @@ -14,6 +24,8 @@ describe Groups::MilestonesController do controller.instance_variable_set(:@group, group) end + it_behaves_like 'milestone tabs' + describe "#create" do it "creates group milestone with Chinese title" do post :create, diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb new file mode 100644 index 00000000000..d321bfcea9d --- /dev/null +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Oauth::AuthorizationsController do + let(:user) { create(:user) } + + let(:doorkeeper) do + Doorkeeper::Application.create( + name: "MyApp", + redirect_uri: 'http://example.com', + scopes: "") + end + + let(:params) do + { + response_type: "code", + client_id: doorkeeper.uid, + redirect_uri: doorkeeper.redirect_uri, + state: 'state' + } + end + + before do + sign_in(user) + end + + describe 'GET #new' do + context 'without valid params' do + it 'returns 200 code and renders error view' do + get :new + + expect(response).to have_http_status(200) + expect(response).to render_template('doorkeeper/authorizations/error') + end + end + + context 'with valid params' do + it 'returns 200 code and renders view' do + get :new, params + + expect(response).to have_http_status(200) + expect(response).to render_template('doorkeeper/authorizations/new') + end + + it 'deletes session.user_return_to and redirects when skip authorization' do + request.session['user_return_to'] = 'http://example.com' + allow(controller).to receive(:skip_authorization?).and_return(true) + + get :new, params + + expect(request.session['user_return_to']).to be_nil + expect(response).to have_http_status(302) + end + end + end +end diff --git a/spec/controllers/profiles/personal_access_tokens_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb index 98a43e278b2..98a43e278b2 100644 --- a/spec/controllers/profiles/personal_access_tokens_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 0fd09d156c4..3b3caa9d3e6 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -3,6 +3,57 @@ require 'rails_helper' describe Projects::BlobController do let(:project) { create(:project, :public, :repository) } + describe "GET show" do + render_views + + context 'with file path' do + before do + get(:show, + namespace_id: project.namespace, + project_id: project, + id: id) + end + + context "valid branch, valid file" do + let(:id) { 'master/README.md' } + it { is_expected.to respond_with(:success) } + end + + context "valid branch, invalid file" do + let(:id) { 'master/invalid-path.rb' } + it { is_expected.to respond_with(:not_found) } + end + + context "invalid branch, valid file" do + let(:id) { 'invalid-branch/README.md' } + it { is_expected.to respond_with(:not_found) } + end + + context "binary file" do + let(:id) { 'binary-encoding/encoding/binary-1.bin' } + it { is_expected.to respond_with(:success) } + end + end + + context 'with tree path' do + before do + get(:show, + namespace_id: project.namespace, + project_id: project, + id: id) + controller.instance_variable_set(:@blob, nil) + end + + context 'redirect to tree' do + let(:id) { 'markdown/doc' } + it 'redirects' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc") + end + end + end + end + describe 'GET diff' do let(:user) { create(:user) } diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb new file mode 100644 index 00000000000..89692b601b2 --- /dev/null +++ b/spec/controllers/projects/deployments_controller_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Projects::DeploymentsController do + include ApiHelpers + + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:environment) { create(:environment, name: 'production', project: project) } + + before do + project.add_master(user) + + sign_in(user) + end + + describe 'GET #index' do + it 'returns list of deployments from last 8 hours' do + create(:deployment, environment: environment, created_at: 9.hours.ago) + create(:deployment, environment: environment, created_at: 7.hours.ago) + create(:deployment, environment: environment) + + get :index, environment_params(after: 8.hours.ago) + + expect(response).to be_ok + + expect(json_response['deployments'].count).to eq(2) + end + + it 'returns a list with deployments information' do + create(:deployment, environment: environment) + + get :index, environment_params + + expect(response).to be_ok + expect(response).to match_response_schema('deployments') + end + end + + def environment_params(opts = {}) + opts.reverse_merge(namespace_id: project.namespace, project_id: project, environment_id: environment.id) + end +end diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 47e61c3cea8..84a61b2784e 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -7,6 +7,7 @@ describe Projects::MilestonesController do let(:issue) { create(:issue, project: project, milestone: milestone) } let!(:label) { create(:label, project: project, title: 'Issue Label', issues: [issue]) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) } + let(:milestone_path) { namespace_project_milestone_path } before do sign_in(user) @@ -14,6 +15,8 @@ describe Projects::MilestonesController do controller.instance_variable_set(:@project, project) end + it_behaves_like 'milestone tabs' + describe "#show" do render_views diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index f140eaef5d5..45f4cf9180d 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -167,6 +167,47 @@ describe Projects::NotesController do end end + describe 'DELETE destroy' do + let(:request_params) do + { + namespace_id: project.namespace, + project_id: project, + id: note, + format: :js + } + end + + context 'user is the author of a note' do + before do + sign_in(note.author) + project.team << [note.author, :developer] + end + + it "returns status 200 for html" do + delete :destroy, request_params + + expect(response).to have_http_status(200) + end + + it "deletes the note" do + expect { delete :destroy, request_params }.to change { Note.count }.from(1).to(0) + end + end + + context 'user is not the author of a note' do + before do + sign_in(user) + project.team << [user, :developer] + end + + it "returns status 404" do + delete :destroy, request_params + + expect(response).to have_http_status(404) + end + end + end + describe 'POST toggle_award_emoji' do before do sign_in(user) diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todos_controller_spec.rb index c5a4153d991..c5a4153d991 100644 --- a/spec/controllers/projects/todo_controller_spec.rb +++ b/spec/controllers/projects/todos_controller_spec.rb diff --git a/spec/controllers/snippets/notes_controller_spec.rb b/spec/controllers/snippets/notes_controller_spec.rb new file mode 100644 index 00000000000..1c494b8c7ab --- /dev/null +++ b/spec/controllers/snippets/notes_controller_spec.rb @@ -0,0 +1,196 @@ +require 'spec_helper' + +describe Snippets::NotesController do + let(:user) { create(:user) } + + let(:private_snippet) { create(:personal_snippet, :private) } + let(:internal_snippet) { create(:personal_snippet, :internal) } + let(:public_snippet) { create(:personal_snippet, :public) } + + let(:note_on_private) { create(:note_on_personal_snippet, noteable: private_snippet) } + let(:note_on_internal) { create(:note_on_personal_snippet, noteable: internal_snippet) } + let(:note_on_public) { create(:note_on_personal_snippet, noteable: public_snippet) } + + describe 'GET index' do + context 'when a snippet is public' do + before do + note_on_public + + get :index, { snippet_id: public_snippet } + end + + it "returns status 200" do + expect(response).to have_http_status(200) + end + + it "returns not empty array of notes" do + expect(JSON.parse(response.body)["notes"].empty?).to be_falsey + end + end + + context 'when a snippet is internal' do + before do + note_on_internal + end + + context 'when user not logged in' do + it "returns status 404" do + get :index, { snippet_id: internal_snippet } + + expect(response).to have_http_status(404) + end + end + + context 'when user logged in' do + before do + sign_in(user) + end + + it "returns status 200" do + get :index, { snippet_id: internal_snippet } + + expect(response).to have_http_status(200) + end + end + end + + context 'when a snippet is private' do + before do + note_on_private + end + + context 'when user not logged in' do + it "returns status 404" do + get :index, { snippet_id: private_snippet } + + expect(response).to have_http_status(404) + end + end + + context 'when user other than author logged in' do + before do + sign_in(user) + end + + it "returns status 404" do + get :index, { snippet_id: private_snippet } + + expect(response).to have_http_status(404) + end + end + + context 'when author logged in' do + before do + note_on_private + + sign_in(private_snippet.author) + end + + it "returns status 200" do + get :index, { snippet_id: private_snippet } + + expect(response).to have_http_status(200) + end + + it "returns 1 note" do + get :index, { snippet_id: private_snippet } + + expect(JSON.parse(response.body)['notes'].count).to eq(1) + end + end + end + + context 'dont show non visible notes' do + before do + note_on_public + + sign_in(user) + + expect_any_instance_of(Note).to receive(:cross_reference_not_visible_for?).and_return(true) + end + + it "does not return any note" do + get :index, { snippet_id: public_snippet } + + expect(JSON.parse(response.body)['notes'].count).to eq(0) + end + end + end + + describe 'DELETE destroy' do + let(:request_params) do + { + snippet_id: public_snippet, + id: note_on_public, + format: :js + } + end + + context 'when user is the author of a note' do + before do + sign_in(note_on_public.author) + end + + it "returns status 200" do + delete :destroy, request_params + + expect(response).to have_http_status(200) + end + + it "deletes the note" do + expect{ delete :destroy, request_params }.to change{ Note.count }.from(1).to(0) + end + + context 'system note' do + before do + expect_any_instance_of(Note).to receive(:system?).and_return(true) + end + + it "does not delete the note" do + expect{ delete :destroy, request_params }.not_to change{ Note.count } + end + end + end + + context 'when user is not the author of a note' do + before do + sign_in(user) + + note_on_public + end + + it "returns status 404" do + delete :destroy, request_params + + expect(response).to have_http_status(404) + end + + it "does not update the note" do + expect{ delete :destroy, request_params }.not_to change{ Note.count } + end + end + end + + describe 'POST toggle_award_emoji' do + let(:note) { create(:note_on_personal_snippet, noteable: public_snippet) } + before do + sign_in(user) + end + + subject { post(:toggle_award_emoji, snippet_id: public_snippet, id: note.id, name: "thumbsup") } + + it "toggles the award emoji" do + expect { subject }.to change { note.award_emoji.count }.by(1) + + expect(response).to have_http_status(200) + end + + it "removes the already awarded emoji when it exists" do + note.toggle_award_emoji('thumbsup', user) # create award emoji before + + expect { subject }.to change { AwardEmoji.count }.by(-1) + + expect(response).to have_http_status(200) + end + end +end diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index 234f3edd3d8..41cd5bdcdd8 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -350,144 +350,138 @@ describe SnippetsController do end end - %w(raw download).each do |action| - describe "GET #{action}" do - context 'when the personal snippet is private' do - let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + describe "GET #raw" do + context 'when the personal snippet is private' do + let(:personal_snippet) { create(:personal_snippet, :private, author: user) } - context 'when signed in' do - before do - sign_in(user) - end + context 'when signed in' do + before do + sign_in(user) + end - context 'when signed in user is not the author' do - let(:other_author) { create(:author) } - let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } + context 'when signed in user is not the author' do + let(:other_author) { create(:author) } + let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } - it 'responds with status 404' do - get action, id: other_personal_snippet.to_param + it 'responds with status 404' do + get :raw, id: other_personal_snippet.to_param - expect(response).to have_http_status(404) - end + expect(response).to have_http_status(404) end + end - context 'when signed in user is the author' do - before { get action, id: personal_snippet.to_param } + context 'when signed in user is the author' do + before { get :raw, id: personal_snippet.to_param } - it 'responds with status 200' do - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) - end + it 'responds with status 200' do + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end - it 'has expected headers' do - expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') + it 'has expected headers' do + expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') - if action == :download - expect(response.header['Content-Disposition']).to match(/attachment/) - elsif action == :raw - expect(response.header['Content-Disposition']).to match(/inline/) - end - end + expect(response.header['Content-Disposition']).to match(/inline/) end end + end - context 'when not signed in' do - it 'redirects to the sign in page' do - get action, id: personal_snippet.to_param + context 'when not signed in' do + it 'redirects to the sign in page' do + get :raw, id: personal_snippet.to_param - expect(response).to redirect_to(new_user_session_path) - end + expect(response).to redirect_to(new_user_session_path) end end + end - context 'when the personal snippet is internal' do - let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } + context 'when the personal snippet is internal' do + let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } - context 'when signed in' do - before do - sign_in(user) - end + context 'when signed in' do + before do + sign_in(user) + end - it 'responds with status 200' do - get action, id: personal_snippet.to_param + it 'responds with status 200' do + get :raw, id: personal_snippet.to_param - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) - end + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) end + end - context 'when not signed in' do - it 'redirects to the sign in page' do - get action, id: personal_snippet.to_param + context 'when not signed in' do + it 'redirects to the sign in page' do + get :raw, id: personal_snippet.to_param - expect(response).to redirect_to(new_user_session_path) - end + expect(response).to redirect_to(new_user_session_path) end end + end - context 'when the personal snippet is public' do - let(:personal_snippet) { create(:personal_snippet, :public, author: user) } + context 'when the personal snippet is public' do + let(:personal_snippet) { create(:personal_snippet, :public, author: user) } - context 'when signed in' do - before do - sign_in(user) - end + context 'when signed in' do + before do + sign_in(user) + end - it 'responds with status 200' do - get action, id: personal_snippet.to_param + it 'responds with status 200' do + get :raw, id: personal_snippet.to_param - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) - end + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end - context 'CRLF line ending' do - let(:personal_snippet) do - create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line") - end + context 'CRLF line ending' do + let(:personal_snippet) do + create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line") + end - it 'returns LF line endings by default' do - get action, id: personal_snippet.to_param + it 'returns LF line endings by default' do + get :raw, id: personal_snippet.to_param - expect(response.body).to eq("first line\nsecond line\nthird line") - end + expect(response.body).to eq("first line\nsecond line\nthird line") + end - it 'does not convert line endings when parameter present' do - get action, id: personal_snippet.to_param, line_ending: :raw + it 'does not convert line endings when parameter present' do + get :raw, id: personal_snippet.to_param, line_ending: :raw - expect(response.body).to eq("first line\r\nsecond line\r\nthird line") - end + expect(response.body).to eq("first line\r\nsecond line\r\nthird line") end end + end - context 'when not signed in' do - it 'responds with status 200' do - get action, id: personal_snippet.to_param + context 'when not signed in' do + it 'responds with status 200' do + get :raw, id: personal_snippet.to_param - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) - end + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) end end + end - context 'when the personal snippet does not exist' do - context 'when signed in' do - before do - sign_in(user) - end + context 'when the personal snippet does not exist' do + context 'when signed in' do + before do + sign_in(user) + end - it 'responds with status 404' do - get action, id: 'doesntexist' + it 'responds with status 404' do + get :raw, id: 'doesntexist' - expect(response).to have_http_status(404) - end + expect(response).to have_http_status(404) end + end - context 'when not signed in' do - it 'responds with status 404' do - get action, id: 'doesntexist' + context 'when not signed in' do + it 'responds with status 404' do + get :raw, id: 'doesntexist' - expect(response).to have_http_status(404) - end + expect(response).to have_http_status(404) end end end diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 93f4903119c..44c3186d813 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -5,7 +5,7 @@ include ActionDispatch::TestProcess FactoryGirl.define do factory :note do project factory: :empty_project - note "Note" + note { generate(:title) } author on_issue diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb index 39c2a9dd1fb..0210e871a63 100644 --- a/spec/factories/project_hooks.rb +++ b/spec/factories/project_hooks.rb @@ -1,6 +1,7 @@ FactoryGirl.define do factory :project_hook do url { generate(:url) } + enable_ssl_verification false trait :token do token { SecureRandom.hex(10) } @@ -11,6 +12,7 @@ FactoryGirl.define do merge_requests_events true tag_push_events true issues_events true + confidential_issues_events true note_events true build_events true pipeline_events true diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 0db2fe04edd..3580752a805 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -32,6 +32,10 @@ FactoryGirl.define do request_access_enabled true end + trait :with_avatar do + avatar { File.open(Rails.root.join('spec/fixtures/dk.png')) } + end + trait :repository do # no-op... for now! end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index fb519a9bf12..c5f24d412d7 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Admin::Hooks", feature: true do +describe 'Admin::Hooks', feature: true do before do @project = create(:project) login_as :admin @@ -8,24 +8,24 @@ describe "Admin::Hooks", feature: true do @system_hook = create(:system_hook) end - describe "GET /admin/hooks" do - it "is ok" do + describe 'GET /admin/hooks' do + it 'is ok' do visit admin_root_path - page.within ".layout-nav" do - click_on "Hooks" + page.within '.layout-nav' do + click_on 'Hooks' end expect(current_path).to eq(admin_hooks_path) end - it "has hooks list" do + it 'has hooks list' do visit admin_hooks_path expect(page).to have_content(@system_hook.url) end end - describe "New Hook" do + describe 'New Hook' do let(:url) { generate(:url) } it 'adds new hook' do @@ -40,11 +40,36 @@ describe "Admin::Hooks", feature: true do end end - describe "Test" do + describe 'Update existing hook' do + let(:new_url) { generate(:url) } + + it 'updates existing hook' do + visit admin_hooks_path + + click_link 'Edit' + fill_in 'hook_url', with: new_url + check 'Enable SSL verification' + click_button 'Save changes' + + expect(page).to have_content 'SSL Verification: enabled' + expect(current_path).to eq(admin_hooks_path) + expect(page).to have_content(new_url) + end + end + + describe 'Remove existing hook' do + it 'remove existing hook' do + visit admin_hooks_path + + expect { click_link 'Remove' }.to change(SystemHook, :count).by(-1) + end + end + + describe 'Test' do before do WebMock.stub_request(:post, @system_hook.url) visit admin_hooks_path - click_link "Test hook" + click_link 'Test hook' end it { expect(current_path).to eq(admin_hooks_path) } diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb new file mode 100644 index 00000000000..e8ecb70306b --- /dev/null +++ b/spec/features/admin/admin_requests_profiles_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe 'Admin::RequestsProfilesController', feature: true do + before do + FileUtils.mkdir_p(Gitlab::RequestProfiler::PROFILES_DIR) + login_as(:admin) + end + + after do + Gitlab::RequestProfiler.remove_all_profiles + end + + describe 'GET /admin/requests_profiles' do + it 'shows the current profile token' do + allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + + visit admin_requests_profiles_path + + expect(page).to have_content("X-Profile-Token: #{Gitlab::RequestProfiler.profile_token}") + end + + it 'lists all available profiles' do + time1 = 1.hour.ago + time2 = 2.hours.ago + time3 = 3.hours.ago + profile1 = "|gitlab-org|gitlab-ce_#{time1.to_i}.html" + profile2 = "|gitlab-org|gitlab-ce_#{time2.to_i}.html" + profile3 = "|gitlab-com|infrastructure_#{time3.to_i}.html" + + FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile1}") + FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile2}") + FileUtils.touch("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile3}") + + visit admin_requests_profiles_path + + within('.panel', text: '/gitlab-org/gitlab-ce') do + expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile1)}']", text: time1.to_s(:long)) + expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile2)}']", text: time2.to_s(:long)) + end + + within('.panel', text: '/gitlab-com/infrastructure') do + expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile3)}']", text: time3.to_s(:long)) + end + end + end + + describe 'GET /admin/requests_profiles/:profile' do + context 'when a profile exists' do + it 'displays the content of the profile' do + content = 'This is a request profile' + profile = "|gitlab-org|gitlab-ce_#{Time.now.to_i}.html" + + File.write("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile}", content) + + visit admin_requests_profile_path(profile) + + expect(page).to have_content(content) + end + end + + context 'when a profile does not exist' do + it 'shows an error message' do + visit admin_requests_profile_path('|non|existent_12345.html') + + expect(page).to have_content('Profile not found') + end + end + end +end diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index 55e10a1a89b..7a2987e815d 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -53,7 +53,7 @@ describe "User Feed", feature: true do end it 'has XHTML summaries in issue descriptions' do - expect(body).to match /we have a bug!<\/p>\n\n<hr ?\/>\n\n<p dir="auto">I guess/ + expect(body).to match /<hr ?\/>/ end it 'has XHTML summaries in notes' do diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb index 8e5421a984b..9828cb179a7 100644 --- a/spec/features/explore/groups_list_spec.rb +++ b/spec/features/explore/groups_list_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Explore Groups page', js: true, feature: true do +describe 'Explore Groups page', :js, :feature do let!(:user) { create :user } let!(:group) { create(:group) } let!(:public_group) { create(:group, :public) } @@ -46,19 +46,39 @@ describe 'Explore Groups page', js: true, feature: true do it 'shows non-archived projects count' do # Initially project is not archived expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1") - + # Archive project empty_project.archive! visit explore_groups_path # Check project count expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("0") - + # Unarchive project empty_project.unarchive! visit explore_groups_path # Check project count - expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1") + expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1") + end + + describe 'landing component' do + it 'should show a landing component' do + expect(page).to have_content('Below you will find all the groups that are public.') + end + + it 'should be dismissable' do + find('.dismiss-button').click + + expect(page).not_to have_content('Below you will find all the groups that are public.') + end + + it 'should persistently not show once dismissed' do + find('.dismiss-button').click + + visit explore_groups_path + + expect(page).not_to have_content('Below you will find all the groups that are public.') + end end end diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 755992069ff..21b8cf3add5 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe 'New/edit issue', feature: true, js: true do include GitlabRoutingHelper + include ActionView::Helpers::JavaScriptHelper let!(:project) { create(:project) } let!(:user) { create(:user)} @@ -105,6 +106,33 @@ describe 'New/edit issue', feature: true, js: true do expect(find('.js-label-select')).to have_content('Labels') end + + it 'correctly updates the selected user when changing assignee' do + click_button 'Assignee' + page.within '.dropdown-menu-user' do + click_link user.name + end + + expect(find('input[name="issue[assignee_id]"]', visible: false).value).to match(user.id.to_s) + + click_button user.name + + expect(find('.dropdown-menu-user a.is-active').first(:xpath, '..')['data-user-id']).to eq(user.id.to_s) + + # check the ::before pseudo element to ensure checkmark icon is present + expect(before_for_selector('.dropdown-menu-selectable a.is-active')).not_to eq('') + expect(before_for_selector('.dropdown-menu-selectable a:not(.is-active)')).to eq('') + + page.within '.dropdown-menu-user' do + click_link user2.name + end + + expect(find('input[name="issue[assignee_id]"]', visible: false).value).to match(user2.id.to_s) + + click_button user2.name + + expect(find('.dropdown-menu-user a.is-active').first(:xpath, '..')['data-user-id']).to eq(user2.id.to_s) + end end context 'edit issue' do @@ -154,4 +182,14 @@ describe 'New/edit issue', feature: true, js: true do end end end + + def before_for_selector(selector) + js = <<-JS.strip_heredoc + (function(selector) { + var el = document.querySelector(selector); + return window.getComputedStyle(el, '::before').getPropertyValue('content'); + })("#{escape_javascript(selector)}") + JS + page.evaluate_script(js) + end end diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index baacd7edb86..82b80a69bed 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -3,7 +3,8 @@ require 'rails_helper' feature 'Issue Sidebar', feature: true do include MobileHelpers - let(:project) { create(:project, :public) } + let(:group) { create(:group, :nested) } + let(:project) { create(:project, :public, namespace: group) } let(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} let!(:label) { create(:label, project: project, title: 'bug') } @@ -55,10 +56,12 @@ feature 'Issue Sidebar', feature: true do # Resize the window resize_screen_sm # Make sure the sidebar is collapsed + find(sidebar_selector) expect(page).to have_css(sidebar_selector) # Once is collapsed let's open the sidebard and reload open_issue_sidebar refresh + find(sidebar_selector) expect(page).to have_css(sidebar_selector) # Restore the window size as it was including the sidebar restore_window_size @@ -149,9 +152,7 @@ feature 'Issue Sidebar', feature: true do end def open_issue_sidebar - page.within('aside.right-sidebar.right-sidebar-collapsed') do - find('.js-sidebar-toggle').click - sleep 1 - end + find('aside.right-sidebar.right-sidebar-collapsed .js-sidebar-toggle').click + find('aside.right-sidebar.right-sidebar-expanded') end end diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index 16b09933bda..f1b3e7f158c 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -34,7 +34,7 @@ feature 'Create New Merge Request', feature: true, js: true do expect(page).to have_content('Target branch') first('.js-target-branch').click - first('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0').click + find('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0', match: :first).click expect(page).to have_content "b83d6e3" end diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb index 50d7ca39045..9eec3d7f270 100644 --- a/spec/features/milestones/milestones_spec.rb +++ b/spec/features/milestones/milestones_spec.rb @@ -86,6 +86,9 @@ describe 'Milestone draggable', feature: true, js: true do visit namespace_project_milestone_path(project.namespace, project, milestone) page.find("a[href='#tab-merge-requests']").click + + wait_for_ajax + scroll_into_view('.milestone-content') drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1) diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index cc11cb7a55f..8dba2ccbafa 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -1,13 +1,10 @@ require 'spec_helper' feature 'File blob', :js, feature: true do - include TreeHelper - include WaitForAjax - let(:project) { create(:project, :public) } def visit_blob(path, fragment = nil) - visit namespace_project_blob_path(project.namespace, project, tree_join('master', path), anchor: fragment) + visit namespace_project_blob_path(project.namespace, project, File.join('master', path), anchor: fragment) end context 'Ruby file' do @@ -27,6 +24,9 @@ feature 'File blob', :js, feature: true do # shows an enabled copy button expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + + # shows a raw button + expect(page).to have_link('Open raw') end end end @@ -39,7 +39,7 @@ feature 'File blob', :js, feature: true do wait_for_ajax end - it 'displays the blob' do + it 'displays the blob using the rich viewer' do aggregate_failures do # hides the simple viewer expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) @@ -53,6 +53,9 @@ feature 'File blob', :js, feature: true do # shows a disabled copy button expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + + # shows a raw button + expect(page).to have_link('Open raw') end end @@ -63,7 +66,7 @@ feature 'File blob', :js, feature: true do wait_for_ajax end - it 'displays the blob' do + it 'displays the blob using the simple viewer' do aggregate_failures do # hides the rich viewer expect(page).to have_selector('.blob-viewer[data-type="simple"]') @@ -84,7 +87,7 @@ feature 'File blob', :js, feature: true do wait_for_ajax end - it 'displays the blob' do + it 'displays the blob using the rich viewer' do aggregate_failures do # hides the simple viewer expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) @@ -105,7 +108,7 @@ feature 'File blob', :js, feature: true do wait_for_ajax end - it 'displays the blob' do + it 'displays the blob using the simple viewer' do aggregate_failures do # hides the rich viewer expect(page).to have_selector('.blob-viewer[data-type="simple"]') @@ -163,6 +166,9 @@ feature 'File blob', :js, feature: true do # does not show a copy button expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # shows a raw button + expect(page).to have_link('Open raw') end end @@ -206,6 +212,9 @@ feature 'File blob', :js, feature: true do # shows an enabled copy button expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + + # shows a raw button + expect(page).to have_link('Open raw') end end end @@ -222,7 +231,7 @@ feature 'File blob', :js, feature: true do branch_name: 'master', commit_message: "Add PDF", file_path: 'files/test.pdf', - file_content: File.read(Rails.root.join('spec/javascripts/blob/pdf/test.pdf')) + file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data ).execute visit_blob('files/test.pdf') @@ -240,6 +249,9 @@ feature 'File blob', :js, feature: true do # does not show a copy button expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # shows a download button + expect(page).to have_link('Download') end end end @@ -265,6 +277,9 @@ feature 'File blob', :js, feature: true do # does not show a copy button expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # shows a download button + expect(page).to have_link('Download') end end end @@ -286,6 +301,9 @@ feature 'File blob', :js, feature: true do # shows an enabled copy button expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + + # shows a raw button + expect(page).to have_link('Open raw') end end end @@ -308,6 +326,9 @@ feature 'File blob', :js, feature: true do # does not show a copy button expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # shows a download button + expect(page).to have_link('Download') end end end diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb index 5d64d42fd61..fa67d390c47 100644 --- a/spec/features/projects/commit/cherry_pick_spec.rb +++ b/spec/features/projects/commit/cherry_pick_spec.rb @@ -74,8 +74,10 @@ describe 'Cherry-pick Commits' do wait_for_ajax - page.within('#modal-cherry-pick-commit .dropdown-menu .dropdown-content') do - click_link 'feature' + page.within('#modal-cherry-pick-commit .dropdown-menu') do + find('.dropdown-input input').set('feature') + wait_for_ajax + click_link "feature" end page.within('#modal-cherry-pick-commit') do diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index acc3efe04e6..1e12f8542e2 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -200,7 +200,7 @@ feature 'Environment', :feature do end scenario 'user deletes the branch with running environment' do - visit namespace_project_branches_path(project.namespace, project) + visit namespace_project_branches_path(project.namespace, project, search: 'feature') remove_branch_with_hooks(project, user, 'feature') do page.within('.js-branch-feature') { find('a.btn-remove').click } diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb index 05f3162f13c..1370ab1c521 100644 --- a/spec/features/projects/merge_request_button_spec.rb +++ b/spec/features/projects/merge_request_button_spec.rb @@ -85,8 +85,8 @@ feature 'Merge Request button', feature: true do context 'on branches page' do it_behaves_like 'Merge request button only shown when allowed' do let(:label) { 'Merge request' } - let(:url) { namespace_project_branches_path(project.namespace, project) } - let(:fork_url) { namespace_project_branches_path(forked_project.namespace, forked_project) } + let(:url) { namespace_project_branches_path(project.namespace, project, search: 'feature') } + let(:fork_url) { namespace_project_branches_path(forked_project.namespace, forked_project, search: 'feature') } end end diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb index 5e19907eef9..b4fc0edbde8 100644 --- a/spec/features/projects/milestones/milestone_spec.rb +++ b/spec/features/projects/milestones/milestone_spec.rb @@ -78,11 +78,10 @@ feature 'Project milestone', :feature do it 'shows the total MR and issue counts' do find('.milestone-sidebar .block', match: :first) - blocks = all('.milestone-sidebar .block') aggregate_failures 'MR and issue blocks' do - expect(blocks[3]).to have_content 1 - expect(blocks[4]).to have_content 0 + expect(find('.milestone-sidebar .block.issues')).to have_content 1 + expect(find('.milestone-sidebar .block.merge-requests')).to have_content 0 end end end diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb new file mode 100644 index 00000000000..7909234556e --- /dev/null +++ b/spec/features/projects/settings/integration_settings_spec.rb @@ -0,0 +1,94 @@ +require 'spec_helper' + +feature 'Integration settings', feature: true do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:role) { :developer } + let(:integrations_path) { namespace_project_settings_integrations_path(project.namespace, project) } + + background do + login_as(user) + project.team << [user, role] + end + + context 'for developer' do + given(:role) { :developer } + + scenario 'to be disallowed to view' do + visit integrations_path + + expect(page.status_code).to eq(404) + end + end + + context 'for master' do + given(:role) { :master } + + context 'Webhooks' do + let(:hook) { create(:project_hook, :all_events_enabled, enable_ssl_verification: true, project: project) } + let(:url) { generate(:url) } + + scenario 'show list of webhooks' do + hook + + visit integrations_path + + expect(page.status_code).to eq(200) + expect(page).to have_content(hook.url) + expect(page).to have_content('SSL Verification: enabled') + expect(page).to have_content('Push Events') + expect(page).to have_content('Tag Push Events') + expect(page).to have_content('Issues Events') + expect(page).to have_content('Confidential Issues Events') + expect(page).to have_content('Note Events') + expect(page).to have_content('Merge Requests Events') + expect(page).to have_content('Pipeline Events') + expect(page).to have_content('Wiki Page Events') + end + + scenario 'create webhook' do + visit integrations_path + + fill_in 'hook_url', with: url + check 'Tag push events' + check 'Enable SSL verification' + + click_button 'Add webhook' + + expect(page).to have_content(url) + expect(page).to have_content('SSL Verification: enabled') + expect(page).to have_content('Push Events') + expect(page).to have_content('Tag Push Events') + end + + scenario 'edit existing webhook' do + hook + visit integrations_path + + click_link 'Edit' + fill_in 'hook_url', with: url + check 'Enable SSL verification' + click_button 'Save changes' + + expect(page).to have_content 'SSL Verification: enabled' + expect(page).to have_content(url) + end + + scenario 'test existing webhook' do + WebMock.stub_request(:post, hook.url) + visit integrations_path + + click_link 'Test' + + expect(current_path).to eq(integrations_path) + end + + scenario 'remove existing webhook' do + hook + visit integrations_path + + expect { click_link 'Remove' }.to change(ProjectHook, :count).by(-1) + end + end + end +end diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb new file mode 100644 index 00000000000..cedf3778c7e --- /dev/null +++ b/spec/features/projects/snippets/show_spec.rb @@ -0,0 +1,144 @@ +require 'spec_helper' + +feature 'Project snippet', :js, feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) } + + before do + project.team << [user, :master] + login_as(user) + end + + context 'Ruby file' do + let(:file_name) { 'popen.rb' } + let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data } + + before do + visit namespace_project_snippet_path(project.namespace, project, snippet) + + wait_for_ajax + end + + it 'displays the blob' do + aggregate_failures do + # shows highlighted Ruby code + expect(page).to have_content("require 'fileutils'") + + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + + # shows a raw button + expect(page).to have_link('Open raw') + + # shows a download button + expect(page).to have_link('Download') + end + end + end + + context 'Markdown file' do + let(:file_name) { 'ruby-style-guide.md' } + let(:content) { project.repository.blob_at('master', 'files/markdown/ruby-style-guide.md').data } + + context 'visiting directly' do + before do + visit namespace_project_snippet_path(project.namespace, project, snippet) + + wait_for_ajax + end + + it 'displays the blob using the rich viewer' do + aggregate_failures do + # hides the simple viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) + expect(page).to have_selector('.blob-viewer[data-type="rich"]') + + # shows rendered Markdown + expect(page).to have_link("PEP-8") + + # shows a viewer switcher + expect(page).to have_selector('.js-blob-viewer-switcher') + + # shows a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + + # shows a raw button + expect(page).to have_link('Open raw') + + # shows a download button + expect(page).to have_link('Download') + end + end + + context 'switching to the simple viewer' do + before do + find('.js-blob-viewer-switch-btn[data-viewer=simple]').click + + wait_for_ajax + end + + it 'displays the blob using the simple viewer' do + aggregate_failures do + # hides the rich viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) + + # shows highlighted Markdown code + expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + end + end + + context 'switching to the rich viewer again' do + before do + find('.js-blob-viewer-switch-btn[data-viewer=rich]').click + + wait_for_ajax + end + + it 'displays the blob using the rich viewer' do + aggregate_failures do + # hides the simple viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) + expect(page).to have_selector('.blob-viewer[data-type="rich"]') + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + end + end + end + end + end + + context 'visiting with a line number anchor' do + before do + visit namespace_project_snippet_path(project.namespace, project, snippet, anchor: 'L1') + + wait_for_ajax + end + + it 'displays the blob using the simple viewer' do + aggregate_failures do + # hides the rich viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) + + # highlights the line in question + expect(page).to have_selector('#LC1.hll') + + # shows highlighted Markdown code + expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + end + end + end + end +end diff --git a/spec/features/protected_branches/access_control_ce_spec.rb b/spec/features/protected_branches/access_control_ce_spec.rb index eb3cea775da..d30e7947106 100644 --- a/spec/features/protected_branches/access_control_ce_spec.rb +++ b/spec/features/protected_branches/access_control_ce_spec.rb @@ -9,7 +9,7 @@ RSpec.shared_examples "protected branches > access control > CE" do allowed_to_push_button = find(".js-allowed-to-push") unless allowed_to_push_button.text == access_type_name - allowed_to_push_button.click + allowed_to_push_button.trigger('click') within(".dropdown.open .dropdown-menu") { click_on access_type_name } end end diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index acc5641f930..fc9b293c393 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -8,7 +8,7 @@ feature 'Projected Branches', feature: true, js: true do before { login_as(user) } def set_protected_branch_name(branch_name) - find(".js-protected-branch-select").click + find(".js-protected-branch-select").trigger('click') find(".dropdown-input-field").set(branch_name) click_on("Create wildcard #{branch_name}") end diff --git a/spec/features/protected_tags/access_control_ce_spec.rb b/spec/features/protected_tags/access_control_ce_spec.rb index 5b2baf8616c..5b24ac0292b 100644 --- a/spec/features/protected_tags/access_control_ce_spec.rb +++ b/spec/features/protected_tags/access_control_ce_spec.rb @@ -10,6 +10,7 @@ RSpec.shared_examples "protected tags > access control > CE" do unless allowed_to_create_button.text == access_type_name allowed_to_create_button.click + find('.dropdown.open .dropdown-menu li', match: :first) within(".dropdown.open .dropdown-menu") { click_on access_type_name } end end diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index a1a36931824..26879a77c48 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -466,6 +466,21 @@ describe "Internal Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } end + describe "GET /:project_path/environments/:id/deployments" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_deployments_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for(:admin) } + it { is_expected.to be_allowed_for(:owner).of(project) } + it { is_expected.to be_allowed_for(:master).of(project) } + it { is_expected.to be_allowed_for(:developer).of(project) } + it { is_expected.to be_allowed_for(:reporter).of(project) } + it { is_expected.to be_denied_for(:guest).of(project) } + it { is_expected.to be_denied_for(:user) } + it { is_expected.to be_denied_for(:external) } + it { is_expected.to be_denied_for(:visitor) } + end + describe "GET /:project_path/environments/new" do subject { new_namespace_project_environment_path(project.namespace, project) } diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 5d58494a22a..699ca4f724c 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -449,6 +449,21 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } end + describe "GET /:project_path/environments/:id/deployments" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_deployments_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for(:admin) } + it { is_expected.to be_allowed_for(:owner).of(project) } + it { is_expected.to be_allowed_for(:master).of(project) } + it { is_expected.to be_allowed_for(:developer).of(project) } + it { is_expected.to be_allowed_for(:reporter).of(project) } + it { is_expected.to be_denied_for(:guest).of(project) } + it { is_expected.to be_denied_for(:user) } + it { is_expected.to be_denied_for(:external) } + it { is_expected.to be_denied_for(:visitor) } + end + describe "GET /:project_path/environments/new" do subject { new_namespace_project_environment_path(project.namespace, project) } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 5df5b710dc4..624f0d0f485 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -286,6 +286,21 @@ describe "Public Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } end + describe "GET /:project_path/environments/:id/deployments" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_deployments_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for(:admin) } + it { is_expected.to be_allowed_for(:owner).of(project) } + it { is_expected.to be_allowed_for(:master).of(project) } + it { is_expected.to be_allowed_for(:developer).of(project) } + it { is_expected.to be_allowed_for(:reporter).of(project) } + it { is_expected.to be_denied_for(:guest).of(project) } + it { is_expected.to be_denied_for(:user) } + it { is_expected.to be_denied_for(:external) } + it { is_expected.to be_denied_for(:visitor) } + end + describe "GET /:project_path/environments/new" do subject { new_namespace_project_environment_path(project.namespace, project) } diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/create_snippet_spec.rb index 5470276bf06..9409c323288 100644 --- a/spec/features/snippets/create_snippet_spec.rb +++ b/spec/features/snippets/create_snippet_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Create Snippet', feature: true do +feature 'Create Snippet', :js, feature: true do before do login_as :user visit new_snippet_path @@ -9,10 +9,11 @@ feature 'Create Snippet', feature: true do scenario 'Authenticated user creates a snippet' do fill_in 'personal_snippet_title', with: 'My Snippet Title' page.within('.file-editor') do - find(:xpath, "//input[@id='personal_snippet_content']").set 'Hello World!' + find('.ace_editor').native.send_keys 'Hello World!' end click_button 'Create snippet' + wait_for_ajax expect(page).to have_content('My Snippet Title') expect(page).to have_content('Hello World!') @@ -22,10 +23,11 @@ feature 'Create Snippet', feature: true do fill_in 'personal_snippet_title', with: 'My Snippet Title' page.within('.file-editor') do find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name' - find(:xpath, "//input[@id='personal_snippet_content']").set 'Hello World!' + find('.ace_editor').native.send_keys 'Hello World!' end click_button 'Create snippet' + wait_for_ajax expect(page).to have_content('My Snippet Title') expect(page).to have_content('snippet+file+name') diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb new file mode 100644 index 00000000000..c646039e0b1 --- /dev/null +++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'Comments on personal snippets', feature: true do + let!(:user) { create(:user) } + let!(:snippet) { create(:personal_snippet, :public) } + let!(:snippet_notes) do + [ + create(:note_on_personal_snippet, noteable: snippet, author: user), + create(:note_on_personal_snippet, noteable: snippet) + ] + end + let!(:other_note) { create(:note_on_personal_snippet) } + + before do + login_as user + visit snippet_path(snippet) + end + + subject { page } + + context 'viewing the snippet detail page' do + it 'contains notes for a snippet with correct action icons' do + expect(page).to have_selector('#notes-list li', count: 2) + + # comment authored by current user + page.within("#notes-list li#note_#{snippet_notes[0].id}") do + expect(page).to have_content(snippet_notes[0].note) + expect(page).to have_selector('.js-note-delete') + expect(page).to have_selector('.note-emoji-button') + end + + page.within("#notes-list li#note_#{snippet_notes[1].id}") do + expect(page).to have_content(snippet_notes[1].note) + expect(page).not_to have_selector('.js-note-delete') + expect(page).to have_selector('.note-emoji-button') + end + end + end +end diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb index 34300ccb940..2df483818c3 100644 --- a/spec/features/snippets/public_snippets_spec.rb +++ b/spec/features/snippets/public_snippets_spec.rb @@ -1,10 +1,11 @@ require 'rails_helper' -feature 'Public Snippets', feature: true do +feature 'Public Snippets', :js, feature: true do scenario 'Unauthenticated user should see public snippets' do public_snippet = create(:personal_snippet, :public) visit snippet_path(public_snippet) + wait_for_ajax expect(page).to have_content(public_snippet.content) end diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb new file mode 100644 index 00000000000..e36cf547f80 --- /dev/null +++ b/spec/features/snippets/show_spec.rb @@ -0,0 +1,138 @@ +require 'spec_helper' + +feature 'Snippet', :js, feature: true do + let(:project) { create(:project, :repository) } + let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) } + + context 'Ruby file' do + let(:file_name) { 'popen.rb' } + let(:content) { project.repository.blob_at('master', 'files/ruby/popen.rb').data } + + before do + visit snippet_path(snippet) + + wait_for_ajax + end + + it 'displays the blob' do + aggregate_failures do + # shows highlighted Ruby code + expect(page).to have_content("require 'fileutils'") + + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + + # shows a raw button + expect(page).to have_link('Open raw') + + # shows a download button + expect(page).to have_link('Download') + end + end + end + + context 'Markdown file' do + let(:file_name) { 'ruby-style-guide.md' } + let(:content) { project.repository.blob_at('master', 'files/markdown/ruby-style-guide.md').data } + + context 'visiting directly' do + before do + visit snippet_path(snippet) + + wait_for_ajax + end + + it 'displays the blob using the rich viewer' do + aggregate_failures do + # hides the simple viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) + expect(page).to have_selector('.blob-viewer[data-type="rich"]') + + # shows rendered Markdown + expect(page).to have_link("PEP-8") + + # shows a viewer switcher + expect(page).to have_selector('.js-blob-viewer-switcher') + + # shows a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + + # shows a raw button + expect(page).to have_link('Open raw') + + # shows a download button + expect(page).to have_link('Download') + end + end + + context 'switching to the simple viewer' do + before do + find('.js-blob-viewer-switch-btn[data-viewer=simple]').click + + wait_for_ajax + end + + it 'displays the blob using the simple viewer' do + aggregate_failures do + # hides the rich viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) + + # shows highlighted Markdown code + expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + end + end + + context 'switching to the rich viewer again' do + before do + find('.js-blob-viewer-switch-btn[data-viewer=rich]').click + + wait_for_ajax + end + + it 'displays the blob using the rich viewer' do + aggregate_failures do + # hides the simple viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) + expect(page).to have_selector('.blob-viewer[data-type="rich"]') + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + end + end + end + end + end + + context 'visiting with a line number anchor' do + before do + visit snippet_path(snippet, anchor: 'L1') + + wait_for_ajax + end + + it 'displays the blob using the simple viewer' do + aggregate_failures do + # hides the rich viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) + + # highlights the line in question + expect(page).to have_selector('#LC1.hll') + + # shows highlighted Markdown code + expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + end + end + end + end +end diff --git a/spec/features/todos/todos_filtering_spec.rb b/spec/features/todos/todos_filtering_spec.rb index cecb98641a6..f32e70c2c3f 100644 --- a/spec/features/todos/todos_filtering_spec.rb +++ b/spec/features/todos/todos_filtering_spec.rb @@ -45,8 +45,8 @@ describe 'Dashboard > User filters todos', feature: true, js: true do wait_for_ajax - expect(find('.todos-list')).to have_content user_1.name - expect(find('.todos-list')).not_to have_content user_2.name + expect(find('.todos-list')).to have_content 'merge request' + expect(find('.todos-list')).not_to have_content 'issue' end it "shows only authors of existing todos" do diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb index 50c207fb9cb..be5b3af417f 100644 --- a/spec/features/todos/todos_spec.rb +++ b/spec/features/todos/todos_spec.rb @@ -99,6 +99,83 @@ describe 'Dashboard Todos', feature: true do end end + context 'User created todos for themself' do + before do + login_as(user) + end + + context 'issue assigned todo' do + before do + create(:todo, :assigned, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows issue assigned to yourself message' do + page.within('.js-todos-all') do + expect(page).to have_content("You assigned issue #{issue.to_reference(full: true)} to yourself") + end + end + end + + context 'marked todo' do + before do + create(:todo, :marked, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows you added a todo message' do + page.within('.js-todos-all') do + expect(page).to have_content("You added a todo for issue #{issue.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + + context 'mentioned todo' do + before do + create(:todo, :mentioned, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows you mentioned yourself message' do + page.within('.js-todos-all') do + expect(page).to have_content("You mentioned yourself on issue #{issue.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + + context 'directly_addressed todo' do + before do + create(:todo, :directly_addressed, user: user, project: project, target: issue, author: user) + visit dashboard_todos_path + end + + it 'shows you directly addressed yourself message' do + page.within('.js-todos-all') do + expect(page).to have_content("You directly addressed yourself on issue #{issue.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + + context 'approval todo' do + let(:merge_request) { create(:merge_request) } + + before do + create(:todo, :approval_required, user: user, project: project, target: merge_request, author: user) + visit dashboard_todos_path + end + + it 'shows you set yourself as an approver message' do + page.within('.js-todos-all') do + expect(page).to have_content("You set yourself as an approver for merge request #{merge_request.to_reference(full: true)}") + expect(page).not_to have_content('to yourself') + end + end + end + end + context 'User has done todos', js: true do before do create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author) diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 81fa2de1cc3..783f330221c 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -104,6 +104,24 @@ feature 'Triggers', feature: true, js: true do expect(page).to have_content 'The form contains the following errors' end + + context 'when GitLab time_zone is ActiveSupport::TimeZone format' do + before do + allow(Time).to receive(:zone) + .and_return(ActiveSupport::TimeZone['Eastern Time (US & Canada)']) + end + + scenario 'do fill form with valid data and save' do + find('#trigger_trigger_schedule_attributes_active').click + fill_in 'trigger_trigger_schedule_attributes_cron', with: '1 * * * *' + fill_in 'trigger_trigger_schedule_attributes_cron_timezone', with: 'UTC' + fill_in 'trigger_trigger_schedule_attributes_ref', with: 'master' + click_button 'Save trigger' + + expect(page.find('.flash-notice')) + .to have_content 'Trigger was successfully updated.' + end + end end context 'disabling schedule' do diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index a1ae1d746af..a5f717e6233 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -16,7 +16,7 @@ describe IssuesFinder do set(:label_link) { create(:label_link, label: label, target: issue2) } let(:search_user) { user } let(:params) { {} } - let(:issues) { IssuesFinder.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute } + let(:issues) { described_class.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute } before(:context) do project1.team << [user, :master] @@ -282,15 +282,15 @@ describe IssuesFinder do let!(:confidential_issue) { create(:issue, project: project, confidential: true) } it 'returns non confidential issues for nil user' do - expect(IssuesFinder.send(:not_restricted_by_confidentiality, nil)).to include(public_issue) + expect(described_class.send(:not_restricted_by_confidentiality, nil)).to include(public_issue) end it 'returns non confidential issues for user not authorized for the issues projects' do - expect(IssuesFinder.send(:not_restricted_by_confidentiality, user)).to include(public_issue) + expect(described_class.send(:not_restricted_by_confidentiality, user)).to include(public_issue) end it 'returns all issues for user authorized for the issues projects' do - expect(IssuesFinder.send(:not_restricted_by_confidentiality, authorized_user)).to include(public_issue, confidential_issue) + expect(described_class.send(:not_restricted_by_confidentiality, authorized_user)).to include(public_issue, confidential_issue) end end end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 21ef94ac5d1..58b7cd5e098 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -23,26 +23,26 @@ describe MergeRequestsFinder do describe "#execute" do it 'filters by scope' do params = { scope: 'authored', state: 'opened' } - merge_requests = MergeRequestsFinder.new(user, params).execute + merge_requests = described_class.new(user, params).execute expect(merge_requests.size).to eq(3) end it 'filters by project' do params = { project_id: project1.id, scope: 'authored', state: 'opened' } - merge_requests = MergeRequestsFinder.new(user, params).execute + merge_requests = described_class.new(user, params).execute expect(merge_requests.size).to eq(1) end it 'filters by non_archived' do params = { non_archived: true } - merge_requests = MergeRequestsFinder.new(user, params).execute + merge_requests = described_class.new(user, params).execute expect(merge_requests.size).to eq(3) end it 'filters by iid' do params = { project_id: project1.id, iids: merge_request1.iid } - merge_requests = MergeRequestsFinder.new(user, params).execute + merge_requests = described_class.new(user, params).execute expect(merge_requests).to contain_exactly(merge_request1) end diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb index 765bf44d863..ba6bbb3bce0 100644 --- a/spec/finders/notes_finder_spec.rb +++ b/spec/finders/notes_finder_spec.rb @@ -110,6 +110,15 @@ describe NotesFinder do expect(notes.count).to eq(1) end + it 'finds notes on personal snippets' do + note = create(:note_on_personal_snippet) + params = { target_type: 'personal_snippet', target_id: note.noteable_id } + + notes = described_class.new(project, user, params).execute + + expect(notes.count).to eq(1) + end + it 'raises an exception for an invalid target_type' do params[:target_type] = 'invalid' expect { described_class.new(project, user, params).execute }.to raise_error('invalid target_type') diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb index 975e99c5807..cb6c80d1bd0 100644 --- a/spec/finders/snippets_finder_spec.rb +++ b/spec/finders/snippets_finder_spec.rb @@ -14,13 +14,13 @@ describe SnippetsFinder do let!(:snippet3) { create(:personal_snippet, :public) } it "returns all private and internal snippets" do - snippets = SnippetsFinder.new.execute(user, filter: :all) + snippets = described_class.new.execute(user, filter: :all) expect(snippets).to include(snippet2, snippet3) expect(snippets).not_to include(snippet1) end it "returns all public snippets" do - snippets = SnippetsFinder.new.execute(nil, filter: :all) + snippets = described_class.new.execute(nil, filter: :all) expect(snippets).to include(snippet3) expect(snippets).not_to include(snippet1, snippet2) end @@ -32,7 +32,7 @@ describe SnippetsFinder do let!(:snippet3) { create(:personal_snippet, :public) } it "returns public public snippets" do - snippets = SnippetsFinder.new.execute(nil, filter: :public) + snippets = described_class.new.execute(nil, filter: :public) expect(snippets).to include(snippet3) expect(snippets).not_to include(snippet1, snippet2) @@ -45,36 +45,36 @@ describe SnippetsFinder do let!(:snippet3) { create(:personal_snippet, :public, author: user) } it "returns all public and internal snippets" do - snippets = SnippetsFinder.new.execute(user1, filter: :by_user, user: user) + snippets = described_class.new.execute(user1, filter: :by_user, user: user) expect(snippets).to include(snippet2, snippet3) expect(snippets).not_to include(snippet1) end it "returns internal snippets" do - snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_internal") + snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_internal") expect(snippets).to include(snippet2) expect(snippets).not_to include(snippet1, snippet3) end it "returns private snippets" do - snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_private") + snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_private") expect(snippets).to include(snippet1) expect(snippets).not_to include(snippet2, snippet3) end it "returns public snippets" do - snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_public") + snippets = described_class.new.execute(user, filter: :by_user, user: user, scope: "are_public") expect(snippets).to include(snippet3) expect(snippets).not_to include(snippet1, snippet2) end it "returns all snippets" do - snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user) + snippets = described_class.new.execute(user, filter: :by_user, user: user) expect(snippets).to include(snippet1, snippet2, snippet3) end it "returns only public snippets if unauthenticated user" do - snippets = SnippetsFinder.new.execute(nil, filter: :by_user, user: user) + snippets = described_class.new.execute(nil, filter: :by_user, user: user) expect(snippets).to include(snippet3) expect(snippets).not_to include(snippet2, snippet1) end @@ -88,43 +88,43 @@ describe SnippetsFinder do end it "returns public snippets for unauthorized user" do - snippets = SnippetsFinder.new.execute(nil, filter: :by_project, project: project1) + snippets = described_class.new.execute(nil, filter: :by_project, project: project1) expect(snippets).to include(@snippet3) expect(snippets).not_to include(@snippet1, @snippet2) end it "returns public and internal snippets for non project members" do - snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1) + snippets = described_class.new.execute(user, filter: :by_project, project: project1) expect(snippets).to include(@snippet2, @snippet3) expect(snippets).not_to include(@snippet1) end it "returns public snippets for non project members" do - snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_public") + snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_public") expect(snippets).to include(@snippet3) expect(snippets).not_to include(@snippet1, @snippet2) end it "returns internal snippets for non project members" do - snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_internal") + snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_internal") expect(snippets).to include(@snippet2) expect(snippets).not_to include(@snippet1, @snippet3) end it "does not return private snippets for non project members" do - snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_private") + snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_private") expect(snippets).not_to include(@snippet1, @snippet2, @snippet3) end it "returns all snippets for project members" do project1.team << [user, :developer] - snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1) + snippets = described_class.new.execute(user, filter: :by_project, project: project1) expect(snippets).to include(@snippet1, @snippet2, @snippet3) end it "returns private snippets for project members" do project1.team << [user, :developer] - snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1, scope: "are_private") + snippets = described_class.new.execute(user, filter: :by_project, project: project1, scope: "are_private") expect(snippets).to include(@snippet1) end end diff --git a/spec/fixtures/api/schemas/deployments.json b/spec/fixtures/api/schemas/deployments.json new file mode 100644 index 00000000000..1112f23aab2 --- /dev/null +++ b/spec/fixtures/api/schemas/deployments.json @@ -0,0 +1,58 @@ +{ + "additionalProperties": false, + "properties": { + "deployments": { + "items": { + "additionalProperties": false, + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "iid": { + "type": "integer" + }, + "last?": { + "type": "boolean" + }, + "ref": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "sha": { + "type": "string" + }, + "tag": { + "type": "boolean" + } + }, + "required": [ + "sha", + "created_at", + "iid", + "tag", + "last?", + "ref", + "id" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + } + }, + "required": [ + "deployments" + ], + "type": "object" +} diff --git a/spec/helpers/award_emoji_helper_spec.rb b/spec/helpers/award_emoji_helper_spec.rb new file mode 100644 index 00000000000..7dfd6a3f6b4 --- /dev/null +++ b/spec/helpers/award_emoji_helper_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +describe AwardEmojiHelper do + describe '.toggle_award_url' do + context 'note on personal snippet' do + let(:note) { create(:note_on_personal_snippet) } + + it 'returns correct url' do + expected_url = "/snippets/#{note.noteable.id}/notes/#{note.id}/toggle_award_emoji" + + expect(helper.toggle_award_url(note)).to eq(expected_url) + end + end + + context 'note on project item' do + let(:note) { create(:note_on_project_snippet) } + + it 'returns correct url' do + @project = note.noteable.project + + expected_url = "/#{@project.namespace.path}/#{@project.path}/notes/#{note.id}/toggle_award_emoji" + + expect(helper.toggle_award_url(note)).to eq(expected_url) + end + end + + context 'personal snippet' do + let(:snippet) { create(:personal_snippet) } + + it 'returns correct url' do + expected_url = "/snippets/#{snippet.id}/toggle_award_emoji" + + expect(helper.toggle_award_url(snippet)).to eq(expected_url) + end + end + + context 'merge request' do + let(:merge_request) { create(:merge_request) } + + it 'returns correct url' do + @project = merge_request.project + + expected_url = "/#{@project.namespace.path}/#{@project.path}/merge_requests/#{merge_request.id}/toggle_award_emoji" + + expect(helper.toggle_award_url(merge_request)).to eq(expected_url) + end + end + + context 'issue' do + let(:issue) { create(:issue) } + + it 'returns correct url' do + @project = issue.project + + expected_url = "/#{@project.namespace.path}/#{@project.path}/issues/#{issue.id}/toggle_award_emoji" + + expect(helper.toggle_award_url(issue)).to eq(expected_url) + end + end + end +end diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 379f62f73e1..075f1887d91 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -157,6 +157,7 @@ describe BlobHelper do describe '#blob_render_error_options' do before do assign(:project, project) + assign(:blob, blob) assign(:id, File.join('master', blob.path)) controller.params[:controller] = 'projects/blob' diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index a7c3c281083..c3bd0cb3542 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -56,7 +56,7 @@ describe EventsHelper do it 'preserves code color scheme' do input = "```ruby\ndef test\n 'hello world'\nend\n```" - expected = '<pre class="code highlight js-syntax-highlight ruby">' \ + expected = "\n<pre class=\"code highlight js-syntax-highlight ruby\">" \ "<code><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \ "</code></pre>" expect(helper.event_note(input)).to eq(expected) diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index c10f4b09b5b..2a0de0b0656 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -111,7 +111,7 @@ describe MarkupHelper do it 'replaces commit message with emoji to link' do actual = link_to_gfm(':book: Book', '/foo') expect(actual). - to eq '<gl-emoji data-name="book" data-unicode-version="6.0">📖</gl-emoji><a href="/foo"> Book</a>' + to eq '<gl-emoji title="open book" data-name="book" data-unicode-version="6.0">📖</gl-emoji><a href="/foo"> Book</a>' end end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index e9037749ef2..10681af5f7e 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -64,7 +64,7 @@ describe MergeRequestsHelper do it do @project = project - + is_expected.to eq("#1, #2, and #{other_project.namespace.path}/#{other_project.path}#3") end end @@ -149,6 +149,50 @@ describe MergeRequestsHelper do end end + describe '#target_projects' do + let(:project) { create(:empty_project) } + let(:fork_project) { create(:empty_project, forked_from_project: project) } + + context 'when target project has enabled merge requests' do + it 'returns the forked_from project' do + expect(target_projects(fork_project)).to contain_exactly(project, fork_project) + end + end + + context 'when target project has disabled merge requests' do + it 'returns the forked project' do + project.project_feature.update(merge_requests_access_level: 0) + + expect(target_projects(fork_project)).to contain_exactly(fork_project) + end + end + end + + describe '#new_mr_path_from_push_event' do + subject(:url_params) { URI.decode_www_form(new_mr_path_from_push_event(event)).to_h } + let(:user) { create(:user) } + let(:project) { create(:empty_project, creator: user) } + let(:fork_project) { create(:project, forked_from_project: project, creator: user) } + let(:event) do + push_data = Gitlab::DataBuilder::Push.build_sample(fork_project, user) + create(:event, :pushed, project: fork_project, target: fork_project, author: user, data: push_data) + end + + context 'when target project has enabled merge requests' do + it 'returns link to create merge request on source project' do + expect(url_params['merge_request[target_project_id]'].to_i).to eq(project.id) + end + end + + context 'when target project has disabled merge requests' do + it 'returns link to create merge request on forked project' do + project.project_feature.update(merge_requests_access_level: 0) + + expect(url_params['merge_request[target_project_id]'].to_i).to eq(fork_project.id) + end + end + end + describe '#mr_issues_mentioned_but_not_closing' do let(:user_1) { create(:user) } let(:user_2) { create(:user) } diff --git a/spec/javascripts/blob/pdf/index_spec.js b/spec/javascripts/blob/pdf/index_spec.js index d3a4d04345b..bbeaf95e68d 100644 --- a/spec/javascripts/blob/pdf/index_spec.js +++ b/spec/javascripts/blob/pdf/index_spec.js @@ -1,5 +1,7 @@ +/* eslint-disable import/no-unresolved */ + import renderPDF from '~/blob/pdf'; -import testPDF from './test.pdf'; +import testPDF from '../../fixtures/blob/pdf/test.pdf'; describe('PDF renderer', () => { let viewer; @@ -59,7 +61,7 @@ describe('PDF renderer', () => { describe('error getting file', () => { beforeEach((done) => { - viewer.dataset.endpoint = 'invalid/endpoint'; + viewer.dataset.endpoint = 'invalid/path/to/file.pdf'; app = renderPDF(); checkLoaded(done); diff --git a/spec/javascripts/blob/pdf/test.pdf b/spec/javascripts/blob/pdf/test.pdf Binary files differdeleted file mode 100644 index eb3d147fde3..00000000000 --- a/spec/javascripts/blob/pdf/test.pdf +++ /dev/null diff --git a/spec/javascripts/fixtures/environments.rb b/spec/javascripts/fixtures/environments.rb new file mode 100644 index 00000000000..3474f4696ef --- /dev/null +++ b/spec/javascripts/fixtures/environments.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Projects::EnvironmentsController, '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + let(:admin) { create(:admin) } + let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:project) { create(:project_empty_repo, namespace: namespace, path: 'environments-project') } + let(:environment) { create(:environment, name: 'production', project: project) } + + render_views + + before(:all) do + clean_frontend_fixtures('environments/metrics') + end + + before(:each) do + sign_in(admin) + end + + it 'environments/metrics/metrics.html.raw' do |example| + get :metrics, + namespace_id: project.namespace, + project_id: project, + id: environment.id + + expect(response).to be_success + store_frontend_fixture(response, example.description) + end +end diff --git a/spec/javascripts/fixtures/environments/metrics.html.haml b/spec/javascripts/fixtures/environments/metrics.html.haml deleted file mode 100644 index e2dd9519898..00000000000 --- a/spec/javascripts/fixtures/environments/metrics.html.haml +++ /dev/null @@ -1,62 +0,0 @@ -.prometheus-container{ 'data-has-metrics': "false", 'data-doc-link': '/help/administration/monitoring/prometheus/index.md', 'data-prometheus-integration': '/root/hello-prometheus/services/prometheus/edit' } - .top-area - .row - .col-sm-6 - %h3.page-title - Metrics for environment - .prometheus-state - .js-getting-started.hidden - .row - .col-md-4.col-md-offset-4.state-svg - %svg - .row - .col-md-6.col-md-offset-3 - %h4.text-center.state-title - Get started with performance monitoring - .row - .col-md-6.col-md-offset-3 - .description-text.text-center.state-description - Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments. Learn more about performance monitoring - .row.state-button-section - .col-md-4.col-md-offset-4.text-center.state-button - %a.btn.btn-success - Configure Prometheus - .js-loading.hidden - .row - .col-md-4.col-md-offset-4.state-svg - %svg - .row - .col-md-6.col-md-offset-3 - %h4.text-center.state-title - Waiting for performance data - .row - .col-md-6.col-md-offset-3 - .description-text.text-center.state-description - Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available. - .row.state-button-section - .col-md-4.col-md-offset-4.text-center.state-button - %a.btn.btn-success - View documentation - .js-unable-to-connect.hidden - .row - .col-md-4.col-md-offset-4.state-svg - %svg - .row - .col-md-6.col-md-offset-3 - %h4.text-center.state-title - Unable to connect to Prometheus server - .row - .col-md-6.col-md-offset-3 - .description-text.text-center.state-description - Ensure connectivity is available from the GitLab server to the Prometheus server - .row.state-button-section - .col-md-4.col-md-offset-4.text-center.state-button - %a.btn.btn-success - View documentation - .prometheus-graphs - .row - .col-sm-12 - %svg.prometheus-graph{ 'graph-type' => 'cpu_values' } - .row - .col-sm-12 - %svg.prometheus-graph{ 'graph-type' => 'memory_values' } diff --git a/spec/javascripts/fixtures/line_highlighter.html.haml b/spec/javascripts/fixtures/line_highlighter.html.haml index 514877340e4..2782c50e298 100644 --- a/spec/javascripts/fixtures/line_highlighter.html.haml +++ b/spec/javascripts/fixtures/line_highlighter.html.haml @@ -1,4 +1,4 @@ -#blob-content-holder +.file-holder .file-content .line-numbers - 1.upto(25) do |i| diff --git a/spec/javascripts/fixtures/pdf.rb b/spec/javascripts/fixtures/pdf.rb new file mode 100644 index 00000000000..6b2422a7986 --- /dev/null +++ b/spec/javascripts/fixtures/pdf.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe 'PDF file', '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:project) { create(:project, namespace: namespace, path: 'pdf-project') } + + before(:all) do + clean_frontend_fixtures('blob/pdf/') + end + + it 'blob/pdf/test.pdf' do |example| + blob = project.repository.blob_at('e774ebd33', 'files/pdf/test.pdf') + + store_frontend_fixture(blob.data.force_encoding("utf-8"), example.description) + end +end diff --git a/spec/javascripts/landing_spec.js b/spec/javascripts/landing_spec.js new file mode 100644 index 00000000000..7916073190a --- /dev/null +++ b/spec/javascripts/landing_spec.js @@ -0,0 +1,160 @@ +import Landing from '~/landing'; +import Cookies from 'js-cookie'; + +describe('Landing', function () { + describe('class constructor', function () { + beforeEach(function () { + this.landingElement = {}; + this.dismissButton = {}; + this.cookieName = 'cookie_name'; + + this.landing = new Landing(this.landingElement, this.dismissButton, this.cookieName); + }); + + it('should set .landing', function () { + expect(this.landing.landingElement).toBe(this.landingElement); + }); + + it('should set .cookieName', function () { + expect(this.landing.cookieName).toBe(this.cookieName); + }); + + it('should set .dismissButton', function () { + expect(this.landing.dismissButton).toBe(this.dismissButton); + }); + + it('should set .eventWrapper', function () { + expect(this.landing.eventWrapper).toEqual({}); + }); + }); + + describe('toggle', function () { + beforeEach(function () { + this.isDismissed = false; + this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) }; + this.landing = { + isDismissed: () => {}, + addEvents: () => {}, + landingElement: this.landingElement, + }; + + spyOn(this.landing, 'isDismissed').and.returnValue(this.isDismissed); + spyOn(this.landing, 'addEvents'); + + Landing.prototype.toggle.call(this.landing); + }); + + it('should call .isDismissed', function () { + expect(this.landing.isDismissed).toHaveBeenCalled(); + }); + + it('should call .classList.toggle', function () { + expect(this.landingElement.classList.toggle).toHaveBeenCalledWith('hidden', this.isDismissed); + }); + + it('should call .addEvents', function () { + expect(this.landing.addEvents).toHaveBeenCalled(); + }); + + describe('if isDismissed is true', function () { + beforeEach(function () { + this.isDismissed = true; + this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) }; + this.landing = { + isDismissed: () => {}, + addEvents: () => {}, + landingElement: this.landingElement, + }; + + spyOn(this.landing, 'isDismissed').and.returnValue(this.isDismissed); + spyOn(this.landing, 'addEvents'); + + this.landing.isDismissed.calls.reset(); + + Landing.prototype.toggle.call(this.landing); + }); + + it('should not call .addEvents', function () { + expect(this.landing.addEvents).not.toHaveBeenCalled(); + }); + }); + }); + + describe('addEvents', function () { + beforeEach(function () { + this.dismissButton = jasmine.createSpyObj('dismissButton', ['addEventListener']); + this.eventWrapper = {}; + this.landing = { + eventWrapper: this.eventWrapper, + dismissButton: this.dismissButton, + dismissLanding: () => {}, + }; + + Landing.prototype.addEvents.call(this.landing); + }); + + it('should set .eventWrapper.dismissLanding', function () { + expect(this.eventWrapper.dismissLanding).toEqual(jasmine.any(Function)); + }); + + it('should call .addEventListener', function () { + expect(this.dismissButton.addEventListener).toHaveBeenCalledWith('click', this.eventWrapper.dismissLanding); + }); + }); + + describe('removeEvents', function () { + beforeEach(function () { + this.dismissButton = jasmine.createSpyObj('dismissButton', ['removeEventListener']); + this.eventWrapper = { dismissLanding: () => {} }; + this.landing = { + eventWrapper: this.eventWrapper, + dismissButton: this.dismissButton, + }; + + Landing.prototype.removeEvents.call(this.landing); + }); + + it('should call .removeEventListener', function () { + expect(this.dismissButton.removeEventListener).toHaveBeenCalledWith('click', this.eventWrapper.dismissLanding); + }); + }); + + describe('dismissLanding', function () { + beforeEach(function () { + this.landingElement = { classList: jasmine.createSpyObj('classList', ['add']) }; + this.cookieName = 'cookie_name'; + this.landing = { landingElement: this.landingElement, cookieName: this.cookieName }; + + spyOn(Cookies, 'set'); + + Landing.prototype.dismissLanding.call(this.landing); + }); + + it('should call .classList.add', function () { + expect(this.landingElement.classList.add).toHaveBeenCalledWith('hidden'); + }); + + it('should call Cookies.set', function () { + expect(Cookies.set).toHaveBeenCalledWith(this.cookieName, 'true', { expires: 365 }); + }); + }); + + describe('isDismissed', function () { + beforeEach(function () { + this.cookieName = 'cookie_name'; + this.landing = { cookieName: this.cookieName }; + + spyOn(Cookies, 'get').and.returnValue('true'); + + this.isDismissed = Landing.prototype.isDismissed.call(this.landing); + }); + + it('should call Cookies.get', function () { + expect(Cookies.get).toHaveBeenCalledWith(this.cookieName); + }); + + it('should return a boolean', function () { + expect(typeof this.isDismissed).toEqual('boolean'); + }); + }); +}); diff --git a/spec/javascripts/monitoring/deployments_spec.js b/spec/javascripts/monitoring/deployments_spec.js new file mode 100644 index 00000000000..19bc11d0f24 --- /dev/null +++ b/spec/javascripts/monitoring/deployments_spec.js @@ -0,0 +1,133 @@ +import d3 from 'd3'; +import PrometheusGraph from '~/monitoring/prometheus_graph'; +import Deployments from '~/monitoring/deployments'; +import { prometheusMockData } from './prometheus_mock_data'; + +describe('Metrics deployments', () => { + const fixtureName = 'environments/metrics/metrics.html.raw'; + let deployment; + let prometheusGraph; + + const graphElement = () => document.querySelector('.prometheus-graph'); + + preloadFixtures(fixtureName); + + beforeEach((done) => { + // Setup the view + loadFixtures(fixtureName); + + d3.selectAll('.prometheus-graph') + .append('g') + .attr('class', 'graph-container'); + + prometheusGraph = new PrometheusGraph(); + deployment = new Deployments(1000, 500); + + spyOn(prometheusGraph, 'init'); + spyOn($, 'ajax').and.callFake(() => { + const d = $.Deferred(); + d.resolve({ + deployments: [{ + id: 1, + created_at: deployment.chartData[10].time, + sha: 'testing', + tag: false, + ref: { + name: 'testing', + }, + }, { + id: 2, + created_at: deployment.chartData[15].time, + sha: '', + tag: true, + ref: { + name: 'tag', + }, + }], + }); + + setTimeout(done); + + return d.promise(); + }); + + prometheusGraph.configureGraph(); + prometheusGraph.transformData(prometheusMockData.metrics); + + deployment.init(prometheusGraph.graphSpecificProperties.memory_values.data); + }); + + it('creates line on graph for deploment', () => { + expect( + graphElement().querySelectorAll('.deployment-line').length, + ).toBe(2); + }); + + it('creates hidden deploy boxes', () => { + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box').length, + ).toBe(2); + }); + + it('hides the info boxes by default', () => { + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, + ).toBe(2); + }); + + it('shows sha short code when tag is false', () => { + expect( + graphElement().querySelector('.deploy-info-1-cpu_values .js-deploy-info-box').textContent.trim(), + ).toContain('testin'); + }); + + it('shows ref name when tag is true', () => { + expect( + graphElement().querySelector('.deploy-info-2-cpu_values .js-deploy-info-box').textContent.trim(), + ).toContain('tag'); + }); + + it('shows info box when moving mouse over line', () => { + deployment.mouseOverDeployInfo(deployment.data[0].xPos, 'cpu_values'); + + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, + ).toBe(1); + + expect( + graphElement().querySelector('.deploy-info-1-cpu_values .js-deploy-info-box.hidden'), + ).toBeNull(); + }); + + it('hides previously visible info box when moving mouse away', () => { + deployment.mouseOverDeployInfo(500, 'cpu_values'); + + expect( + graphElement().querySelectorAll('.prometheus-graph .js-deploy-info-box.hidden').length, + ).toBe(2); + + expect( + graphElement().querySelector('.deploy-info-1-cpu_values .js-deploy-info-box.hidden'), + ).not.toBeNull(); + }); + + describe('refText', () => { + it('returns shortened SHA', () => { + expect( + Deployments.refText({ + tag: false, + sha: '123456789', + }), + ).toBe('123456'); + }); + + it('returns tag name', () => { + expect( + Deployments.refText({ + tag: true, + ref: 'v1.0', + }), + ).toBe('v1.0'); + }); + }); +}); diff --git a/spec/javascripts/monitoring/prometheus_graph_spec.js b/spec/javascripts/monitoring/prometheus_graph_spec.js index 4b904fc2960..25578bf1c6e 100644 --- a/spec/javascripts/monitoring/prometheus_graph_spec.js +++ b/spec/javascripts/monitoring/prometheus_graph_spec.js @@ -3,7 +3,7 @@ import PrometheusGraph from '~/monitoring/prometheus_graph'; import { prometheusMockData } from './prometheus_mock_data'; describe('PrometheusGraph', () => { - const fixtureName = 'static/environments/metrics.html.raw'; + const fixtureName = 'environments/metrics/metrics.html.raw'; const prometheusGraphContainer = '.prometheus-graph'; const prometheusGraphContents = `${prometheusGraphContainer}[graph-type=cpu_values]`; @@ -77,7 +77,7 @@ describe('PrometheusGraph', () => { }); describe('PrometheusGraphs UX states', () => { - const fixtureName = 'static/environments/metrics.html.raw'; + const fixtureName = 'environments/metrics/metrics.html.raw'; preloadFixtures(fixtureName); beforeEach(() => { diff --git a/spec/javascripts/pdf/index_spec.js b/spec/javascripts/pdf/index_spec.js new file mode 100644 index 00000000000..f661fae5fe2 --- /dev/null +++ b/spec/javascripts/pdf/index_spec.js @@ -0,0 +1,61 @@ +/* eslint-disable import/no-unresolved */ + +import Vue from 'vue'; +import { PDFJS } from 'pdfjs-dist'; +import workerSrc from 'vendor/pdf.worker'; + +import PDFLab from '~/pdf/index.vue'; +import pdf from '../fixtures/blob/pdf/test.pdf'; + +PDFJS.workerSrc = workerSrc; +const Component = Vue.extend(PDFLab); + +describe('PDF component', () => { + let vm; + + const checkLoaded = (done) => { + if (vm.loading) { + setTimeout(() => { + checkLoaded(done); + }, 100); + } else { + done(); + } + }; + + describe('without PDF data', () => { + beforeEach((done) => { + vm = new Component({ + propsData: { + pdf: '', + }, + }); + + vm.$mount(); + + checkLoaded(done); + }); + + it('does not render', () => { + expect(vm.$el.tagName).toBeUndefined(); + }); + }); + + describe('with PDF data', () => { + beforeEach((done) => { + vm = new Component({ + propsData: { + pdf, + }, + }); + + vm.$mount(); + + checkLoaded(done); + }); + + it('renders pdf component', () => { + expect(vm.$el.tagName).toBeDefined(); + }); + }); +}); diff --git a/spec/javascripts/pdf/page_spec.js b/spec/javascripts/pdf/page_spec.js new file mode 100644 index 00000000000..ac76ebbfbe6 --- /dev/null +++ b/spec/javascripts/pdf/page_spec.js @@ -0,0 +1,57 @@ +/* eslint-disable import/no-unresolved */ + +import Vue from 'vue'; +import pdfjsLib from 'pdfjs-dist'; +import workerSrc from 'vendor/pdf.worker'; + +import PageComponent from '~/pdf/page/index.vue'; +import testPDF from '../fixtures/blob/pdf/test.pdf'; + +const Component = Vue.extend(PageComponent); + +describe('Page component', () => { + let vm; + let testPage; + pdfjsLib.PDFJS.workerSrc = workerSrc; + + const checkRendered = (done) => { + if (vm.rendering) { + setTimeout(() => { + checkRendered(done); + }, 100); + } else { + done(); + } + }; + + beforeEach((done) => { + pdfjsLib.getDocument(testPDF) + .then(pdf => pdf.getPage(1)) + .then((page) => { + testPage = page; + done(); + }) + .catch((error) => { + console.error(error); + }); + }); + + describe('render', () => { + beforeEach((done) => { + vm = new Component({ + propsData: { + page: testPage, + number: 1, + }, + }); + + vm.$mount(); + + checkRendered(done); + }); + + it('renders first page', () => { + expect(vm.$el.tagName).toBeDefined(); + }); + }); +}); diff --git a/spec/lib/banzai/renderer_spec.rb b/spec/lib/banzai/renderer_spec.rb index e6f8d2a1fed..0e094405e33 100644 --- a/spec/lib/banzai/renderer_spec.rb +++ b/spec/lib/banzai/renderer_spec.rb @@ -11,7 +11,7 @@ describe Banzai::Renderer do end describe '#render_field' do - let(:renderer) { Banzai::Renderer } + let(:renderer) { described_class } subject { renderer.render_field(object, :field) } context 'with a stale cache' do diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb index 96dacdc5cd2..f95adf3a84b 100644 --- a/spec/lib/constraints/group_url_constrainer_spec.rb +++ b/spec/lib/constraints/group_url_constrainer_spec.rb @@ -17,6 +17,13 @@ describe GroupUrlConstrainer, lib: true do it { expect(subject.matches?(request)).to be_truthy } end + context 'valid request for nested group with reserved top level name' do + let!(:nested_group) { create(:group, path: 'api', parent: group) } + let!(:request) { build_request('gitlab/api') } + + it { expect(subject.matches?(request)).to be_truthy } + end + context 'invalid request' do let(:request) { build_request('foo') } diff --git a/spec/lib/gitlab/changes_list_spec.rb b/spec/lib/gitlab/changes_list_spec.rb index 69d86144e32..464508fcd73 100644 --- a/spec/lib/gitlab/changes_list_spec.rb +++ b/spec/lib/gitlab/changes_list_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::ChangesList do let(:invalid_changes) { 1 } context 'when changes is a valid string' do - let(:changes_list) { Gitlab::ChangesList.new(valid_changes_string) } + let(:changes_list) { described_class.new(valid_changes_string) } it 'splits elements by newline character' do expect(changes_list).to contain_exactly({ diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb index 7a84bbebd02..bc66ce83d4a 100644 --- a/spec/lib/gitlab/checks/force_push_spec.rb +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' -describe Gitlab::Checks::ChangeAccess, lib: true do +describe Gitlab::Checks::ForcePush, lib: true do let(:project) { create(:project, :repository) } context "exit code checking" do it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0]) - expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error + expect { described_class.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error end it "raises a runtime error if the `popen` call to git returns a non-zero exit code" do allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1]) - expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) + expect { described_class.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) end end end diff --git a/spec/lib/gitlab/ci/build/credentials/factory_spec.rb b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb index 10b4b7a8826..d53db05e5e6 100644 --- a/spec/lib/gitlab/ci/build/credentials/factory_spec.rb +++ b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' describe Gitlab::Ci::Build::Credentials::Factory do let(:build) { create(:ci_build, name: 'spinach', stage: 'test', stage_idx: 0) } - subject { Gitlab::Ci::Build::Credentials::Factory.new(build).create! } + subject { described_class.new(build).create! } class TestProvider def initialize(build); end end before do - allow_any_instance_of(Gitlab::Ci::Build::Credentials::Factory).to receive(:providers).and_return([TestProvider]) + allow_any_instance_of(described_class).to receive(:providers).and_return([TestProvider]) end context 'when provider is valid' do diff --git a/spec/lib/gitlab/ci/build/credentials/registry_spec.rb b/spec/lib/gitlab/ci/build/credentials/registry_spec.rb index 84e44dd53e2..c6054138cde 100644 --- a/spec/lib/gitlab/ci/build/credentials/registry_spec.rb +++ b/spec/lib/gitlab/ci/build/credentials/registry_spec.rb @@ -4,14 +4,14 @@ describe Gitlab::Ci::Build::Credentials::Registry do let(:build) { create(:ci_build, name: 'spinach', stage: 'test', stage_idx: 0) } let(:registry_url) { 'registry.example.com:5005' } - subject { Gitlab::Ci::Build::Credentials::Registry.new(build) } + subject { described_class.new(build) } before do stub_container_registry_config(host_port: registry_url) end it 'contains valid DockerRegistry credentials' do - expect(subject).to be_kind_of(Gitlab::Ci::Build::Credentials::Registry) + expect(subject).to be_kind_of(described_class) expect(subject.username).to eq 'gitlab-ci-token' expect(subject.password).to eq build.token @@ -20,7 +20,7 @@ describe Gitlab::Ci::Build::Credentials::Registry do end describe '.valid?' do - subject { Gitlab::Ci::Build::Credentials::Registry.new(build).valid? } + subject { described_class.new(build).valid? } context 'when registry is enabled' do before do diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb index 0864bc7258d..809fda11879 100644 --- a/spec/lib/gitlab/ci/cron_parser_spec.rb +++ b/spec/lib/gitlab/ci/cron_parser_spec.rb @@ -60,14 +60,60 @@ describe Gitlab::Ci::CronParser do end end - context 'when cron_timezone is US/Pacific' do - let(:cron) { '0 0 * * *' } - let(:cron_timezone) { 'US/Pacific' } + context 'when cron_timezone is TZInfo format' do + before do + allow(Time).to receive(:zone) + .and_return(ActiveSupport::TimeZone['UTC']) + end - it_behaves_like "returns time in the future" + let(:hour_in_utc) do + ActiveSupport::TimeZone[cron_timezone] + .now.change(hour: 0).in_time_zone('UTC').hour + end + + context 'when cron_timezone is US/Pacific' do + let(:cron) { '* 0 * * *' } + let(:cron_timezone) { 'US/Pacific' } + + it_behaves_like "returns time in the future" + + it 'converts time in server time zone' do + expect(subject.hour).to eq(hour_in_utc) + end + end + end + + context 'when cron_timezone is ActiveSupport::TimeZone format' do + before do + allow(Time).to receive(:zone) + .and_return(ActiveSupport::TimeZone['UTC']) + end + + let(:hour_in_utc) do + ActiveSupport::TimeZone[cron_timezone] + .now.change(hour: 0).in_time_zone('UTC').hour + end + + context 'when cron_timezone is Berlin' do + let(:cron) { '* 0 * * *' } + let(:cron_timezone) { 'Berlin' } + + it_behaves_like "returns time in the future" + + it 'converts time in server time zone' do + expect(subject.hour).to eq(hour_in_utc) + end + end - it 'converts time in server time zone' do - expect(subject.hour).to eq((Time.zone.now.in_time_zone(cron_timezone).utc_offset / 60 / 60).abs) + context 'when cron_timezone is Eastern Time (US & Canada)' do + let(:cron) { '* 0 * * *' } + let(:cron_timezone) { 'Eastern Time (US & Canada)' } + + it_behaves_like "returns time in the future" + + it 'converts time in server time zone' do + expect(subject.hour).to eq(hour_in_utc) + end end end end @@ -76,9 +122,21 @@ describe Gitlab::Ci::CronParser do let(:cron) { 'invalid_cron' } let(:cron_timezone) { 'invalid_cron_timezone' } - it 'returns nil' do - is_expected.to be_nil - end + it { is_expected.to be_nil } + end + + context 'when cron syntax is quoted' do + let(:cron) { "'0 * * * *'" } + let(:cron_timezone) { 'UTC' } + + it { expect(subject).to be_nil } + end + + context 'when cron syntax is rufus-scheduler syntax' do + let(:cron) { 'every 3h' } + let(:cron_timezone) { 'UTC' } + + it { expect(subject).to be_nil } end end @@ -96,6 +154,12 @@ describe Gitlab::Ci::CronParser do it { is_expected.to eq(false) } end + + context 'when cron syntax is quoted' do + let(:cron) { "'0 * * * *'" } + + it { is_expected.to eq(false) } + end end describe '#cron_timezone_valid?' do @@ -112,5 +176,11 @@ describe Gitlab::Ci::CronParser do it { is_expected.to eq(false) } end + + context 'when cron_timezone is ActiveSupport::TimeZone format' do + let(:cron_timezone) { 'Eastern Time (US & Canada)' } + + it { is_expected.to eq(true) } + end end end diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb index b01c4805a34..c796c98ec9f 100644 --- a/spec/lib/gitlab/current_settings_spec.rb +++ b/spec/lib/gitlab/current_settings_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::CurrentSettings do describe '#current_application_settings' do context 'with DB available' do before do - allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true) + allow_any_instance_of(described_class).to receive(:connect_to_db?).and_return(true) end it 'attempts to use cached values first' do @@ -36,7 +36,7 @@ describe Gitlab::CurrentSettings do context 'with DB unavailable' do before do - allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false) + allow_any_instance_of(described_class).to receive(:connect_to_db?).and_return(false) end it 'returns an in-memory ApplicationSetting object' do diff --git a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb index c455cd9b942..d8757c601ab 100644 --- a/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/base_event_fetcher_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::CycleAnalytics::BaseEventFetcher do before do allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return(Issue.all) - allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:serialize) do |event| + allow_any_instance_of(described_class).to receive(:serialize) do |event| event end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index a044b871730..737fac14f92 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -726,4 +726,37 @@ describe Gitlab::Database::MigrationHelpers, lib: true do expect(model.column_for(:users, :kittens)).to be_nil end end + + describe '#replace_sql' do + context 'using postgres' do + before do + allow(Gitlab::Database).to receive(:mysql?).and_return(false) + end + + it 'builds the sql with correct functions' do + expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s). + to include('regexp_replace') + end + end + + context 'using mysql' do + before do + allow(Gitlab::Database).to receive(:mysql?).and_return(true) + end + + it 'builds the sql with the correct functions' do + expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s). + to include('locate', 'insert') + end + end + + describe 'results' do + let!(:user) { create(:user, name: 'Kathy Alice Aliceson') } + + it 'replaces the correct part of the string' do + model.update_column_in_batches(:users, :name, model.replace_sql(Arel::Table.new(:users)[:name], 'Alice', 'Eve')) + expect(user.reload.name).to eq('Kathy Eve Aliceson') + end + end + end end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb new file mode 100644 index 00000000000..64bc5fc0429 --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb @@ -0,0 +1,197 @@ +require 'spec_helper' + +describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase do + let(:migration) { FakeRenameReservedPathMigrationV1.new } + let(:subject) { described_class.new(['the-path'], migration) } + + before do + allow(migration).to receive(:say) + end + + def migration_namespace(namespace) + Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses:: + Namespace.find(namespace.id) + end + + def migration_project(project) + Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses:: + Project.find(project.id) + end + + describe "#remove_last_ocurrence" do + it "removes only the last occurance of a string" do + input = "this/is/a-word-to-replace/namespace/with/a-word-to-replace" + + expect(subject.remove_last_occurrence(input, "a-word-to-replace")) + .to eq("this/is/a-word-to-replace/namespace/with/") + end + end + + describe '#remove_cached_html_for_projects' do + let(:project) { create(:empty_project, description_html: 'Project description') } + + it 'removes description_html from projects' do + subject.remove_cached_html_for_projects([project.id]) + + expect(project.reload.description_html).to be_nil + end + + it 'removes issue descriptions' do + issue = create(:issue, project: project, description_html: 'Issue description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(issue.reload.description_html).to be_nil + end + + it 'removes merge request descriptions' do + merge_request = create(:merge_request, + source_project: project, + target_project: project, + description_html: 'MergeRequest description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(merge_request.reload.description_html).to be_nil + end + + it 'removes note html' do + note = create(:note, + project: project, + noteable: create(:issue, project: project), + note_html: 'note description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(note.reload.note_html).to be_nil + end + + it 'removes milestone description' do + milestone = create(:milestone, + project: project, + description_html: 'milestone description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(milestone.reload.description_html).to be_nil + end + end + + describe '#rename_path_for_routable' do + context 'for namespaces' do + let(:namespace) { create(:namespace, path: 'the-path') } + it "renames namespaces called the-path" do + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(namespace.reload.path).to eq("the-path0") + end + + it "renames the route to the namespace" do + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(Namespace.find(namespace.id).full_path).to eq("the-path0") + end + + it "renames the route for projects of the namespace" do + project = create(:project, path: "project-path", namespace: namespace) + + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(project.route.reload.path).to eq("the-path0/project-path") + end + + it 'returns the old & the new path' do + old_path, new_path = subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(old_path).to eq('the-path') + expect(new_path).to eq('the-path0') + end + + context "the-path namespace -> subgroup -> the-path0 project" do + it "updates the route of the project correctly" do + subgroup = create(:group, path: "subgroup", parent: namespace) + project = create(:project, path: "the-path0", namespace: subgroup) + + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(project.route.reload.path).to eq("the-path0/subgroup/the-path0") + end + end + end + + context 'for projects' do + let(:parent) { create(:namespace, path: 'the-parent') } + let(:project) { create(:empty_project, path: 'the-path', namespace: parent) } + + it 'renames the project called `the-path`' do + subject.rename_path_for_routable(migration_project(project)) + + expect(project.reload.path).to eq('the-path0') + end + + it 'renames the route for the project' do + subject.rename_path_for_routable(project) + + expect(project.reload.route.path).to eq('the-parent/the-path0') + end + + it 'returns the old & new path' do + old_path, new_path = subject.rename_path_for_routable(migration_project(project)) + + expect(old_path).to eq('the-parent/the-path') + expect(new_path).to eq('the-parent/the-path0') + end + end + end + + describe '#move_pages' do + it 'moves the pages directory' do + expect(subject).to receive(:move_folders) + .with(TestEnv.pages_path, 'old-path', 'new-path') + + subject.move_pages('old-path', 'new-path') + end + end + + describe "#move_uploads" do + let(:test_dir) { File.join(Rails.root, 'tmp', 'tests', 'rename_reserved_paths') } + let(:uploads_dir) { File.join(test_dir, 'public', 'uploads') } + + it 'moves subdirectories in the uploads folder' do + expect(subject).to receive(:uploads_dir).and_return(uploads_dir) + expect(subject).to receive(:move_folders).with(uploads_dir, 'old_path', 'new_path') + + subject.move_uploads('old_path', 'new_path') + end + + it "doesn't move uploads when they are stored in object storage" do + expect(subject).to receive(:file_storage?).and_return(false) + expect(subject).not_to receive(:move_folders) + + subject.move_uploads('old_path', 'new_path') + end + end + + describe '#move_folders' do + let(:test_dir) { File.join(Rails.root, 'tmp', 'tests', 'rename_reserved_paths') } + let(:uploads_dir) { File.join(test_dir, 'public', 'uploads') } + + before do + FileUtils.remove_dir(test_dir) if File.directory?(test_dir) + FileUtils.mkdir_p(uploads_dir) + allow(subject).to receive(:uploads_dir).and_return(uploads_dir) + end + + it 'moves a folder with files' do + source = File.join(uploads_dir, 'parent-group', 'sub-group') + FileUtils.mkdir_p(source) + destination = File.join(uploads_dir, 'parent-group', 'moved-group') + FileUtils.touch(File.join(source, 'test.txt')) + expected_file = File.join(destination, 'test.txt') + + subject.move_folders(uploads_dir, File.join('parent-group', 'sub-group'), File.join('parent-group', 'moved-group')) + + expect(File.exist?(expected_file)).to be(true) + end + end +end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb new file mode 100644 index 00000000000..a25c5da488a --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb @@ -0,0 +1,171 @@ +require 'spec_helper' + +describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do + let(:migration) { FakeRenameReservedPathMigrationV1.new } + let(:subject) { described_class.new(['the-path'], migration) } + + before do + allow(migration).to receive(:say) + end + + def migration_namespace(namespace) + Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses:: + Namespace.find(namespace.id) + end + + describe '#namespaces_for_paths' do + context 'nested namespaces' do + let(:subject) { described_class.new(['parent/the-Path'], migration) } + + it 'includes the namespace' do + parent = create(:namespace, path: 'parent') + child = create(:namespace, path: 'the-path', parent: parent) + + found_ids = subject.namespaces_for_paths(type: :child). + map(&:id) + expect(found_ids).to contain_exactly(child.id) + end + end + + context 'for child namespaces' do + it 'only returns child namespaces with the correct path' do + _root_namespace = create(:namespace, path: 'THE-path') + _other_path = create(:namespace, + path: 'other', + parent: create(:namespace)) + namespace = create(:namespace, + path: 'the-path', + parent: create(:namespace)) + + found_ids = subject.namespaces_for_paths(type: :child). + map(&:id) + expect(found_ids).to contain_exactly(namespace.id) + end + end + + context 'for top levelnamespaces' do + it 'only returns child namespaces with the correct path' do + root_namespace = create(:namespace, path: 'the-path') + _other_path = create(:namespace, path: 'other') + _child_namespace = create(:namespace, + path: 'the-path', + parent: create(:namespace)) + + found_ids = subject.namespaces_for_paths(type: :top_level). + map(&:id) + expect(found_ids).to contain_exactly(root_namespace.id) + end + end + end + + describe '#move_repositories' do + let(:namespace) { create(:group, name: 'hello-group') } + it 'moves a project for a namespace' do + create(:project, namespace: namespace, path: 'hello-project') + expected_path = File.join(TestEnv.repos_path, 'bye-group', 'hello-project.git') + + subject.move_repositories(namespace, 'hello-group', 'bye-group') + + expect(File.directory?(expected_path)).to be(true) + end + + it 'moves a namespace in a subdirectory correctly' do + child_namespace = create(:group, name: 'sub-group', parent: namespace) + create(:project, namespace: child_namespace, path: 'hello-project') + + expected_path = File.join(TestEnv.repos_path, 'hello-group', 'renamed-sub-group', 'hello-project.git') + + subject.move_repositories(child_namespace, 'hello-group/sub-group', 'hello-group/renamed-sub-group') + + expect(File.directory?(expected_path)).to be(true) + end + + it 'moves a parent namespace with subdirectories' do + child_namespace = create(:group, name: 'sub-group', parent: namespace) + create(:project, namespace: child_namespace, path: 'hello-project') + expected_path = File.join(TestEnv.repos_path, 'renamed-group', 'sub-group', 'hello-project.git') + + subject.move_repositories(child_namespace, 'hello-group', 'renamed-group') + + expect(File.directory?(expected_path)).to be(true) + end + end + + describe "#child_ids_for_parent" do + it "collects child ids for all levels" do + parent = create(:namespace) + first_child = create(:namespace, parent: parent) + second_child = create(:namespace, parent: parent) + third_child = create(:namespace, parent: second_child) + all_ids = [parent.id, first_child.id, second_child.id, third_child.id] + + collected_ids = subject.child_ids_for_parent(parent, ids: [parent.id]) + + expect(collected_ids).to contain_exactly(*all_ids) + end + end + + describe "#rename_namespace" do + let(:namespace) { create(:namespace, path: 'the-path') } + + it 'renames paths & routes for the namespace' do + expect(subject).to receive(:rename_path_for_routable). + with(namespace). + and_call_original + + subject.rename_namespace(namespace) + + expect(namespace.reload.path).to eq('the-path0') + end + + it "moves the the repository for a project in the namespace" do + create(:project, namespace: namespace, path: "the-path-project") + expected_repo = File.join(TestEnv.repos_path, "the-path0", "the-path-project.git") + + subject.rename_namespace(namespace) + + expect(File.directory?(expected_repo)).to be(true) + end + + it "moves the uploads for the namespace" do + expect(subject).to receive(:move_uploads).with("the-path", "the-path0") + + subject.rename_namespace(namespace) + end + + it "moves the pages for the namespace" do + expect(subject).to receive(:move_pages).with("the-path", "the-path0") + + subject.rename_namespace(namespace) + end + + it 'invalidates the markdown cache of related projects' do + project = create(:empty_project, namespace: namespace, path: "the-path-project") + + expect(subject).to receive(:remove_cached_html_for_projects).with([project.id]) + + subject.rename_namespace(namespace) + end + end + + describe '#rename_namespaces' do + let!(:top_level_namespace) { create(:namespace, path: 'the-path') } + let!(:child_namespace) do + create(:namespace, path: 'the-path', parent: create(:namespace)) + end + + it 'renames top level namespaces the namespace' do + expect(subject).to receive(:rename_namespace). + with(migration_namespace(top_level_namespace)) + + subject.rename_namespaces(type: :top_level) + end + + it 'renames child namespaces' do + expect(subject).to receive(:rename_namespace). + with(migration_namespace(child_namespace)) + + subject.rename_namespaces(type: :child) + end + end +end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb new file mode 100644 index 00000000000..59e8de2712d --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do + let(:migration) { FakeRenameReservedPathMigrationV1.new } + let(:subject) { described_class.new(['the-path'], migration) } + + before do + allow(migration).to receive(:say) + end + + describe '#projects_for_paths' do + it 'searches using nested paths' do + namespace = create(:namespace, path: 'hello') + project = create(:empty_project, path: 'THE-path', namespace: namespace) + + result_ids = described_class.new(['Hello/the-path'], migration). + projects_for_paths.map(&:id) + + expect(result_ids).to contain_exactly(project.id) + end + + it 'includes the correct projects' do + project = create(:empty_project, path: 'THE-path') + _other_project = create(:empty_project) + + result_ids = subject.projects_for_paths.map(&:id) + + expect(result_ids).to contain_exactly(project.id) + end + end + + describe '#rename_projects' do + let!(:projects) { create_list(:empty_project, 2, path: 'the-path') } + + it 'renames each project' do + expect(subject).to receive(:rename_project).twice + + subject.rename_projects + end + + it 'invalidates the markdown cache of related projects' do + expect(subject).to receive(:remove_cached_html_for_projects). + with(projects.map(&:id)) + + subject.rename_projects + end + end + + describe '#rename_project' do + let(:project) do + create(:empty_project, + path: 'the-path', + namespace: create(:namespace, path: 'known-parent' )) + end + + it 'renames path & route for the project' do + expect(subject).to receive(:rename_path_for_routable). + with(project). + and_call_original + + subject.rename_project(project) + + expect(project.reload.path).to eq('the-path0') + end + + it 'moves the wiki & the repo' do + expect(subject).to receive(:move_repository). + with(project, 'known-parent/the-path.wiki', 'known-parent/the-path0.wiki') + expect(subject).to receive(:move_repository). + with(project, 'known-parent/the-path', 'known-parent/the-path0') + + subject.rename_project(project) + end + + it 'moves uploads' do + expect(subject).to receive(:move_uploads). + with('known-parent/the-path', 'known-parent/the-path0') + + subject.rename_project(project) + end + + it 'moves pages' do + expect(subject).to receive(:move_pages). + with('known-parent/the-path', 'known-parent/the-path0') + + subject.rename_project(project) + end + end + + describe '#move_repository' do + let(:known_parent) { create(:namespace, path: 'known-parent') } + let(:project) { create(:project, path: 'the-path', namespace: known_parent) } + + it 'moves the repository for a project' do + expected_path = File.join(TestEnv.repos_path, 'known-parent', 'new-repo.git') + + subject.move_repository(project, 'known-parent/the-path', 'known-parent/new-repo') + + expect(File.directory?(expected_path)).to be(true) + end + end +end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb new file mode 100644 index 00000000000..f8cc1eb91ec --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +shared_examples 'renames child namespaces' do |type| + it 'renames namespaces' do + rename_namespaces = double + expect(described_class::RenameNamespaces). + to receive(:new).with(['first-path', 'second-path'], subject). + and_return(rename_namespaces) + expect(rename_namespaces).to receive(:rename_namespaces). + with(type: :child) + + subject.rename_wildcard_paths(['first-path', 'second-path']) + end +end + +describe Gitlab::Database::RenameReservedPathsMigration::V1 do + let(:subject) { FakeRenameReservedPathMigrationV1.new } + + before do + allow(subject).to receive(:say) + end + + describe '#rename_child_paths' do + it_behaves_like 'renames child namespaces' + end + + describe '#rename_wildcard_paths' do + it_behaves_like 'renames child namespaces' + + it 'should rename projects' do + rename_projects = double + expect(described_class::RenameProjects). + to receive(:new).with(['the-path'], subject). + and_return(rename_projects) + + expect(rename_projects).to receive(:rename_projects) + + subject.rename_wildcard_paths(['the-path']) + end + end + + describe '#rename_root_paths' do + it 'should rename namespaces' do + rename_namespaces = double + expect(described_class::RenameNamespaces). + to receive(:new).with(['the-path'], subject). + and_return(rename_namespaces) + expect(rename_namespaces).to receive(:rename_namespaces). + with(type: :top_level) + + subject.rename_root_paths('the-path') + end + end +end diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index 2a86b427806..f127e45ae6a 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -7,9 +7,17 @@ describe Gitlab::Email::Receiver, lib: true do context "when we cannot find a capable handler" do let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "!!!") } - it "raises a UnknownIncomingEmail" do + it "raises an UnknownIncomingEmail error" do expect { receiver.execute }.to raise_error(Gitlab::Email::UnknownIncomingEmail) end + + context "and the email contains no references header" do + let(:email_raw) { fixture_file("emails/auto_reply.eml").gsub(mail_key, "!!!") } + + it "raises an UnknownIncomingEmail error" do + expect { receiver.execute }.to raise_error(Gitlab::Email::UnknownIncomingEmail) + end + end end context "when the email is blank" do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index f88653cb1fe..ddedb7c3443 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -24,21 +24,26 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - # TODO: Uncomment when feature is reenabled - # context 'with gitaly enabled' do - # before { stub_gitaly } - # - # it 'gets the branch name from GitalyClient' do - # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name) - # repository.root_ref - # end - # - # it 'wraps GRPC exceptions' do - # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name). - # and_raise(GRPC::Unknown) - # expect { repository.root_ref }.to raise_error(Gitlab::Git::CommandError) - # end - # end + context 'with gitaly enabled' do + before { stub_gitaly } + + it 'gets the branch name from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name) + repository.root_ref + end + + it 'wraps GRPC not found' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name). + and_raise(GRPC::NotFound) + expect { repository.root_ref }.to raise_error(Gitlab::Git::Repository::NoRepository) + end + + it 'wraps GRPC exceptions' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name). + and_raise(GRPC::Unknown) + expect { repository.root_ref }.to raise_error(Gitlab::Git::CommandError) + end + end end describe "#rugged" do @@ -113,21 +118,26 @@ describe Gitlab::Git::Repository, seed_helper: true do it { is_expected.to include("master") } it { is_expected.not_to include("branch-from-space") } - # TODO: Uncomment when feature is reenabled - # context 'with gitaly enabled' do - # before { stub_gitaly } - # - # it 'gets the branch names from GitalyClient' do - # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names) - # subject - # end - # - # it 'wraps GRPC exceptions' do - # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names). - # and_raise(GRPC::Unknown) - # expect { subject }.to raise_error(Gitlab::Git::CommandError) - # end - # end + context 'with gitaly enabled' do + before { stub_gitaly } + + it 'gets the branch names from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names) + subject + end + + it 'wraps GRPC not found' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names). + and_raise(GRPC::NotFound) + expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository) + end + + it 'wraps GRPC other exceptions' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names). + and_raise(GRPC::Unknown) + expect { subject }.to raise_error(Gitlab::Git::CommandError) + end + end end describe '#tag_names' do @@ -145,21 +155,26 @@ describe Gitlab::Git::Repository, seed_helper: true do it { is_expected.to include("v1.0.0") } it { is_expected.not_to include("v5.0.0") } - # TODO: Uncomment when feature is reenabled - # context 'with gitaly enabled' do - # before { stub_gitaly } - # - # it 'gets the tag names from GitalyClient' do - # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names) - # subject - # end - # - # it 'wraps GRPC exceptions' do - # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names). - # and_raise(GRPC::Unknown) - # expect { subject }.to raise_error(Gitlab::Git::CommandError) - # end - # end + context 'with gitaly enabled' do + before { stub_gitaly } + + it 'gets the tag names from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names) + subject + end + + it 'wraps GRPC not found' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names). + and_raise(GRPC::NotFound) + expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository) + end + + it 'wraps GRPC exceptions' do + expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names). + and_raise(GRPC::Unknown) + expect { subject }.to raise_error(Gitlab::Git::CommandError) + end + end end shared_examples 'archive check' do |extenstion| @@ -1074,20 +1089,8 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#branch_count' do - before(:each) do - valid_ref = double(:ref) - invalid_ref = double(:ref) - - allow(valid_ref).to receive_messages(name: 'master', target: double(:target)) - - allow(invalid_ref).to receive_messages(name: 'bad-branch') - allow(invalid_ref).to receive(:target) { raise Rugged::ReferenceError } - - allow(repository.rugged).to receive_messages(branches: [valid_ref, invalid_ref]) - end - it 'returns the number of branches' do - expect(repository.branch_count).to eq(1) + expect(repository.branch_count).to eq(9) end end diff --git a/spec/lib/gitlab/git/util_spec.rb b/spec/lib/gitlab/git/util_spec.rb index bcca4d4c746..69d3ca55397 100644 --- a/spec/lib/gitlab/git/util_spec.rb +++ b/spec/lib/gitlab/git/util_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::Git::Util do ["foo\n\n", 2], ].each do |string, line_count| it "counts #{line_count} lines in #{string.inspect}" do - expect(Gitlab::Git::Util.count_lines(string)).to eq(line_count) + expect(described_class.count_lines(string)).to eq(line_count) end end end diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/gitlab/git_ref_validator_spec.rb index cc8daa535d6..cc8daa535d6 100644 --- a/spec/lib/git_ref_validator_spec.rb +++ b/spec/lib/gitlab/git_ref_validator_spec.rb diff --git a/spec/lib/gitlab/gitaly_client/ref_spec.rb b/spec/lib/gitlab/gitaly_client/ref_spec.rb index 5405eafd281..255f23e6270 100644 --- a/spec/lib/gitlab/gitaly_client/ref_spec.rb +++ b/spec/lib/gitlab/gitaly_client/ref_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::GitalyClient::Ref do let(:project) { create(:empty_project) } let(:repo_path) { project.repository.path_to_repo } - let(:client) { Gitlab::GitalyClient::Ref.new(project.repository) } + let(:client) { described_class.new(project.repository) } before do allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true) diff --git a/spec/lib/gitlab/healthchecks/db_check_spec.rb b/spec/lib/gitlab/health_checks/db_check_spec.rb index 33c6c24449c..33c6c24449c 100644 --- a/spec/lib/gitlab/healthchecks/db_check_spec.rb +++ b/spec/lib/gitlab/health_checks/db_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb index 4cd8cf313a5..4cd8cf313a5 100644 --- a/spec/lib/gitlab/healthchecks/fs_shards_check_spec.rb +++ b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis_check_spec.rb index 734cdcb893e..734cdcb893e 100644 --- a/spec/lib/gitlab/healthchecks/redis_check_spec.rb +++ b/spec/lib/gitlab/health_checks/redis_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/simple_check_shared.rb b/spec/lib/gitlab/health_checks/simple_check_shared.rb index 1fa6d0faef9..1fa6d0faef9 100644 --- a/spec/lib/gitlab/healthchecks/simple_check_shared.rb +++ b/spec/lib/gitlab/health_checks/simple_check_shared.rb diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb index c5ce06afd73..42f3fc59f04 100644 --- a/spec/lib/gitlab/import_export/fork_spec.rb +++ b/spec/lib/gitlab/import_export/fork_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'forked project import', services: true do let(:user) { create(:user) } let!(:project_with_repo) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') } - let!(:project) { create(:empty_project) } + let!(:project) { create(:empty_project, name: 'test-repo-restorer-no-repo', path: 'test-repo-restorer-no-repo') } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:forked_from_project) { create(:project) } diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index bfecfa28ed1..fdbb6a0556d 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -2,6 +2,7 @@ "description": "Nisi et repellendus ut enim quo accusamus vel magnam.", "visibility_level": 10, "archived": false, + "description_html": "description", "labels": [ { "id": 2, diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 0e9607c5bd3..14338515892 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -30,6 +30,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::ENABLED) end + it 'has the project html description' do + expect(Project.find_by_path('project').description_html).to eq('description') + end + it 'has the same label associated to two issues' do expect(ProjectLabel.find_by_title('test2').issues.count).to eq(2) end diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index d2d89e3b019..6e145947104 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -189,6 +189,16 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do end end end + + context 'project attributes' do + it 'contains the html description' do + expect(saved_project_json).to include("description_html" => 'description') + end + + it 'does not contain the runners token' do + expect(saved_project_json).not_to include("runners_token" => 'token') + end + end end end @@ -209,6 +219,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do releases: [release], group: group ) + project.update(description_html: 'description') project_label = create(:label, project: project) group_label = create(:group_label, group: group) create(:label_link, label: project_label, target: issue) diff --git a/spec/lib/gitlab/import_export/reader_spec.rb b/spec/lib/gitlab/import_export/reader_spec.rb index 48d74b07e27..d700af142be 100644 --- a/spec/lib/gitlab/import_export/reader_spec.rb +++ b/spec/lib/gitlab/import_export/reader_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::ImportExport::Reader, lib: true do let(:test_config) { 'spec/support/import_export/import_export.yml' } let(:project_tree_hash) do { - only: [:name, :path], + except: [:id, :created_at], include: [:issues, :labels, { merge_requests: { only: [:id], diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 0372e3f7dbf..ebfaab4eacd 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -329,6 +329,28 @@ Project: - snippets_enabled - visibility_level - archived +- created_at +- updated_at +- last_activity_at +- star_count +- ci_id +- shared_runners_enabled +- build_coverage_regex +- build_allow_git_fetchs +- build_timeout +- pending_delete +- public_builds +- last_repository_check_failed +- last_repository_check_at +- container_registry_enabled +- only_allow_merge_if_pipeline_succeeds +- has_external_issue_tracker +- request_access_enabled +- has_external_wiki +- only_allow_merge_if_all_discussions_are_resolved +- auto_cancel_pending_pipelines +- printing_merge_request_link_enabled +- build_allow_git_fetch Author: - name ProjectFeature: diff --git a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb index 071e5fac3f0..071e5fac3f0 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb diff --git a/spec/lib/gitlab/ldap/person_spec.rb b/spec/lib/gitlab/ldap/person_spec.rb index 9a556cde5d5..087c4d8c92c 100644 --- a/spec/lib/gitlab/ldap/person_spec.rb +++ b/spec/lib/gitlab/ldap/person_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::LDAP::Person do it 'uses the configured name attribute and handles values as an array' do name = 'John Doe' entry['cn'] = [name] - person = Gitlab::LDAP::Person.new(entry, 'ldapmain') + person = described_class.new(entry, 'ldapmain') expect(person.name).to eq(name) end @@ -30,7 +30,7 @@ describe Gitlab::LDAP::Person do it 'returns the value of mail, if present' do mail = 'john@example.com' entry['mail'] = mail - person = Gitlab::LDAP::Person.new(entry, 'ldapmain') + person = described_class.new(entry, 'ldapmain') expect(person.email).to eq([mail]) end @@ -38,7 +38,7 @@ describe Gitlab::LDAP::Person do it 'returns the value of userPrincipalName, if mail and email are not present' do user_principal_name = 'john.doe@example.com' entry['userPrincipalName'] = user_principal_name - person = Gitlab::LDAP::Person.new(entry, 'ldapmain') + person = described_class.new(entry, 'ldapmain') expect(person.email).to eq([user_principal_name]) end diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index ab6e311b1e8..208a8d028cd 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::Metrics do expect(pool).to receive(:with).and_yield(connection) expect(connection).to receive(:write_points).with(an_instance_of(Array)) - expect(Gitlab::Metrics).to receive(:pool).and_return(pool) + expect(described_class).to receive(:pool).and_return(pool) described_class.submit_metrics([{ 'series' => 'kittens', 'tags' => {} }]) end @@ -64,7 +64,7 @@ describe Gitlab::Metrics do describe '.measure' do context 'without a transaction' do it 'returns the return value of the block' do - val = Gitlab::Metrics.measure(:foo) { 10 } + val = described_class.measure(:foo) { 10 } expect(val).to eq(10) end @@ -74,7 +74,7 @@ describe Gitlab::Metrics do let(:transaction) { Gitlab::Metrics::Transaction.new } before do - allow(Gitlab::Metrics).to receive(:current_transaction). + allow(described_class).to receive(:current_transaction). and_return(transaction) end @@ -88,11 +88,11 @@ describe Gitlab::Metrics do expect(transaction).to receive(:increment). with('foo_call_count', 1) - Gitlab::Metrics.measure(:foo) { 10 } + described_class.measure(:foo) { 10 } end it 'returns the return value of the block' do - val = Gitlab::Metrics.measure(:foo) { 10 } + val = described_class.measure(:foo) { 10 } expect(val).to eq(10) end @@ -105,7 +105,7 @@ describe Gitlab::Metrics do expect_any_instance_of(Gitlab::Metrics::Transaction). not_to receive(:add_tag) - Gitlab::Metrics.tag_transaction(:foo, 'bar') + described_class.tag_transaction(:foo, 'bar') end end @@ -113,13 +113,13 @@ describe Gitlab::Metrics do let(:transaction) { Gitlab::Metrics::Transaction.new } it 'adds the tag to the transaction' do - expect(Gitlab::Metrics).to receive(:current_transaction). + expect(described_class).to receive(:current_transaction). and_return(transaction) expect(transaction).to receive(:add_tag). with(:foo, 'bar') - Gitlab::Metrics.tag_transaction(:foo, 'bar') + described_class.tag_transaction(:foo, 'bar') end end end @@ -130,7 +130,7 @@ describe Gitlab::Metrics do expect_any_instance_of(Gitlab::Metrics::Transaction). not_to receive(:action=) - Gitlab::Metrics.action = 'foo' + described_class.action = 'foo' end end @@ -138,12 +138,12 @@ describe Gitlab::Metrics do it 'sets the action of a transaction' do trans = Gitlab::Metrics::Transaction.new - expect(Gitlab::Metrics).to receive(:current_transaction). + expect(described_class).to receive(:current_transaction). and_return(trans) expect(trans).to receive(:action=).with('foo') - Gitlab::Metrics.action = 'foo' + described_class.action = 'foo' end end end @@ -160,7 +160,7 @@ describe Gitlab::Metrics do expect_any_instance_of(Gitlab::Metrics::Transaction). not_to receive(:add_event) - Gitlab::Metrics.add_event(:meow) + described_class.add_event(:meow) end end @@ -170,10 +170,10 @@ describe Gitlab::Metrics do expect(transaction).to receive(:add_event).with(:meow) - expect(Gitlab::Metrics).to receive(:current_transaction). + expect(described_class).to receive(:current_transaction). and_return(transaction) - Gitlab::Metrics.add_event(:meow) + described_class.add_event(:meow) end end end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 127cd8c78d8..72e947f2cc2 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -45,8 +45,8 @@ describe Gitlab::Regex, lib: true do it { is_expected.not_to match('foo-') } end - describe 'FULL_NAMESPACE_REGEX_STR' do - subject { %r{\A#{Gitlab::Regex::FULL_NAMESPACE_REGEX_STR}\z} } + describe '.full_namespace_regex' do + subject { described_class.full_namespace_regex } it { is_expected.to match('gitlab.org') } it { is_expected.to match('gitlab.org/gitlab-git') } diff --git a/spec/lib/gitlab/request_profiler_spec.rb b/spec/lib/gitlab/request_profiler_spec.rb new file mode 100644 index 00000000000..ae9c06ebb7d --- /dev/null +++ b/spec/lib/gitlab/request_profiler_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Gitlab::RequestProfiler, lib: true do + describe '.profile_token' do + it 'returns a token' do + expect(described_class.profile_token).to be_present + end + + it 'caches the token' do + expect(Rails.cache).to receive(:fetch).with('profile-token') + + described_class.profile_token + end + end + + describe '.remove_all_profiles' do + it 'removes Gitlab::RequestProfiler::PROFILES_DIR directory' do + dir = described_class::PROFILES_DIR + FileUtils.mkdir_p(dir) + + expect(Dir.exist?(dir)).to be true + + described_class.remove_all_profiles + expect(Dir.exist?(dir)).to be false + end + end +end diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 6675d26734e..6675d26734e 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb diff --git a/spec/lib/gitlab/sidekiq_throttler_spec.rb b/spec/lib/gitlab/sidekiq_throttler_spec.rb index ff32e0e699d..6374ac80207 100644 --- a/spec/lib/gitlab/sidekiq_throttler_spec.rb +++ b/spec/lib/gitlab/sidekiq_throttler_spec.rb @@ -13,14 +13,14 @@ describe Gitlab::SidekiqThrottler do describe '#execute!' do it 'sets limits on the selected queues' do - Gitlab::SidekiqThrottler.execute! + described_class.execute! expect(Sidekiq::Queue['build'].limit).to eq 4 expect(Sidekiq::Queue['project_cache'].limit).to eq 4 end it 'does not set limits on other queues' do - Gitlab::SidekiqThrottler.execute! + described_class.execute! expect(Sidekiq::Queue['merge'].limit).to be_nil end diff --git a/spec/lib/gitlab/slash_commands/dsl_spec.rb b/spec/lib/gitlab/slash_commands/dsl_spec.rb index 26217a0e3b2..2763d950716 100644 --- a/spec/lib/gitlab/slash_commands/dsl_spec.rb +++ b/spec/lib/gitlab/slash_commands/dsl_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::SlashCommands::Dsl do before :all do DummyClass = Struct.new(:project) do - include Gitlab::SlashCommands::Dsl + include Gitlab::SlashCommands::Dsl # rubocop:disable RSpec/DescribedClass desc 'A command with no args' command :no_args, :none do diff --git a/spec/lib/gitlab/template/gitignore_template_spec.rb b/spec/lib/gitlab/template/gitignore_template_spec.rb index 9750a012e22..97797f42aaa 100644 --- a/spec/lib/gitlab/template/gitignore_template_spec.rb +++ b/spec/lib/gitlab/template/gitignore_template_spec.rb @@ -24,7 +24,7 @@ describe Gitlab::Template::GitignoreTemplate do it 'returns the Gitignore object of a valid file' do ruby = subject.find('Ruby') - expect(ruby).to be_a Gitlab::Template::GitignoreTemplate + expect(ruby).to be_a described_class expect(ruby.name).to eq('Ruby') end end diff --git a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb index e3b8321eda3..6541326d1de 100644 --- a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb +++ b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb @@ -25,7 +25,7 @@ describe Gitlab::Template::GitlabCiYmlTemplate do it 'returns the GitlabCiYml object of a valid file' do ruby = subject.find('Ruby') - expect(ruby).to be_a Gitlab::Template::GitlabCiYmlTemplate + expect(ruby).to be_a described_class expect(ruby.name).to eq('Ruby') end end diff --git a/spec/lib/gitlab/template/issue_template_spec.rb b/spec/lib/gitlab/template/issue_template_spec.rb index 9213ced7b19..329d1d74970 100644 --- a/spec/lib/gitlab/template/issue_template_spec.rb +++ b/spec/lib/gitlab/template/issue_template_spec.rb @@ -37,7 +37,7 @@ describe Gitlab::Template::IssueTemplate do it 'returns the issue object of a valid file' do ruby = subject.find('bug', project) - expect(ruby).to be_a Gitlab::Template::IssueTemplate + expect(ruby).to be_a described_class expect(ruby.name).to eq('bug') end end diff --git a/spec/lib/gitlab/template/merge_request_template_spec.rb b/spec/lib/gitlab/template/merge_request_template_spec.rb index 77dd3079e22..2b0056d9bab 100644 --- a/spec/lib/gitlab/template/merge_request_template_spec.rb +++ b/spec/lib/gitlab/template/merge_request_template_spec.rb @@ -37,7 +37,7 @@ describe Gitlab::Template::MergeRequestTemplate do it 'returns the merge request object of a valid file' do ruby = subject.find('bug', project) - expect(ruby).to be_a Gitlab::Template::MergeRequestTemplate + expect(ruby).to be_a described_class expect(ruby.name).to eq('bug') end end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 7f21288cf88..bf1dfe7f412 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::UsageData do let!(:board) { create(:board, project: project) } describe '#data' do - subject { Gitlab::UsageData.data } + subject { described_class.data } it "gathers usage data" do expect(subject.keys).to match_array(%i( @@ -58,7 +58,7 @@ describe Gitlab::UsageData do end describe '#license_usage_data' do - subject { Gitlab::UsageData.license_usage_data } + subject { described_class.license_usage_data } it "gathers license data" do expect(subject[:uuid]).to eq(current_application_settings.uuid) diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 611cdbbc865..2b27ff66c09 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -87,10 +87,10 @@ describe Gitlab::UserAccess, lib: true do expect(access.can_push_to_branch?(branch.name)).to be_falsey end - it 'returns true if branch does not exist and user has permission to merge' do + it 'returns false if branch does not exist' do project.team << [user, :developer] - expect(access.can_push_to_branch?(not_existing_branch.name)).to be_truthy + expect(access.can_push_to_branch?(not_existing_branch.name)).to be_falsey end end diff --git a/spec/lib/light_url_builder_spec.rb b/spec/lib/light_url_builder_spec.rb deleted file mode 100644 index 3fe8cf43934..00000000000 --- a/spec/lib/light_url_builder_spec.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'spec_helper' - -describe Gitlab::UrlBuilder, lib: true do - describe '.build' do - context 'when passing a Commit' do - it 'returns a proper URL' do - commit = build_stubbed(:commit) - - url = described_class.build(commit) - - expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" - end - end - - context 'when passing an Issue' do - it 'returns a proper URL' do - issue = build_stubbed(:issue, iid: 42) - - url = described_class.build(issue) - - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" - end - end - - context 'when passing a MergeRequest' do - it 'returns a proper URL' do - merge_request = build_stubbed(:merge_request, iid: 42) - - url = described_class.build(merge_request) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" - end - end - - context 'when passing a Note' do - context 'on a Commit' do - it 'returns a proper URL' do - note = build_stubbed(:note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on a Commit Diff' do - it 'returns a proper URL' do - note = build_stubbed(:diff_note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on an Issue' do - it 'returns a proper URL' do - issue = create(:issue, iid: 42) - note = build_stubbed(:note_on_issue, noteable: issue) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" - end - end - - context 'on a MergeRequest' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:note_on_merge_request, noteable: merge_request) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end - - context 'on a MergeRequest Diff' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end - - context 'on a ProjectSnippet' do - it 'returns a proper URL' do - project_snippet = create(:project_snippet) - note = build_stubbed(:note_on_project_snippet, noteable: project_snippet) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" - end - end - - context 'on another object' do - it 'returns a proper URL' do - project = build_stubbed(:empty_project) - - expect { described_class.build(project) }. - to raise_error(NotImplementedError, 'No URL builder defined for Project') - end - end - end - - context 'when passing a WikiPage' do - it 'returns a proper URL' do - wiki_page = build(:wiki_page) - url = described_class.build(wiki_page) - - expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" - end - end - end -end diff --git a/spec/mailers/emails/merge_requests_spec.rb b/spec/mailers/emails/merge_requests_spec.rb index e22858d1d8f..2ad572bb5c7 100644 --- a/spec/mailers/emails/merge_requests_spec.rb +++ b/spec/mailers/emails/merge_requests_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'email_spec' -describe Notify, "merge request notifications" do +describe Emails::MergeRequests do include EmailSpec::Matchers describe "#resolved_all_discussions_email" do diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb index 5ca936f28f0..8c1c9bf135f 100644 --- a/spec/mailers/emails/profile_spec.rb +++ b/spec/mailers/emails/profile_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'email_spec' -describe Notify do +describe Emails::Profile do include EmailSpec::Matchers include_context 'gitlab email notification' @@ -15,106 +15,104 @@ describe Notify do end end - describe 'profile notifications' do - describe 'for new users, the email' do - let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } - let(:token) { 'kETLwRaayvigPq_x3SNM' } + describe 'for new users, the email' do + let(:example_site_path) { root_path } + let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } + let(:token) { 'kETLwRaayvigPq_x3SNM' } - subject { Notify.new_user_email(new_user.id, token) } + subject { Notify.new_user_email(new_user.id, token) } - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'a new user email' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'a new user email' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'contains the password text' do - is_expected.to have_body_text /Click here to set your password/ - end + it 'contains the password text' do + is_expected.to have_body_text /Click here to set your password/ + end - it 'includes a link for user to set password' do - params = "reset_password_token=#{token}" - is_expected.to have_body_text( - %r{http://#{Gitlab.config.gitlab.host}(:\d+)?/users/password/edit\?#{params}} - ) - end + it 'includes a link for user to set password' do + params = "reset_password_token=#{token}" + is_expected.to have_body_text( + %r{http://#{Gitlab.config.gitlab.host}(:\d+)?/users/password/edit\?#{params}} + ) + end - it 'explains the reset link expiration' do - is_expected.to have_body_text(/This link is valid for \d+ (hours?|days?)/) - is_expected.to have_body_text(new_user_password_url) - is_expected.to have_body_text(/\?user_email=.*%40.*/) - end + it 'explains the reset link expiration' do + is_expected.to have_body_text(/This link is valid for \d+ (hours?|days?)/) + is_expected.to have_body_text(new_user_password_url) + is_expected.to have_body_text(/\?user_email=.*%40.*/) end + end - describe 'for users that signed up, the email' do - let(:example_site_path) { root_path } - let(:new_user) { create(:user, email: new_user_address, password: "securePassword") } + describe 'for users that signed up, the email' do + let(:example_site_path) { root_path } + let(:new_user) { create(:user, email: new_user_address, password: "securePassword") } - subject { Notify.new_user_email(new_user.id) } + subject { Notify.new_user_email(new_user.id) } - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'a new user email' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'a new user email' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'does not contain the new user\'s password' do - is_expected.not_to have_body_text /password/ - end + it 'does not contain the new user\'s password' do + is_expected.not_to have_body_text /password/ end + end - describe 'user added ssh key' do - let(:key) { create(:personal_key) } + describe 'user added ssh key' do + let(:key) { create(:personal_key) } - subject { Notify.new_ssh_key_email(key.id) } + subject { Notify.new_ssh_key_email(key.id) } - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'is sent to the new user' do - is_expected.to deliver_to key.user.email - end + it 'is sent to the new user' do + is_expected.to deliver_to key.user.email + end - it 'has the correct subject' do - is_expected.to have_subject /^SSH key was added to your account$/i - end + it 'has the correct subject' do + is_expected.to have_subject /^SSH key was added to your account$/i + end - it 'contains the new ssh key title' do - is_expected.to have_body_text /#{key.title}/ - end + it 'contains the new ssh key title' do + is_expected.to have_body_text /#{key.title}/ + end - it 'includes a link to ssh keys page' do - is_expected.to have_body_text /#{profile_keys_path}/ - end + it 'includes a link to ssh keys page' do + is_expected.to have_body_text /#{profile_keys_path}/ + end - context 'with SSH key that does not exist' do - it { expect { Notify.new_ssh_key_email('foo') }.not_to raise_error } - end + context 'with SSH key that does not exist' do + it { expect { Notify.new_ssh_key_email('foo') }.not_to raise_error } end + end - describe 'user added email' do - let(:email) { create(:email) } + describe 'user added email' do + let(:email) { create(:email) } - subject { Notify.new_email_email(email.id) } + subject { Notify.new_email_email(email.id) } - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like 'a user cannot unsubscribe through footer link' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' - it 'is sent to the new user' do - is_expected.to deliver_to email.user.email - end + it 'is sent to the new user' do + is_expected.to deliver_to email.user.email + end - it 'has the correct subject' do - is_expected.to have_subject /^Email was added to your account$/i - end + it 'has the correct subject' do + is_expected.to have_subject /^Email was added to your account$/i + end - it 'contains the new email address' do - is_expected.to have_body_text /#{email.email}/ - end + it 'contains the new email address' do + is_expected.to have_body_text /#{email.email}/ + end - it 'includes a link to emails page' do - is_expected.to have_body_text /#{profile_emails_path}/ - end + it 'includes a link to emails page' do + is_expected.to have_body_text /#{profile_emails_path}/ end end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index e6f0a3b5920..9f12e40d808 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -40,7 +40,7 @@ describe Notify do let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: 'My awesome description') } describe 'that are new' do - subject { Notify.new_issue_email(issue.assignee_id, issue.id) } + subject { described_class.new_issue_email(issue.assignee_id, issue.id) } it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread with reply-by-email enabled' do @@ -69,7 +69,7 @@ describe Notify do end describe 'that are new with a description' do - subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) } + subject { described_class.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) } it_behaves_like 'it should show Gmail Actions View Issue link' @@ -79,7 +79,7 @@ describe Notify do end describe 'that have been reassigned' do - subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) } + subject { described_class.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) } it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -105,7 +105,7 @@ describe Notify do end describe 'that have been relabeled' do - subject { Notify.relabeled_issue_email(recipient.id, issue.id, %w[foo bar baz], current_user.id) } + subject { described_class.relabeled_issue_email(recipient.id, issue.id, %w[foo bar baz], current_user.id) } it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -132,7 +132,7 @@ describe Notify do describe 'status changed' do let(:status) { 'closed' } - subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) } + subject { described_class.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) } it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do let(:model) { issue } @@ -158,7 +158,7 @@ describe Notify do describe 'moved to another project' do let(:new_issue) { create(:issue) } - subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) } + subject { described_class.issue_moved_email(recipient, issue, new_issue, current_user) } it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do let(:model) { issue } @@ -190,7 +190,7 @@ describe Notify do let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: 'My awesome description') } describe 'that are new' do - subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } + subject { described_class.new_merge_request_email(merge_request.assignee_id, merge_request.id) } it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread with reply-by-email enabled' do @@ -221,7 +221,7 @@ describe Notify do end describe 'that are new with a description' do - subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } + subject { described_class.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } it_behaves_like 'it should show Gmail Actions View Merge request link' it_behaves_like "an unsubscribeable thread" @@ -232,7 +232,7 @@ describe Notify do end describe 'that are reassigned' do - subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) } + subject { described_class.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) } it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -258,7 +258,7 @@ describe Notify do end describe 'that have been relabeled' do - subject { Notify.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) } + subject { described_class.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) } it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -283,7 +283,7 @@ describe Notify do describe 'status changed' do let(:status) { 'reopened' } - subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) } + subject { described_class.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) } it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do let(:model) { merge_request } @@ -308,7 +308,7 @@ describe Notify do end describe 'that are merged' do - subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) } + subject { described_class.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) } it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -337,7 +337,7 @@ describe Notify do describe 'project was moved' do let(:project) { create(:empty_project) } let(:user) { create(:user) } - subject { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } + subject { described_class.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -363,7 +363,7 @@ describe Notify do project.request_access(user) project.requesters.find_by(user_id: user.id) end - subject { Notify.member_access_requested_email('project', project_member.id) } + subject { described_class.member_access_requested_email('project', project_member.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -390,7 +390,7 @@ describe Notify do project.request_access(user) project.requesters.find_by(user_id: user.id) end - subject { Notify.member_access_requested_email('project', project_member.id) } + subject { described_class.member_access_requested_email('project', project_member.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -416,7 +416,7 @@ describe Notify do project.request_access(user) project.requesters.find_by(user_id: user.id) end - subject { Notify.member_access_denied_email('project', project.id, user.id) } + subject { described_class.member_access_denied_email('project', project.id, user.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -434,7 +434,7 @@ describe Notify do let(:project) { create(:empty_project, :public, :access_requestable, namespace: owner.namespace) } let(:user) { create(:user) } let(:project_member) { create(:project_member, project: project, user: user) } - subject { Notify.member_access_granted_email('project', project_member.id) } + subject { described_class.member_access_granted_email('project', project_member.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -465,7 +465,7 @@ describe Notify do let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:project_member) { invite_to_project(project, inviter: master) } - subject { Notify.member_invited_email('project', project_member.id, project_member.invite_token) } + subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -490,7 +490,7 @@ describe Notify do invitee end - subject { Notify.member_invite_accepted_email('project', project_member.id) } + subject { described_class.member_invite_accepted_email('project', project_member.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -514,7 +514,7 @@ describe Notify do invitee end - subject { Notify.member_invite_declined_email('project', project.id, project_member.invite_email, master.id) } + subject { described_class.member_invite_declined_email('project', project.id, project_member.invite_email, master.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -574,7 +574,7 @@ describe Notify do before(:each) { allow(note).to receive(:noteable).and_return(commit) } - subject { Notify.note_commit_email(recipient.id, note.id) } + subject { described_class.note_commit_email(recipient.id, note.id) } it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -596,7 +596,7 @@ describe Notify do let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") } before(:each) { allow(note).to receive(:noteable).and_return(merge_request) } - subject { Notify.note_merge_request_email(recipient.id, note.id) } + subject { described_class.note_merge_request_email(recipient.id, note.id) } it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -618,7 +618,7 @@ describe Notify do let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") } before(:each) { allow(note).to receive(:noteable).and_return(issue) } - subject { Notify.note_issue_email(recipient.id, note.id) } + subject { described_class.note_issue_email(recipient.id, note.id) } it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -680,7 +680,7 @@ describe Notify do before(:each) { allow(note).to receive(:noteable).and_return(commit) } - subject { Notify.note_commit_email(recipient.id, note.id) } + subject { described_class.note_commit_email(recipient.id, note.id) } it_behaves_like 'a discussion note email', :discussion_note_on_commit it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -704,7 +704,7 @@ describe Notify do let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") } before(:each) { allow(note).to receive(:noteable).and_return(merge_request) } - subject { Notify.note_merge_request_email(recipient.id, note.id) } + subject { described_class.note_merge_request_email(recipient.id, note.id) } it_behaves_like 'a discussion note email', :discussion_note_on_merge_request it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -728,7 +728,7 @@ describe Notify do let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") } before(:each) { allow(note).to receive(:noteable).and_return(issue) } - subject { Notify.note_issue_email(recipient.id, note.id) } + subject { described_class.note_issue_email(recipient.id, note.id) } it_behaves_like 'a discussion note email', :discussion_note_on_issue it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do @@ -798,7 +798,7 @@ describe Notify do let(:commit) { project.commit } let(:note) { create(:diff_note_on_commit) } - subject { Notify.note_commit_email(recipient.id, note.id) } + subject { described_class.note_commit_email(recipient.id, note.id) } it_behaves_like 'an email for a note on a diff discussion', :diff_note_on_commit it_behaves_like 'it should show Gmail Actions View Commit link' @@ -809,7 +809,7 @@ describe Notify do let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:note) { create(:diff_note_on_merge_request) } - subject { Notify.note_merge_request_email(recipient.id, note.id) } + subject { described_class.note_merge_request_email(recipient.id, note.id) } it_behaves_like 'an email for a note on a diff discussion', :diff_note_on_merge_request it_behaves_like 'it should show Gmail Actions View Merge request link' @@ -826,7 +826,7 @@ describe Notify do group.request_access(user) group.requesters.find_by(user_id: user.id) end - subject { Notify.member_access_requested_email('group', group_member.id) } + subject { described_class.member_access_requested_email('group', group_member.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -847,7 +847,7 @@ describe Notify do group.request_access(user) group.requesters.find_by(user_id: user.id) end - subject { Notify.member_access_denied_email('group', group.id, user.id) } + subject { described_class.member_access_denied_email('group', group.id, user.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -865,7 +865,7 @@ describe Notify do let(:user) { create(:user) } let(:group_member) { create(:group_member, group: group, user: user) } - subject { Notify.member_access_granted_email('group', group_member.id) } + subject { described_class.member_access_granted_email('group', group_member.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -896,7 +896,7 @@ describe Notify do let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } } let(:group_member) { invite_to_group(group, inviter: owner) } - subject { Notify.member_invited_email('group', group_member.id, group_member.invite_token) } + subject { described_class.member_invited_email('group', group_member.id, group_member.invite_token) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -921,7 +921,7 @@ describe Notify do invitee end - subject { Notify.member_invite_accepted_email('group', group_member.id) } + subject { described_class.member_invite_accepted_email('group', group_member.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -945,7 +945,7 @@ describe Notify do invitee end - subject { Notify.member_invite_declined_email('group', group.id, group_member.invite_email, owner.id) } + subject { described_class.member_invite_declined_email('group', group.id, group_member.invite_email, owner.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -994,7 +994,7 @@ describe Notify do let(:user) { create(:user) } let(:tree_path) { namespace_project_tree_path(project.namespace, project, "empty-branch") } - subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/empty-branch', action: :create) } + subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/empty-branch', action: :create) } it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'a user cannot unsubscribe through footer link' @@ -1020,7 +1020,7 @@ describe Notify do let(:user) { create(:user) } let(:tree_path) { namespace_project_tree_path(project.namespace, project, "v1.0") } - subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :create) } + subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :create) } it_behaves_like 'it should not have Gmail Actions links' it_behaves_like "a user cannot unsubscribe through footer link" @@ -1045,7 +1045,7 @@ describe Notify do let(:example_site_path) { root_path } let(:user) { create(:user) } - subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :delete) } + subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :delete) } it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'a user cannot unsubscribe through footer link' @@ -1067,7 +1067,7 @@ describe Notify do let(:example_site_path) { root_path } let(:user) { create(:user) } - subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) } + subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) } it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'a user cannot unsubscribe through footer link' @@ -1096,7 +1096,7 @@ describe Notify do let(:send_from_committer_email) { false } let(:diff_refs) { Gitlab::Diff::DiffRefs.new(base_sha: project.merge_base_commit(sample_image_commit.id, sample_commit.id).id, head_sha: sample_commit.id) } - subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, diff_refs: diff_refs, send_from_committer_email: send_from_committer_email) } + subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, diff_refs: diff_refs, send_from_committer_email: send_from_committer_email) } it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'a user cannot unsubscribe through footer link' @@ -1189,7 +1189,7 @@ describe Notify do let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) } let(:diff_refs) { Gitlab::Diff::DiffRefs.new(base_sha: project.merge_base_commit(sample_image_commit.id, sample_commit.id).id, head_sha: sample_commit.id) } - subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, diff_refs: diff_refs) } + subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, diff_refs: diff_refs) } it_behaves_like 'it should show Gmail Actions View Commit link' it_behaves_like 'a user cannot unsubscribe through footer link' @@ -1215,7 +1215,7 @@ describe Notify do describe 'HTML emails setting' do let(:project) { create(:empty_project) } let(:user) { create(:user) } - let(:multipart_mail) { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } + let(:multipart_mail) { described_class.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } context 'when disabled' do it 'only sends the text template' do diff --git a/spec/migrations/schema_spec.rb b/spec/migrations/active_record/schema_spec.rb index e132529d8d8..e132529d8d8 100644 --- a/spec/migrations/schema_spec.rb +++ b/spec/migrations/active_record/schema_spec.rb diff --git a/spec/models/ci/trigger_schedule_spec.rb b/spec/models/ci/trigger_schedule_spec.rb index 75d21541cee..92447564d7c 100644 --- a/spec/models/ci/trigger_schedule_spec.rb +++ b/spec/models/ci/trigger_schedule_spec.rb @@ -73,4 +73,36 @@ describe Ci::TriggerSchedule, models: true do end end end + + describe '#real_next_run' do + subject do + Ci::TriggerSchedule.last.real_next_run(worker_cron: worker_cron, + worker_time_zone: worker_time_zone) + end + + context 'when GitLab time_zone is UTC' do + before do + allow(Time).to receive(:zone) + .and_return(ActiveSupport::TimeZone[worker_time_zone]) + end + + let(:worker_time_zone) { 'UTC' } + + context 'when cron_timezone is Eastern Time (US & Canada)' do + before do + create(:ci_trigger_schedule, :nightly, + cron_timezone: 'Eastern Time (US & Canada)') + end + + let(:worker_cron) { '0 1 2 3 *' } + + it 'returns the next time worker executes' do + expect(subject.min).to eq(0) + expect(subject.hour).to eq(1) + expect(subject.day).to eq(2) + expect(subject.month).to eq(3) + end + end + end + end end diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb index de791abdf3d..63ad3a3630b 100644 --- a/spec/models/concerns/awardable_spec.rb +++ b/spec/models/concerns/awardable_spec.rb @@ -1,10 +1,12 @@ require 'spec_helper' -describe Issue, "Awardable" do +describe Awardable do let!(:issue) { create(:issue) } let!(:award_emoji) { create(:award_emoji, :downvote, awardable: issue) } describe "Associations" do + subject { build(:issue) } + it { is_expected.to have_many(:award_emoji).dependent(:destroy) } end diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index 4edafbc4e32..40bbb10eaac 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -170,6 +170,12 @@ describe CacheMarkdownField do is_expected.to be_truthy end + + it 'returns false if the markdown field is set but the html is not' do + thing.foo_html = nil + + is_expected.to be_falsy + end end describe '#refresh_markdown_cache!' do diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb index 0002a00770f..8571e85627c 100644 --- a/spec/models/concerns/discussion_on_diff_spec.rb +++ b/spec/models/concerns/discussion_on_diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DiffDiscussion, DiscussionOnDiff, model: true do +describe DiscussionOnDiff, model: true do subject { create(:diff_note_on_merge_request).to_discussion } describe "#truncated_diff_lines" do @@ -8,9 +8,9 @@ describe DiffDiscussion, DiscussionOnDiff, model: true do context "when diff is greater than allowed number of truncated diff lines " do it "returns fewer lines" do - expect(subject.diff_lines.count).to be > described_class::NUMBER_OF_TRUNCATED_DIFF_LINES + expect(subject.diff_lines.count).to be > DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES - expect(truncated_lines.count).to be <= described_class::NUMBER_OF_TRUNCATED_DIFF_LINES + expect(truncated_lines.count).to be <= DiffDiscussion::NUMBER_OF_TRUNCATED_DIFF_LINES end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 4522206fab1..3ecba2e9687 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -1,10 +1,13 @@ require 'spec_helper' -describe Issue, "Issuable" do +describe Issuable do + let(:issuable_class) { Issue } let(:issue) { create(:issue) } let(:user) { create(:user) } describe "Associations" do + subject { build(:issue) } + it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:author) } it { is_expected.to belong_to(:assignee) } @@ -23,10 +26,14 @@ describe Issue, "Issuable" do end describe 'Included modules' do + let(:described_class) { issuable_class } + it { is_expected.to include_module(Awardable) } end describe "Validation" do + subject { build(:issue) } + before do allow(subject).to receive(:set_iid).and_return(false) end @@ -39,9 +46,11 @@ describe Issue, "Issuable" do end describe "Scope" do - it { expect(described_class).to respond_to(:opened) } - it { expect(described_class).to respond_to(:closed) } - it { expect(described_class).to respond_to(:assigned) } + subject { build(:issue) } + + it { expect(issuable_class).to respond_to(:opened) } + it { expect(issuable_class).to respond_to(:closed) } + it { expect(issuable_class).to respond_to(:assigned) } end describe 'author_name' do @@ -115,16 +124,16 @@ describe Issue, "Issuable" do let!(:searchable_issue) { create(:issue, title: "Searchable issue") } it 'returns notes with a matching title' do - expect(described_class.search(searchable_issue.title)). + expect(issuable_class.search(searchable_issue.title)). to eq([searchable_issue]) end it 'returns notes with a partially matching title' do - expect(described_class.search('able')).to eq([searchable_issue]) + expect(issuable_class.search('able')).to eq([searchable_issue]) end it 'returns notes with a matching title regardless of the casing' do - expect(described_class.search(searchable_issue.title.upcase)). + expect(issuable_class.search(searchable_issue.title.upcase)). to eq([searchable_issue]) end end @@ -135,31 +144,31 @@ describe Issue, "Issuable" do end it 'returns notes with a matching title' do - expect(described_class.full_search(searchable_issue.title)). + expect(issuable_class.full_search(searchable_issue.title)). to eq([searchable_issue]) end it 'returns notes with a partially matching title' do - expect(described_class.full_search('able')).to eq([searchable_issue]) + expect(issuable_class.full_search('able')).to eq([searchable_issue]) end it 'returns notes with a matching title regardless of the casing' do - expect(described_class.full_search(searchable_issue.title.upcase)). + expect(issuable_class.full_search(searchable_issue.title.upcase)). to eq([searchable_issue]) end it 'returns notes with a matching description' do - expect(described_class.full_search(searchable_issue.description)). + expect(issuable_class.full_search(searchable_issue.description)). to eq([searchable_issue]) end it 'returns notes with a partially matching description' do - expect(described_class.full_search(searchable_issue.description)). + expect(issuable_class.full_search(searchable_issue.description)). to eq([searchable_issue]) end it 'returns notes with a matching description regardless of the casing' do - expect(described_class.full_search(searchable_issue.description.upcase)). + expect(issuable_class.full_search(searchable_issue.description.upcase)). to eq([searchable_issue]) end end diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb index 92cc8859a8c..bdae742ff1d 100644 --- a/spec/models/concerns/noteable_spec.rb +++ b/spec/models/concerns/noteable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequest, Noteable, model: true do +describe Noteable, model: true do let!(:active_diff_note1) { create(:diff_note_on_merge_request) } let(:project) { active_diff_note1.project } subject { active_diff_note1.noteable } diff --git a/spec/models/concerns/relative_positioning_spec.rb b/spec/models/concerns/relative_positioning_spec.rb index 255b584a85e..494e6f1b6f6 100644 --- a/spec/models/concerns/relative_positioning_spec.rb +++ b/spec/models/concerns/relative_positioning_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issue, 'RelativePositioning' do +describe RelativePositioning do let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } let(:issue1) { create(:issue, project: project) } diff --git a/spec/models/concerns/spammable_spec.rb b/spec/models/concerns/spammable_spec.rb index fd3b8307571..e698207166c 100644 --- a/spec/models/concerns/spammable_spec.rb +++ b/spec/models/concerns/spammable_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' -describe Issue, 'Spammable' do +describe Spammable do let(:issue) { create(:issue, description: 'Test Desc.') } describe 'Associations' do + subject { build(:issue) } + it { is_expected.to have_one(:user_agent_detail).dependent(:destroy) } end diff --git a/spec/models/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb index c3af7a0960f..8c945686b66 100644 --- a/spec/models/concerns/strip_attribute_spec.rb +++ b/spec/models/concerns/strip_attribute_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestone, "StripAttribute" do +describe StripAttribute do let(:milestone) { create(:milestone) } describe ".strip_attributes" do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 8ffde6f7fbb..a11805926cc 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -57,6 +57,32 @@ describe Group, models: true do it { is_expected.not_to validate_presence_of :owner } it { is_expected.to validate_presence_of :two_factor_grace_period } it { is_expected.to validate_numericality_of(:two_factor_grace_period).is_greater_than_or_equal_to(0) } + + describe 'path validation' do + it 'rejects paths reserved on the root namespace when the group has no parent' do + group = build(:group, path: 'api') + + expect(group).not_to be_valid + end + + it 'allows root paths when the group has a parent' do + group = build(:group, path: 'api', parent: create(:group)) + + expect(group).to be_valid + end + + it 'rejects any wildcard paths when not a top level group' do + group = build(:group, path: 'tree', parent: create(:group)) + + expect(group).not_to be_valid + end + + it 'rejects reserved group paths' do + group = build(:group, path: 'activity', parent: create(:group)) + + expect(group).not_to be_valid + end + end end describe '.visible_to_user' do diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index b0f3657d3b5..ccc3deac199 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -390,13 +390,15 @@ describe Member, models: true do %w[project group].each do |source_type| context "when source is a #{source_type}" do let!(:source) { create(source_type, :public, :access_requestable) } - let!(:user) { create(:user) } let!(:admin) { create(:admin) } + let(:user1) { create(:user) } + let(:user2) { create(:user) } it 'returns a <Source>Member objects' do - members = described_class.add_users(source, [user], :master) + members = described_class.add_users(source, [user1, user2], :master) expect(members).to be_a Array + expect(members.size).to eq(2) expect(members.first).to be_a "#{source_type.classify}Member".constantize expect(members.first).to be_persisted end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index e406d0a16bd..8624616316c 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -34,6 +34,13 @@ describe Namespace, models: true do let(:group) { build(:group, :nested, path: 'tree') } it { expect(group).not_to be_valid } + + it 'rejects nested paths' do + parent = create(:group, :nested, path: 'environments') + namespace = build(:project, path: 'folders', namespace: parent) + + expect(namespace).not_to be_valid + end end context 'top-level group' do @@ -47,6 +54,7 @@ describe Namespace, models: true do describe "Respond to" do it { is_expected.to respond_to(:human_name) } it { is_expected.to respond_to(:to_param) } + it { is_expected.to respond_to(:has_parent?) } end describe '#to_param' do diff --git a/spec/models/project_services/pipeline_email_service_spec.rb b/spec/models/project_services/pipelines_email_service_spec.rb index 03932895b0e..03932895b0e 100644 --- a/spec/models/project_services/pipeline_email_service_spec.rb +++ b/spec/models/project_services/pipelines_email_service_spec.rb diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 92d420337f9..36ce3070a6e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -253,6 +253,34 @@ describe Project, models: true do expect(new_project.errors.full_messages.first).to eq('The project is still being deleted. Please try again later.') end end + + describe 'path validation' do + it 'allows paths reserved on the root namespace' do + project = build(:project, path: 'api') + + expect(project).to be_valid + end + + it 'rejects paths reserved on another level' do + project = build(:project, path: 'tree') + + expect(project).not_to be_valid + end + + it 'rejects nested paths' do + parent = create(:group, :nested, path: 'environments') + project = build(:project, path: 'folders', namespace: parent) + + expect(project).not_to be_valid + end + + it 'allows a reserved group name' do + parent = create(:group) + project = build(:project, path: 'avatar', namespace: parent) + + expect(project).to be_valid + end + end end describe 'default_scope' do @@ -781,17 +809,14 @@ describe Project, models: true do let(:project) { create(:empty_project) } - context 'When avatar file is uploaded' do - before do - project.update_columns(avatar: 'uploads/avatar.png') - allow(project.avatar).to receive(:present?) { true } - end + context 'when avatar file is uploaded' do + let(:project) { create(:empty_project, :with_avatar) } - let(:avatar_path) do - "/uploads/project/avatar/#{project.id}/uploads/avatar.png" - end + it 'creates a correct avatar path' do + avatar_path = "/uploads/project/avatar/#{project.id}/dk.png" - it { should eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" } + expect(project.avatar_url).to eq("http://#{Gitlab.config.gitlab.host}#{avatar_path}") + end end context 'When avatar file in git' do @@ -1881,4 +1906,23 @@ describe Project, models: true do expect(project.pipeline_status).to be_loaded end end + + describe '#append_or_update_attribute' do + let(:project) { create(:project) } + + it 'shows full error updating an invalid MR' do + error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\ + ' Validate fork Source project is not a fork of the target project' + + expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }. + to raise_error(ActiveRecord::RecordNotSaved, error_message) + end + + it 'updates the project succesfully' do + merge_request = create(:merge_request, target_project: project, source_project: project) + + expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }. + not_to raise_error + end + end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 98d0641443e..5216764a82d 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1379,12 +1379,22 @@ describe Repository, models: true do describe '#branch_count' do it 'returns the number of branches' do expect(repository.branch_count).to be_an(Integer) + + # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync + rugged_count = repository.raw_repository.rugged.branches.count + + expect(repository.branch_count).to eq(rugged_count) end end describe '#tag_count' do it 'returns the number of tags' do expect(repository.tag_count).to be_an(Integer) + + # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync + rugged_count = repository.raw_repository.rugged.tags.count + + expect(repository.tag_count).to eq(rugged_count) end end @@ -1849,17 +1859,15 @@ describe Repository, models: true do end end - # TODO: Uncomment when feature is reenabled - # describe '#is_ancestor?' do - # context 'Gitaly is_ancestor feature enabled' do - # it 'asks Gitaly server if it\'s an ancestor' do - # commit = repository.commit - # allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(true) - # expect(Gitlab::GitalyClient::Commit).to receive(:is_ancestor). - # with(repository.raw_repository, commit.id, commit.id).and_return(true) - # - # expect(repository.is_ancestor?(commit.id, commit.id)).to be true - # end - # end - # end + describe '#is_ancestor?' do + context 'Gitaly is_ancestor feature enabled' do + it "asks Gitaly server if it's an ancestor" do + commit = repository.commit + expect(repository.raw_repository).to receive(:is_ancestor?).and_call_original + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(true) + + expect(repository.is_ancestor?(commit.id, commit.id)).to be true + end + end + end end diff --git a/spec/models/snippet_blob_spec.rb b/spec/models/snippet_blob_spec.rb new file mode 100644 index 00000000000..120b390586b --- /dev/null +++ b/spec/models/snippet_blob_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe SnippetBlob, models: true do + let(:snippet) { create(:snippet) } + + subject { described_class.new(snippet) } + + describe '#id' do + it 'returns the snippet ID' do + expect(subject.id).to eq(snippet.id) + end + end + + describe '#name' do + it 'returns the snippet file name' do + expect(subject.name).to eq(snippet.file_name) + end + end + + describe '#size' do + it 'returns the data size' do + expect(subject.size).to eq(subject.data.bytesize) + end + end + + describe '#data' do + it 'returns the snippet content' do + expect(subject.data).to eq(snippet.content) + end + end + + describe '#rendered_markup' do + context 'when the content is GFM' do + let(:snippet) { create(:snippet, file_name: 'file.md') } + + it 'returns the rendered GFM' do + expect(subject.rendered_markup).to eq(snippet.content_html) + end + end + + context 'when the content is not GFM' do + it 'returns nil' do + expect(subject.rendered_markup).to be_nil + end + end + end +end diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 8095d01b69e..75b1fc7e216 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -5,7 +5,6 @@ describe Snippet, models: true do subject { described_class } it { is_expected.to include_module(Gitlab::VisibilityLevel) } - it { is_expected.to include_module(Linguist::BlobHelper) } it { is_expected.to include_module(Participable) } it { is_expected.to include_module(Referable) } it { is_expected.to include_module(Sortable) } @@ -241,4 +240,16 @@ describe Snippet, models: true do end end end + + describe '#blob' do + let(:snippet) { create(:snippet) } + + it 'returns a blob representing the snippet data' do + blob = snippet.blob + + expect(blob).to be_a(Blob) + expect(blob.path).to eq(snippet.file_name) + expect(blob.data).to eq(snippet.content) + end + end end diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index 581305ad39f..3f80e1ac534 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -125,4 +125,50 @@ describe Todo, models: true do expect(subject.target_reference).to eq issue.to_reference(full: true) end end + + describe '#self_added?' do + let(:user_1) { build(:user) } + + before do + subject.user = user_1 + end + + it 'is true when the user is the author' do + subject.author = user_1 + + expect(subject).to be_self_added + end + + it 'is false when the user is not the author' do + subject.author = build(:user) + + expect(subject).not_to be_self_added + end + end + + describe '#self_assigned?' do + let(:user_1) { build(:user) } + + before do + subject.user = user_1 + subject.author = user_1 + subject.action = Todo::ASSIGNED + end + + it 'is true when todo is ASSIGNED and self_added' do + expect(subject).to be_self_assigned + end + + it 'is false when the todo is not ASSIGNED' do + subject.action = Todo::MENTIONED + + expect(subject).not_to be_self_assigned + end + + it 'is false when todo is not self_added' do + subject.author = build(:user) + + expect(subject).not_to be_self_assigned + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0bcebc27598..1c2df4c9d97 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -97,6 +97,18 @@ describe User, models: true do expect(user.errors.values).to eq [['dashboard is a reserved name']] end + it 'allows child names' do + user = build(:user, username: 'avatar') + + expect(user).to be_valid + end + + it 'allows wildcard names' do + user = build(:user, username: 'blob') + + expect(user).to be_valid + end + it 'validates uniqueness' do expect(subject).to validate_uniqueness_of(:username).case_insensitive end diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb index 2905d5b26a5..9a870b7fda1 100644 --- a/spec/policies/issue_policy_spec.rb +++ b/spec/policies/issue_policy_spec.rb @@ -1,118 +1,192 @@ require 'spec_helper' describe IssuePolicy, models: true do - let(:user) { create(:user) } - - describe '#rules' do - context 'using a regular issue' do - let(:project) { create(:empty_project, :public) } - let(:issue) { create(:issue, project: project) } - let(:policies) { described_class.abilities(user, issue).to_set } - - context 'with a regular user' do - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end - - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end - - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end - end + let(:guest) { create(:user) } + let(:author) { create(:user) } + let(:assignee) { create(:user) } + let(:reporter) { create(:user) } + let(:group) { create(:group, :public) } + let(:reporter_from_group_link) { create(:user) } + + def permissions(user, issue) + described_class.abilities(user, issue).to_set + end + + context 'a private project' do + let(:non_member) { create(:user) } + let(:project) { create(:empty_project, :private) } + let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } + let(:issue_no_assignee) { create(:issue, project: project) } + + before do + project.team << [guest, :guest] + project.team << [author, :guest] + project.team << [assignee, :guest] + project.team << [reporter, :reporter] + + group.add_reporter(reporter_from_group_link) + + create(:project_group_link, group: group, project: project) + end + + it 'does not allow non-members to read issues' do + expect(permissions(non_member, issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows guests to read issues' do + expect(permissions(guest, issue)).to include(:read_issue) + expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) + + expect(permissions(guest, issue_no_assignee)).to include(:read_issue) + expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows reporters to read, update, and admin issues' do + expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows reporters from group links to read, update, and admin issues' do + expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows issue authors to read and update their issues' do + expect(permissions(author, issue)).to include(:read_issue, :update_issue) + expect(permissions(author, issue)).not_to include(:admin_issue) + + expect(permissions(author, issue_no_assignee)).to include(:read_issue) + expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows issue assignees to read and update their issues' do + expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, issue)).not_to include(:admin_issue) + + expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) + expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end - context 'with a user that is a project reporter' do - before do - project.team << [user, :reporter] - end + context 'with confidential issues' do + let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } + let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end + it 'does not allow non-members to read confidential issues' do + expect(permissions(non_member, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(non_member, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end + + it 'does not allow guests to read confidential issues' do + expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end - it 'includes the admin_issue permission' do - expect(policies).to include(:admin_issue) - end + it 'allows reporters to read, update, and admin confidential issues' do + expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end - it 'includes the update_issue permission' do - expect(policies).to include(:update_issue) - end + it 'allows reporters from group links to read, update, and admin confidential issues' do + expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) end - context 'with a user that is a project guest' do - before do - project.team << [user, :guest] - end + it 'allows issue authors to read and update their confidential issues' do + expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(author, confidential_issue)).not_to include(:admin_issue) - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end + expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end + it 'allows issue assignees to read and update their confidential issues' do + expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end + expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) end end + end - context 'using a confidential issue' do - let(:issue) { create(:issue, :confidential) } + context 'a public project' do + let(:project) { create(:empty_project, :public) } + let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } + let(:issue_no_assignee) { create(:issue, project: project) } - context 'with a regular user' do - let(:policies) { described_class.abilities(user, issue).to_set } + before do + project.team << [guest, :guest] + project.team << [reporter, :reporter] - it 'does not include the read_issue permission' do - expect(policies).not_to include(:read_issue) - end + group.add_reporter(reporter_from_group_link) - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end + create(:project_group_link, group: group, project: project) + end - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end - end + it 'allows guests to read issues' do + expect(permissions(guest, issue)).to include(:read_issue) + expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) + + expect(permissions(guest, issue_no_assignee)).to include(:read_issue) + expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows reporters to read, update, and admin issues' do + expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows reporters from group links to read, update, and admin issues' do + expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end - context 'with a user that is a project member' do - let(:policies) { described_class.abilities(user, issue).to_set } + it 'allows issue authors to read and update their issues' do + expect(permissions(author, issue)).to include(:read_issue, :update_issue) + expect(permissions(author, issue)).not_to include(:admin_issue) - before do - issue.project.team << [user, :reporter] - end + expect(permissions(author, issue_no_assignee)).to include(:read_issue) + expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end + + it 'allows issue assignees to read and update their issues' do + expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, issue)).not_to include(:admin_issue) - it 'includes the read_issue permission' do - expect(policies).to include(:read_issue) - end + expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) + expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) + end - it 'includes the admin_issue permission' do - expect(policies).to include(:admin_issue) - end + context 'with confidential issues' do + let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } + let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - it 'includes the update_issue permission' do - expect(policies).to include(:update_issue) - end + it 'does not allow guests to read confidential issues' do + expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) end - context 'without a user' do - let(:policies) { described_class.abilities(nil, issue).to_set } + it 'allows reporters to read, update, and admin confidential issues' do + expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end + + it 'allows reporter from group links to read, update, and admin confidential issues' do + expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) + expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) + end - it 'does not include the read_issue permission' do - expect(policies).not_to include(:read_issue) - end + it 'allows issue authors to read and update their confidential issues' do + expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(author, confidential_issue)).not_to include(:admin_issue) + + expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) + end - it 'does not include the admin_issue permission' do - expect(policies).not_to include(:admin_issue) - end + it 'allows issue assignees to read and update their confidential issues' do + expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) + expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - it 'does not include the update_issue permission' do - expect(policies).not_to include(:update_issue) - end + expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) end end end diff --git a/spec/policies/issues_policy_spec.rb b/spec/policies/issues_policy_spec.rb deleted file mode 100644 index 2b7b6cad654..00000000000 --- a/spec/policies/issues_policy_spec.rb +++ /dev/null @@ -1,193 +0,0 @@ -require 'spec_helper' - -describe IssuePolicy, models: true do - let(:guest) { create(:user) } - let(:author) { create(:user) } - let(:assignee) { create(:user) } - let(:reporter) { create(:user) } - let(:group) { create(:group, :public) } - let(:reporter_from_group_link) { create(:user) } - - def permissions(user, issue) - IssuePolicy.abilities(user, issue).to_set - end - - context 'a private project' do - let(:non_member) { create(:user) } - let(:project) { create(:empty_project, :private) } - let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } - let(:issue_no_assignee) { create(:issue, project: project) } - - before do - project.team << [guest, :guest] - project.team << [author, :guest] - project.team << [assignee, :guest] - project.team << [reporter, :reporter] - - group.add_reporter(reporter_from_group_link) - - create(:project_group_link, group: group, project: project) - end - - it 'does not allow non-members to read issues' do - expect(permissions(non_member, issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(non_member, issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows guests to read issues' do - expect(permissions(guest, issue)).to include(:read_issue) - expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) - - expect(permissions(guest, issue_no_assignee)).to include(:read_issue) - expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin issues' do - expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters from group links to read, update, and admin issues' do - expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their issues' do - expect(permissions(author, issue)).to include(:read_issue, :update_issue) - expect(permissions(author, issue)).not_to include(:admin_issue) - - expect(permissions(author, issue_no_assignee)).to include(:read_issue) - expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their issues' do - expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, issue)).not_to include(:admin_issue) - - expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) - expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - context 'with confidential issues' do - let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } - let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - - it 'does not allow non-members to read confidential issues' do - expect(permissions(non_member, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(non_member, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'does not allow guests to read confidential issues' do - expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin confidential issues' do - expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters from group links to read, update, and admin confidential issues' do - expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their confidential issues' do - expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(author, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their confidential issues' do - expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - end - end - - context 'a public project' do - let(:project) { create(:empty_project, :public) } - let(:issue) { create(:issue, project: project, assignee: assignee, author: author) } - let(:issue_no_assignee) { create(:issue, project: project) } - - before do - project.team << [guest, :guest] - project.team << [reporter, :reporter] - - group.add_reporter(reporter_from_group_link) - - create(:project_group_link, group: group, project: project) - end - - it 'allows guests to read issues' do - expect(permissions(guest, issue)).to include(:read_issue) - expect(permissions(guest, issue)).not_to include(:update_issue, :admin_issue) - - expect(permissions(guest, issue_no_assignee)).to include(:read_issue) - expect(permissions(guest, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin issues' do - expect(permissions(reporter, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters from group links to read, update, and admin issues' do - expect(permissions(reporter_from_group_link, issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their issues' do - expect(permissions(author, issue)).to include(:read_issue, :update_issue) - expect(permissions(author, issue)).not_to include(:admin_issue) - - expect(permissions(author, issue_no_assignee)).to include(:read_issue) - expect(permissions(author, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their issues' do - expect(permissions(assignee, issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, issue)).not_to include(:admin_issue) - - expect(permissions(assignee, issue_no_assignee)).to include(:read_issue) - expect(permissions(assignee, issue_no_assignee)).not_to include(:update_issue, :admin_issue) - end - - context 'with confidential issues' do - let(:confidential_issue) { create(:issue, :confidential, project: project, assignee: assignee, author: author) } - let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) } - - it 'does not allow guests to read confidential issues' do - expect(permissions(guest, confidential_issue)).not_to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(guest, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporters to read, update, and admin confidential issues' do - expect(permissions(reporter, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows reporter from group links to read, update, and admin confidential issues' do - expect(permissions(reporter_from_group_link, confidential_issue)).to include(:read_issue, :update_issue, :admin_issue) - expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue authors to read and update their confidential issues' do - expect(permissions(author, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(author, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(author, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - - it 'allows issue assignees to read and update their confidential issues' do - expect(permissions(assignee, confidential_issue)).to include(:read_issue, :update_issue) - expect(permissions(assignee, confidential_issue)).not_to include(:admin_issue) - - expect(permissions(assignee, confidential_issue_no_assignee)).not_to include(:read_issue, :update_issue, :admin_issue) - end - end - end -end diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index b5897b2e346..868fef65c1c 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::API do +describe 'doorkeeper access' do let!(:user) { create(:user) } let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) } let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" } diff --git a/spec/requests/api/api_internal_helpers_spec.rb b/spec/requests/api/helpers/internal_helpers_spec.rb index f5265ea60ff..db716b340f1 100644 --- a/spec/requests/api/api_internal_helpers_spec.rb +++ b/spec/requests/api/helpers/internal_helpers_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe ::API::Helpers::InternalHelpers do - include ::API::Helpers::InternalHelpers + include described_class describe '.clean_project_path' do project = 'namespace/project' diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 06c8eb1d0b7..ed392acc607 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe API::Helpers do include API::APIGuard::HelperMethods - include API::Helpers + include described_class include SentryHelper let(:user) { create(:user) } diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index c4bff1647b5..16e5efb2f5b 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -434,6 +434,19 @@ describe API::MergeRequests do expect(json_response['title']).to eq('Test merge_request') end + it 'returns 422 when target project has disabled merge requests' do + project.project_feature.update(merge_requests_access_level: 0) + + post api("/projects/#{fork_project.id}/merge_requests", user2), + title: 'Test', + target_branch: 'master', + source_branch: 'markdown', + author: user2, + target_project_id: project.id + + expect(response).to have_http_status(422) + end + it "returns 400 when source_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb index 819df105960..0d56e1f732e 100644 --- a/spec/requests/api/oauth_tokens_spec.rb +++ b/spec/requests/api/oauth_tokens_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::API do +describe 'OAuth tokens' do context 'Resource Owner Password Credentials' do def request_oauth_token(user) post '/oauth/token', username: user.username, password: user.password, grant_type: 'password' diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb index 6c2950a6e6f..f6ff96be566 100644 --- a/spec/requests/api/v3/merge_requests_spec.rb +++ b/spec/requests/api/v3/merge_requests_spec.rb @@ -338,6 +338,19 @@ describe API::MergeRequests do expect(json_response['title']).to eq('Test merge_request') end + it "returns 422 when target project has disabled merge requests" do + project.project_feature.update(merge_requests_access_level: 0) + + post v3_api("/projects/#{fork_project.id}/merge_requests", user2), + title: 'Test', + target_branch: "master", + source_branch: 'markdown', + author: user2, + target_project_id: project.id + + expect(response).to have_http_status(422) + end + it "returns 400 when source_branch is missing" do post v3_api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb new file mode 100644 index 00000000000..51fbfecec4b --- /dev/null +++ b/spec/requests/request_profiler_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe 'Request Profiler' do + let(:user) { create(:user) } + + shared_examples 'profiling a request' do + before do + allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + allow(RubyProf::Profile).to receive(:profile) do |&blk| + blk.call + RubyProf::Profile.new + end + end + + it 'creates a profile of the request' do + project = create(:project, namespace: user.namespace) + time = Time.now + path = "/#{project.path_with_namespace}" + + Timecop.freeze(time) do + get path, nil, 'X-Profile-Token' => Gitlab::RequestProfiler.profile_token + end + + profile_path = "#{Gitlab.config.shared.path}/tmp/requests_profiles/#{path.tr('/', '|')}_#{time.to_i}.html" + expect(File.exist?(profile_path)).to be true + end + + after do + Gitlab::RequestProfiler.remove_all_profiles + end + end + + context "when user is logged-in" do + before do + login_as(user) + end + + include_examples 'profiling a request' + end + + context "when user is not logged-in" do + include_examples 'profiling a request' + end +end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 99c44bde151..e5fc0b676af 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -71,13 +71,15 @@ describe Admin::ProjectsController, "routing" do end end -# admin_hook_test GET /admin/hooks/:hook_id/test(.:format) admin/hooks#test +# admin_hook_test GET /admin/hooks/:id/test(.:format) admin/hooks#test # admin_hooks GET /admin/hooks(.:format) admin/hooks#index # POST /admin/hooks(.:format) admin/hooks#create # admin_hook DELETE /admin/hooks/:id(.:format) admin/hooks#destroy +# PUT /admin/hooks/:id(.:format) admin/hooks#update +# edit_admin_hook GET /admin/hooks/:id(.:format) admin/hooks#edit describe Admin::HooksController, "routing" do it "to #test" do - expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', hook_id: '1') + expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1') end it "to #index" do @@ -88,6 +90,14 @@ describe Admin::HooksController, "routing" do expect(post("/admin/hooks")).to route_to('admin/hooks#create') end + it "to #edit" do + expect(get("/admin/hooks/1/edit")).to route_to('admin/hooks#edit', id: '1') + end + + it "to #update" do + expect(put("/admin/hooks/1")).to route_to('admin/hooks#update', id: '1') + end + it "to #destroy" do expect(delete("/admin/hooks/1")).to route_to('admin/hooks#destroy', id: '1') end diff --git a/spec/routing/environments_spec.rb b/spec/routing/environments_spec.rb index ba124de70bb..624f3c43f0a 100644 --- a/spec/routing/environments_spec.rb +++ b/spec/routing/environments_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::EnvironmentsController, :routing do +describe 'environments routing', :routing do let(:project) { create(:empty_project) } let(:environment) do diff --git a/spec/routing/notifications_routing_spec.rb b/spec/routing/notifications_routing_spec.rb index 24592942a96..54ed87b5520 100644 --- a/spec/routing/notifications_routing_spec.rb +++ b/spec/routing/notifications_routing_spec.rb @@ -1,13 +1,11 @@ require "spec_helper" -describe Profiles::NotificationsController do - describe "routing" do - it "routes to #show" do - expect(get("/profile/notifications")).to route_to("profiles/notifications#show") - end +describe "notifications routing" do + it "routes to #show" do + expect(get("/profile/notifications")).to route_to("profiles/notifications#show") + end - it "routes to #update" do - expect(put("/profile/notifications")).to route_to("profiles/notifications#update") - end + it "routes to #update" do + expect(put("/profile/notifications")).to route_to("profiles/notifications#update") end end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index a3de022d242..163df072cf6 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -340,14 +340,16 @@ describe 'project routing' do # test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test # project_hooks GET /:project_id/hooks(.:format) hooks#index # POST /:project_id/hooks(.:format) hooks#create - # project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy + # edit_project_hook GET /:project_id/hooks/:id/edit(.:format) hooks#edit + # project_hook PUT /:project_id/hooks/:id(.:format) hooks#update + # DELETE /:project_id/hooks/:id(.:format) hooks#destroy describe Projects::HooksController, 'routing' do it 'to #test' do expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it_behaves_like 'RESTful project resources' do - let(:actions) { [:index, :create, :destroy] } + let(:actions) { [:index, :create, :destroy, :edit, :update] } let(:controller) { 'hooks' } end end diff --git a/spec/rubocop/cop/migration/add_column_with_default_to_large_table_spec.rb b/spec/rubocop/cop/migration/add_column_with_default_to_large_table_spec.rb new file mode 100644 index 00000000000..07cb3fc4a2e --- /dev/null +++ b/spec/rubocop/cop/migration/add_column_with_default_to_large_table_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../../rubocop/cop/migration/add_column_with_default_to_large_table' + +describe RuboCop::Cop::Migration::AddColumnWithDefaultToLargeTable do + include CopHelper + + subject(:cop) { described_class.new } + + context 'in migration' do + before do + allow(cop).to receive(:in_migration?).and_return(true) + end + + described_class::LARGE_TABLES.each do |table| + it "registers an offense for the #{table} table" do + inspect_source(cop, "add_column_with_default :#{table}, :column, default: true") + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + end + end + end + + it 'registers no offense for non-blacklisted tables' do + inspect_source(cop, "add_column_with_default :table, :column, default: true") + + expect(cop.offenses).to be_empty + end + end + + context 'outside of migration' do + it 'registers no offense' do + table = described_class::LARGE_TABLES.sample + inspect_source(cop, "add_column_with_default :#{table}, :column, default: true") + + expect(cop.offenses).to be_empty + end + end +end diff --git a/spec/rubocop/cop/migration/add_column_with_default_spec.rb b/spec/rubocop/cop/migration/reversible_add_column_with_default_spec.rb index 6b9b6b19650..3723d635083 100644 --- a/spec/rubocop/cop/migration/add_column_with_default_spec.rb +++ b/spec/rubocop/cop/migration/reversible_add_column_with_default_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' require 'rubocop' require 'rubocop/rspec/support' -require_relative '../../../../rubocop/cop/migration/add_column_with_default' +require_relative '../../../../rubocop/cop/migration/reversible_add_column_with_default' -describe RuboCop::Cop::Migration::AddColumnWithDefault do +describe RuboCop::Cop::Migration::ReversibleAddColumnWithDefault do include CopHelper subject(:cop) { described_class.new } diff --git a/spec/serializers/analytics_generic_entity_spec.rb b/spec/serializers/analytics_issue_entity_spec.rb index 68086216ba9..68086216ba9 100644 --- a/spec/serializers/analytics_generic_entity_spec.rb +++ b/spec/serializers/analytics_issue_entity_spec.rb diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb index 95eca5463eb..69355bcde42 100644 --- a/spec/serializers/deployment_entity_spec.rb +++ b/spec/serializers/deployment_entity_spec.rb @@ -3,25 +3,23 @@ require 'spec_helper' describe DeploymentEntity do let(:user) { create(:user) } let(:request) { double('request') } + let(:deployment) { create(:deployment) } + let(:entity) { described_class.new(deployment, request: request) } + subject { entity.as_json } before do allow(request).to receive(:user).and_return(user) end - let(:entity) do - described_class.new(deployment, request: request) - end - - let(:deployment) { create(:deployment) } - - subject { entity.as_json } - it 'exposes internal deployment id' do expect(subject).to include(:iid) end it 'exposes nested information about branch' do expect(subject[:ref][:name]).to eq 'master' - expect(subject[:ref][:ref_path]).not_to be_empty + end + + it 'exposes creation date' do + expect(subject).to include(:created_at) end end diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index c94902dbab8..3964b998084 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -18,6 +18,12 @@ describe StatusEntity do it 'contains status details' do expect(subject).to include :text, :icon, :favicon, :label, :group expect(subject).to include :has_details, :details_path + expect(subject[:favicon]).to eq('/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 eq('/assets/ci_favicons/dev/favicon_status_success.ico') end end end diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb index 4a4929daefc..c3b4c2176ee 100644 --- a/spec/services/issues/resolve_discussions_spec.rb +++ b/spec/services/issues/resolve_discussions_spec.rb @@ -1,15 +1,15 @@ require 'spec_helper.rb' -class DummyService < Issues::BaseService - include ::Issues::ResolveDiscussions +describe Issues::ResolveDiscussions, services: true do + class DummyService < Issues::BaseService + include ::Issues::ResolveDiscussions - def initialize(*args) - super - filter_resolve_discussion_params + def initialize(*args) + super + filter_resolve_discussion_params + end end -end -describe DummyService, services: true do let(:project) { create(:project, :repository) } let(:user) { create(:user) } @@ -23,7 +23,7 @@ describe DummyService, services: true do let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: "other") } describe "#merge_request_for_resolving_discussion" do - let(:service) { described_class.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid) } + let(:service) { DummyService.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid) } it "finds the merge request" do expect(service.merge_request_to_resolve_discussions_of).to eq(merge_request) @@ -43,7 +43,7 @@ describe DummyService, services: true do describe "#discussions_to_resolve" do it "contains a single discussion when matching merge request and discussion are passed" do - service = described_class.new( + service = DummyService.new( project, user, discussion_to_resolve: discussion.id, @@ -61,7 +61,7 @@ describe DummyService, services: true do noteable: merge_request, project: merge_request.target_project, line_number: 15)]) - service = described_class.new( + service = DummyService.new( project, user, merge_request_to_resolve_discussions_of: merge_request.iid @@ -79,7 +79,7 @@ describe DummyService, services: true do project: merge_request.target_project, line_number: 15, )]) - service = described_class.new( + service = DummyService.new( project, user, merge_request_to_resolve_discussions_of: merge_request.iid @@ -92,7 +92,7 @@ describe DummyService, services: true do end it "is empty when a discussion and another merge request are passed" do - service = described_class.new( + service = DummyService.new( project, user, discussion_to_resolve: discussion.id, diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index be9f9ea2dec..6f9d1208b1d 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -261,6 +261,16 @@ describe MergeRequests::BuildService, services: true do end end + context 'upstream project has disabled merge requests' do + let(:upstream_project) { create(:empty_project, :merge_requests_disabled) } + let(:project) { create(:empty_project, forked_from_project: upstream_project) } + let(:commits) { Commit.decorate([commit_1], project) } + + it 'sets target project correctly' do + expect(merge_request.target_project).to eq(project) + end + end + context 'target_project is set and accessible by current_user' do let(:target_project) { create(:project, :public, :repository)} let(:commits) { Commit.decorate([commit_1], project) } diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb index 290e00ea1ba..4a7d8ab4c6c 100644 --- a/spec/services/merge_requests/get_urls_service_spec.rb +++ b/spec/services/merge_requests/get_urls_service_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" describe MergeRequests::GetUrlsService do let(:project) { create(:project, :public, :repository) } - let(:service) { MergeRequests::GetUrlsService.new(project) } + let(:service) { described_class.new(project) } let(:source_branch) { "my_branch" } let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" } let(:show_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/#{merge_request.iid}" } @@ -89,7 +89,7 @@ describe MergeRequests::GetUrlsService do let!(:merge_request) { create(:merge_request, source_project: forked_project, target_project: project, source_branch: source_branch) } let(:changes) { existing_branch_changes } # Source project is now the forked one - let(:service) { MergeRequests::GetUrlsService.new(forked_project) } + let(:service) { described_class.new(forked_project) } before do allow(forked_project).to receive(:empty_repo?).and_return(false) diff --git a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb index 35804d41b46..935f4710851 100644 --- a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb +++ b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe MergeRequests::MergeRequestDiffCacheService do - let(:subject) { MergeRequests::MergeRequestDiffCacheService.new } + let(:subject) { described_class.new } describe '#execute' do it 'retrieves the diff files to cache the highlighted result' do diff --git a/spec/services/merge_requests/resolve_service_spec.rb b/spec/services/merge_requests/resolve_service_spec.rb index eaf7785e549..3afd6b92900 100644 --- a/spec/services/merge_requests/resolve_service_spec.rb +++ b/spec/services/merge_requests/resolve_service_spec.rb @@ -50,7 +50,7 @@ describe MergeRequests::ResolveService do context 'when the source and target project are the same' do before do - MergeRequests::ResolveService.new(project, user, params).execute(merge_request) + described_class.new(project, user, params).execute(merge_request) end it 'creates a commit with the message' do @@ -75,7 +75,7 @@ describe MergeRequests::ResolveService do end before do - MergeRequests::ResolveService.new(fork_project, user, params).execute(merge_request_from_fork) + described_class.new(fork_project, user, params).execute(merge_request_from_fork) end it 'creates a commit with the message' do @@ -115,7 +115,7 @@ describe MergeRequests::ResolveService do end before do - MergeRequests::ResolveService.new(project, user, params).execute(merge_request) + described_class.new(project, user, params).execute(merge_request) end it 'creates a commit with the message' do @@ -154,7 +154,7 @@ describe MergeRequests::ResolveService do } end - let(:service) { MergeRequests::ResolveService.new(project, user, invalid_params) } + let(:service) { described_class.new(project, user, invalid_params) } it 'raises a MissingResolution error' do expect { service.execute(merge_request) }. @@ -180,7 +180,7 @@ describe MergeRequests::ResolveService do } end - let(:service) { MergeRequests::ResolveService.new(project, user, invalid_params) } + let(:service) { described_class.new(project, user, invalid_params) } it 'raises a MissingResolution error' do expect { service.execute(merge_request) }. @@ -202,7 +202,7 @@ describe MergeRequests::ResolveService do } end - let(:service) { MergeRequests::ResolveService.new(project, user, invalid_params) } + let(:service) { described_class.new(project, user, invalid_params) } it 'raises a MissingFiles error' do expect { service.execute(merge_request) }. diff --git a/spec/services/merge_requests/resolved_discussion_notification_service.rb b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb index 7ddd812e513..7ddd812e513 100644 --- a/spec/services/merge_requests/resolved_discussion_notification_service.rb +++ b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb diff --git a/spec/services/projects/enable_deploy_key_service_spec.rb b/spec/services/projects/enable_deploy_key_service_spec.rb index a37510cf159..78626fbad4b 100644 --- a/spec/services/projects/enable_deploy_key_service_spec.rb +++ b/spec/services/projects/enable_deploy_key_service_spec.rb @@ -21,6 +21,16 @@ describe Projects::EnableDeployKeyService, services: true do end end + context 'add the same key twice' do + before do + project.deploy_keys << deploy_key + end + + it 'returns existing key' do + expect(service.execute).to eq(deploy_key) + end + end + def service Projects::EnableDeployKeyService.new(project, user, params) end diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index eaf63457b32..fff12beed71 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::HousekeepingService do - subject { Projects::HousekeepingService.new(project) } + subject { described_class.new(project) } let(:project) { create(:project, :repository) } before do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e67ad8f3455..e2d5928e5b2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,8 +13,9 @@ rspec_profiling_is_configured = ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING'] branch_can_be_profiled = - ENV['CI_COMMIT_REF_NAME'] == 'master' || - ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/ + ENV['GITLAB_DATABASE'] == 'postgresql' && + (ENV['CI_COMMIT_REF_NAME'] == 'master' || + ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/) if rspec_profiling_is_configured && (!ENV.key?('CI') || branch_can_be_profiled) require 'rspec_profiling/rspec' diff --git a/spec/support/fake_migration_classes.rb b/spec/support/fake_migration_classes.rb new file mode 100644 index 00000000000..3de0460c3ca --- /dev/null +++ b/spec/support/fake_migration_classes.rb @@ -0,0 +1,3 @@ +class FakeRenameReservedPathMigrationV1 < ActiveRecord::Migration + include Gitlab::Database::RenameReservedPathsMigration::V1 +end diff --git a/spec/support/import_export/import_export.yml b/spec/support/import_export/import_export.yml index 17136dee000..734d6838f4d 100644 --- a/spec/support/import_export/import_export.yml +++ b/spec/support/import_export/import_export.yml @@ -11,9 +11,6 @@ project_tree: - :user included_attributes: - project: - - :name - - :path merge_requests: - :id user: @@ -21,4 +18,7 @@ included_attributes: excluded_attributes: merge_requests: - - :iid
\ No newline at end of file + - :iid + project: + - :id + - :created_at
\ No newline at end of file diff --git a/spec/support/milestone_tabs_examples.rb b/spec/support/milestone_tabs_examples.rb new file mode 100644 index 00000000000..c69f8e11008 --- /dev/null +++ b/spec/support/milestone_tabs_examples.rb @@ -0,0 +1,68 @@ +shared_examples 'milestone tabs' do + def go(path, extra_params = {}) + params = if milestone.is_a?(GlobalMilestone) + { group_id: group.id, id: milestone.safe_title, title: milestone.title } + else + { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid } + end + + get path, params.merge(extra_params) + end + + describe '#merge_requests' do + context 'as html' do + before { go(:merge_requests, format: 'html') } + + it 'redirects to milestone#show' do + expect(response).to redirect_to(milestone_path) + end + end + + context 'as json' do + before { go(:merge_requests, format: 'json') } + + it 'renders the merge requests tab template to a string' do + expect(response).to render_template('shared/milestones/_merge_requests_tab') + expect(json_response).to have_key('html') + end + end + end + + describe '#participants' do + context 'as html' do + before { go(:participants, format: 'html') } + + it 'redirects to milestone#show' do + expect(response).to redirect_to(milestone_path) + end + end + + context 'as json' do + before { go(:participants, format: 'json') } + + it 'renders the participants tab template to a string' do + expect(response).to render_template('shared/milestones/_participants_tab') + expect(json_response).to have_key('html') + end + end + end + + describe '#labels' do + context 'as html' do + before { go(:labels, format: 'html') } + + it 'redirects to milestone#show' do + expect(response).to redirect_to(milestone_path) + end + end + + context 'as json' do + before { go(:labels, format: 'json') } + + it 'renders the labels tab template to a string' do + expect(response).to render_template('shared/milestones/_labels_tab') + expect(json_response).to have_key('html') + end + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 5c8ee8d62f5..0b3c6169c9b 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -39,7 +39,8 @@ module TestEnv 'wip' => 'b9238ee', 'csv' => '3dd0896', 'v1.1.0' => 'b83d6e3', - 'add-ipython-files' => '6d85bb69' + 'add-ipython-files' => '6d85bb69', + 'add-pdf-file' => 'e774ebd3' }.freeze # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb index 0bfa7f72ff8..73da23391ee 100644 --- a/spec/support/wait_for_requests.rb +++ b/spec/support/wait_for_requests.rb @@ -1,11 +1,15 @@ +require_relative './wait_for_ajax' + module WaitForRequests extend self + include WaitForAjax # This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests def wait_for_requests_complete Gitlab::Testing::RequestBlockerMiddleware.block_requests! wait_for('pending AJAX requests complete') do - Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? + Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero? && + finished_all_ajax_requests? end ensure Gitlab::Testing::RequestBlockerMiddleware.allow_requests! diff --git a/spec/tasks/config_lint_spec.rb b/spec/tasks/config_lint_spec.rb index c32f9a740b7..ed6c5b09663 100644 --- a/spec/tasks/config_lint_spec.rb +++ b/spec/tasks/config_lint_spec.rb @@ -5,11 +5,11 @@ describe ConfigLint do let(:files){ ['lib/support/fake.sh'] } it 'errors out if any bash scripts have errors' do - expect { ConfigLint.run(files){ system('exit 1') } }.to raise_error(SystemExit) + expect { described_class.run(files){ system('exit 1') } }.to raise_error(SystemExit) end it 'passes if all scripts are fine' do - expect { ConfigLint.run(files){ system('exit 0') } }.not_to raise_error + expect { described_class.run(files){ system('exit 0') } }.not_to raise_error end end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 0a4a6ed8145..df2f2ce95e6 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -230,11 +230,13 @@ describe 'gitlab:app namespace rake task' do before do FileUtils.mkdir('tmp/tests/default_storage') FileUtils.mkdir('tmp/tests/custom_storage') + gitaly_address = Gitlab.config.repositories.storages.default.gitaly_address storages = { - 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage') }, - 'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage') } + 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage'), 'gitaly_address' => gitaly_address }, + 'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage'), 'gitaly_address' => gitaly_address } } allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) + Gitlab::GitalyClient.configure_channels # Create the projects now, after mocking the settings but before doing the backup project_a diff --git a/spec/validators/dynamic_path_validator_spec.rb b/spec/validators/dynamic_path_validator_spec.rb new file mode 100644 index 00000000000..b114bfc1bca --- /dev/null +++ b/spec/validators/dynamic_path_validator_spec.rb @@ -0,0 +1,266 @@ +require 'spec_helper' + +describe DynamicPathValidator do + let(:validator) { described_class.new(attributes: [:path]) } + + # Pass in a full path to remove the format segment: + # `/ci/lint(.:format)` -> `/ci/lint` + def without_format(path) + path.split('(', 2)[0] + end + + # Pass in a full path and get the last segment before a wildcard + # That's not a parameter + # `/*namespace_id/:project_id/builds/artifacts/*ref_name_and_path` + # -> 'builds/artifacts' + def path_before_wildcard(path) + path = path.gsub(STARTING_WITH_NAMESPACE, "") + path_segments = path.split('/').reject(&:empty?) + wildcard_index = path_segments.index { |segment| parameter?(segment) } + + segments_before_wildcard = path_segments[0..wildcard_index - 1] + + segments_before_wildcard.join('/') + end + + def parameter?(segment) + segment =~ /[*:]/ + end + + # If the path is reserved. Then no conflicting paths can# be created for any + # route using this reserved word. + # + # Both `builds/artifacts` & `build` are covered by reserving the word + # `build` + def wildcards_include?(path) + described_class::WILDCARD_ROUTES.include?(path) || + described_class::WILDCARD_ROUTES.include?(path.split('/').first) + end + + def failure_message(missing_words, constant_name, migration_helper) + missing_words = Array(missing_words) + <<-MSG + Found new routes that could cause conflicts with existing namespaced routes + for groups or projects. + + Add <#{missing_words.join(', ')}> to `DynamicPathValidator::#{constant_name} + to make sure no projects or namespaces can be created with those paths. + + To rename any existing records with those paths you can use the + `Gitlab::Database::RenameReservedpathsMigration::<VERSION>.#{migration_helper}` + migration helper. + + Make sure to make a note of the renamed records in the release blog post. + + MSG + end + + let(:all_routes) do + Rails.application.routes.routes.routes. + map { |r| r.path.spec.to_s } + end + + let(:routes_without_format) { all_routes.map { |path| without_format(path) } } + + # Routes not starting with `/:` or `/*` + # all routes not starting with a param + let(:routes_not_starting_in_wildcard) { routes_without_format.select { |p| p !~ %r{^/[:*]} } } + + let(:top_level_words) do + routes_not_starting_in_wildcard.map do |route| + route.split('/')[1] + end.compact.uniq + end + + # All routes that start with a namespaced path, that have 1 or more + # path-segments before having another wildcard parameter. + # - Starting with paths: + # - `/*namespace_id/:project_id/` + # - `/*namespace_id/:id/` + # - Followed by one or more path-parts not starting with `:` or `*` + # - Followed by a path-part that includes a wildcard parameter `*` + # At the time of writing these routes match: http://rubular.com/r/Rv2pDE5Dvw + STARTING_WITH_NAMESPACE = %r{^/\*namespace_id/:(project_)?id} + NON_PARAM_PARTS = %r{[^:*][a-z\-_/]*} + ANY_OTHER_PATH_PART = %r{[a-z\-_/:]*} + WILDCARD_SEGMENT = %r{\*} + let(:namespaced_wildcard_routes) do + routes_without_format.select do |p| + p =~ %r{#{STARTING_WITH_NAMESPACE}/#{NON_PARAM_PARTS}/#{ANY_OTHER_PATH_PART}#{WILDCARD_SEGMENT}} + end + end + + # This will return all paths that are used in a namespaced route + # before another wildcard path: + # + # /*namespace_id/:project_id/builds/artifacts/*ref_name_and_path + # /*namespace_id/:project_id/info/lfs/objects/*oid + # /*namespace_id/:project_id/commits/*id + # /*namespace_id/:project_id/builds/:build_id/artifacts/file/*path + # -> ['builds/artifacts', 'info/lfs/objects', 'commits', 'artifacts/file'] + let(:all_wildcard_paths) do + namespaced_wildcard_routes.map do |route| + path_before_wildcard(route) + end.uniq + end + + STARTING_WITH_GROUP = %r{^/groups/\*(group_)?id/} + let(:group_routes) do + routes_without_format.select do |path| + path =~ STARTING_WITH_GROUP + end + end + + let(:paths_after_group_id) do + group_routes.map do |route| + route.gsub(STARTING_WITH_GROUP, '').split('/').first + end.uniq + end + + describe 'TOP_LEVEL_ROUTES' do + it 'includes all the top level namespaces' do + failure_block = lambda do + missing_words = top_level_words - described_class::TOP_LEVEL_ROUTES + failure_message(missing_words, 'TOP_LEVEL_ROUTES', 'rename_root_paths') + end + + expect(described_class::TOP_LEVEL_ROUTES) + .to include(*top_level_words), failure_block + end + end + + describe 'GROUP_ROUTES' do + it "don't contain a second wildcard" do + failure_block = lambda do + missing_words = paths_after_group_id - described_class::GROUP_ROUTES + failure_message(missing_words, 'GROUP_ROUTES', 'rename_child_paths') + end + + expect(described_class::GROUP_ROUTES) + .to include(*paths_after_group_id), failure_block + end + end + + describe 'WILDCARD_ROUTES' do + it 'includes all paths that can be used after a namespace/project path' do + aggregate_failures do + all_wildcard_paths.each do |path| + expect(wildcards_include?(path)) + .to be(true), failure_message(path, 'WILDCARD_ROUTES', 'rename_wildcard_paths') + end + end + end + end + + describe '.without_reserved_wildcard_paths_regex' do + subject { described_class.without_reserved_wildcard_paths_regex } + + it 'rejects paths starting with a reserved top level' do + expect(subject).not_to match('dashboard/hello/world') + expect(subject).not_to match('dashboard') + end + + it 'matches valid paths with a toplevel word in a different place' do + expect(subject).to match('parent/dashboard/project-path') + end + + it 'rejects paths containing a wildcard reserved word' do + expect(subject).not_to match('hello/edit') + expect(subject).not_to match('hello/edit/in-the-middle') + expect(subject).not_to match('foo/bar1/refs/master/logs_tree') + end + + it 'matches valid paths' do + expect(subject).to match('parent/child/project-path') + end + end + + describe '.regex_excluding_child_paths' do + let(:subject) { described_class.without_reserved_child_paths_regex } + + it 'rejects paths containing a child reserved word' do + expect(subject).not_to match('hello/group_members') + expect(subject).not_to match('hello/activity/in-the-middle') + expect(subject).not_to match('foo/bar1/refs/master/logs_tree') + end + + it 'allows a child path on the top level' do + expect(subject).to match('activity/foo') + expect(subject).to match('avatar') + end + end + + describe ".valid?" do + it 'is not case sensitive' do + expect(described_class.valid?("Users")).to be_falsey + end + + it "isn't valid when the top level is reserved" do + test_path = 'u/should-be-a/reserved-word' + + expect(described_class.valid?(test_path)).to be_falsey + end + + it "isn't valid if any of the path segments is reserved" do + test_path = 'the-wildcard/wikis/is-not-allowed' + + expect(described_class.valid?(test_path)).to be_falsey + end + + it "is valid if the path doesn't contain reserved words" do + test_path = 'there-are/no-wildcards/in-this-path' + + expect(described_class.valid?(test_path)).to be_truthy + end + + it 'allows allows a child path on the last spot' do + test_path = 'there/can-be-a/project-called/labels' + + expect(described_class.valid?(test_path)).to be_truthy + end + + it 'rejects a child path somewhere else' do + test_path = 'there/can-be-no/labels/group' + + expect(described_class.valid?(test_path)).to be_falsey + end + + it 'rejects paths that are in an incorrect format' do + test_path = 'incorrect/format.git' + + expect(described_class.valid?(test_path)).to be_falsey + end + end + + describe '#path_reserved_for_record?' do + it 'reserves a sub-group named activity' do + group = build(:group, :nested, path: 'activity') + + expect(validator.path_reserved_for_record?(group, 'activity')).to be_truthy + end + + it "doesn't reserve a project called activity" do + project = build(:project, path: 'activity') + + expect(validator.path_reserved_for_record?(project, 'activity')).to be_falsey + end + end + + describe '#validates_each' do + it 'adds a message when the path is not in the correct format' do + group = build(:group) + + validator.validate_each(group, :path, "Path with spaces, and comma's!") + + expect(group.errors[:path]).to include(Gitlab::Regex.namespace_regex_message) + end + + it 'adds a message when the path is not in the correct format' do + group = build(:group, path: 'users') + + validator.validate_each(group, :path, 'users') + + expect(group.errors[:path]).to include('users is a reserved name') + end + end +end diff --git a/spec/views/projects/blob/_viewer.html.haml_spec.rb b/spec/views/projects/blob/_viewer.html.haml_spec.rb index a4915264abe..501f90c5f9a 100644 --- a/spec/views/projects/blob/_viewer.html.haml_spec.rb +++ b/spec/views/projects/blob/_viewer.html.haml_spec.rb @@ -21,6 +21,7 @@ describe 'projects/blob/_viewer.html.haml', :view do before do assign(:project, project) + assign(:blob, blob) assign(:id, File.join('master', blob.path)) controller.params[:controller] = 'projects/blob' diff --git a/spec/views/projects/commit/show.html.haml_spec.rb b/spec/views/projects/commit/show.html.haml_spec.rb new file mode 100644 index 00000000000..122075cc10e --- /dev/null +++ b/spec/views/projects/commit/show.html.haml_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe 'projects/commit/show.html.haml', :view do + let(:project) { create(:project, :repository) } + + before do + assign(:project, project) + assign(:repository, project.repository) + assign(:commit, project.commit) + assign(:noteable, project.commit) + assign(:notes, []) + assign(:diffs, project.commit.diffs) + + allow(view).to receive(:current_user).and_return(nil) + allow(view).to receive(:can?).and_return(false) + allow(view).to receive(:can_collaborate_with_project?).and_return(false) + allow(view).to receive(:current_ref).and_return(project.repository.root_ref) + allow(view).to receive(:diff_btn).and_return('') + end + + context 'inline diff view' do + before do + allow(view).to receive(:diff_view).and_return(:inline) + + render + end + + it 'keeps container-limited' do + expect(rendered).not_to have_selector('.limit-container-width') + end + end + + context 'parallel diff view' do + before do + allow(view).to receive(:diff_view).and_return(:parallel) + + render + end + + it 'spans full width' do + expect(rendered).to have_selector('.limit-container-width') + end + end +end diff --git a/spec/workers/delete_user_worker_spec.rb b/spec/workers/delete_user_worker_spec.rb index 0765573408c..5912dd76262 100644 --- a/spec/workers/delete_user_worker_spec.rb +++ b/spec/workers/delete_user_worker_spec.rb @@ -8,13 +8,13 @@ describe DeleteUserWorker do expect_any_instance_of(Users::DestroyService).to receive(:execute). with(user, {}) - DeleteUserWorker.new.perform(current_user.id, user.id) + described_class.new.perform(current_user.id, user.id) end it "uses symbolized keys" do expect_any_instance_of(Users::DestroyService).to receive(:execute). with(user, test: "test") - DeleteUserWorker.new.perform(current_user.id, user.id, "test" => "test") + described_class.new.perform(current_user.id, user.id, "test" => "test") end end diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb index 8cf2b888f9a..a0ed85cc0b3 100644 --- a/spec/workers/emails_on_push_worker_spec.rb +++ b/spec/workers/emails_on_push_worker_spec.rb @@ -12,7 +12,7 @@ describe EmailsOnPushWorker do let(:perform) { subject.perform(project.id, recipients, data.stringify_keys) } let(:email) { ActionMailer::Base.deliveries.last } - subject { EmailsOnPushWorker.new } + subject { described_class.new } describe "#perform" do context "when push is a new branch" do diff --git a/spec/workers/expire_build_instance_artifacts_worker_spec.rb b/spec/workers/expire_build_instance_artifacts_worker_spec.rb index d202b3de77e..1d8da68883b 100644 --- a/spec/workers/expire_build_instance_artifacts_worker_spec.rb +++ b/spec/workers/expire_build_instance_artifacts_worker_spec.rb @@ -34,12 +34,14 @@ describe ExpireBuildInstanceArtifactsWorker do context 'when associated project was removed' do let(:build) do create(:ci_build, :artifacts, artifacts_expiry) do |build| - build.project.delete + build.project.pending_delete = true end end it 'does not remove artifacts' do - expect(build.reload.artifacts_file.exists?).to be_truthy + expect do + build.reload.artifacts_file + end.not_to raise_error end end end diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 029f35512e0..7a590f64e3c 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -6,7 +6,7 @@ describe GitGarbageCollectWorker do let(:project) { create(:project, :repository) } let(:shell) { Gitlab::Shell.new } - subject { GitGarbageCollectWorker.new } + subject { described_class.new } describe "#perform" do it "flushes ref caches when the task is 'gc'" do diff --git a/spec/workers/gitlab_usage_ping_worker_spec.rb b/spec/workers/gitlab_usage_ping_worker_spec.rb index b6c080f36f4..26241044533 100644 --- a/spec/workers/gitlab_usage_ping_worker_spec.rb +++ b/spec/workers/gitlab_usage_ping_worker_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe GitlabUsagePingWorker do - subject { GitlabUsagePingWorker.new } + subject { described_class.new } it "sends POST request" do stub_application_setting(usage_ping_enabled: true) diff --git a/spec/workers/group_destroy_worker_spec.rb b/spec/workers/group_destroy_worker_spec.rb index 1ff5a3b9034..c78efc67076 100644 --- a/spec/workers/group_destroy_worker_spec.rb +++ b/spec/workers/group_destroy_worker_spec.rb @@ -5,7 +5,7 @@ describe GroupDestroyWorker do let(:user) { create(:admin) } let!(:project) { create(:empty_project, namespace: group) } - subject { GroupDestroyWorker.new } + subject { described_class.new } describe "#perform" do it "deletes the project" do diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb index b5e1fdb8ded..303193bab9b 100644 --- a/spec/workers/merge_worker_spec.rb +++ b/spec/workers/merge_worker_spec.rb @@ -15,7 +15,7 @@ describe MergeWorker do it 'clears cache of source repo after removing source branch' do expect(source_project.repository.branch_names).to include('markdown') - MergeWorker.new.perform( + described_class.new.perform( merge_request.id, merge_request.author_id, commit_message: 'wow such merge', should_remove_source_branch: true) diff --git a/spec/workers/pipeline_proccess_worker_spec.rb b/spec/workers/pipeline_process_worker_spec.rb index 86e9d7f6684..86e9d7f6684 100644 --- a/spec/workers/pipeline_proccess_worker_spec.rb +++ b/spec/workers/pipeline_process_worker_spec.rb diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index a2a559a2369..5ab3c4a0e34 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -10,7 +10,7 @@ describe PostReceive do context "as a resque worker" do it "reponds to #perform" do - expect(PostReceive.new).to respond_to(:perform) + expect(described_class.new).to respond_to(:perform) end end @@ -25,7 +25,7 @@ describe PostReceive do it "calls GitTagPushService" do expect_any_instance_of(GitPushService).to receive(:execute).and_return(true) expect_any_instance_of(GitTagPushService).not_to receive(:execute) - PostReceive.new.perform(pwd(project), key_id, base64_changes) + described_class.new.perform(pwd(project), key_id, base64_changes) end end @@ -35,7 +35,7 @@ describe PostReceive do it "calls GitTagPushService" do expect_any_instance_of(GitPushService).not_to receive(:execute) expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true) - PostReceive.new.perform(pwd(project), key_id, base64_changes) + described_class.new.perform(pwd(project), key_id, base64_changes) end end @@ -45,12 +45,12 @@ describe PostReceive do it "does not call any of the services" do expect_any_instance_of(GitPushService).not_to receive(:execute) expect_any_instance_of(GitTagPushService).not_to receive(:execute) - PostReceive.new.perform(pwd(project), key_id, base64_changes) + described_class.new.perform(pwd(project), key_id, base64_changes) end end context "gitlab-ci.yml" do - subject { PostReceive.new.perform(pwd(project), key_id, base64_changes) } + subject { described_class.new.perform(pwd(project), key_id, base64_changes) } context "creates a Ci::Pipeline for every change" do before do @@ -75,7 +75,7 @@ describe PostReceive do context "webhook" do it "fetches the correct project" do expect(Project).to receive(:find_by_full_path).with(project.path_with_namespace).and_return(project) - PostReceive.new.perform(pwd(project), key_id, base64_changes) + described_class.new.perform(pwd(project), key_id, base64_changes) end it "does not run if the author is not in the project" do @@ -85,7 +85,7 @@ describe PostReceive do expect(project).not_to receive(:execute_hooks) - expect(PostReceive.new.perform(pwd(project), key_id, base64_changes)).to be_falsey + expect(described_class.new.perform(pwd(project), key_id, base64_changes)).to be_falsey end it "asks the project to trigger all hooks" do @@ -93,14 +93,14 @@ describe PostReceive do expect(project).to receive(:execute_hooks).twice expect(project).to receive(:execute_services).twice - PostReceive.new.perform(pwd(project), key_id, base64_changes) + described_class.new.perform(pwd(project), key_id, base64_changes) end it "enqueues a UpdateMergeRequestsWorker job" do allow(Project).to receive(:find_by_full_path).and_return(project) expect(UpdateMergeRequestsWorker).to receive(:perform_async).with(project.id, project.owner.id, any_args) - PostReceive.new.perform(pwd(project), key_id, base64_changes) + described_class.new.perform(pwd(project), key_id, base64_changes) end end diff --git a/spec/workers/project_destroy_worker_spec.rb b/spec/workers/project_destroy_worker_spec.rb index 0ab42f99510..3d135f40c1f 100644 --- a/spec/workers/project_destroy_worker_spec.rb +++ b/spec/workers/project_destroy_worker_spec.rb @@ -4,7 +4,7 @@ describe ProjectDestroyWorker do let(:project) { create(:project, :repository) } let(:path) { project.repository.path_to_repo } - subject { ProjectDestroyWorker.new } + subject { described_class.new } describe "#perform" do it "deletes the project" do diff --git a/spec/workers/remove_expired_members_worker_spec.rb b/spec/workers/remove_expired_members_worker_spec.rb index 402aa1e714e..058fdf4c009 100644 --- a/spec/workers/remove_expired_members_worker_spec.rb +++ b/spec/workers/remove_expired_members_worker_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe RemoveExpiredMembersWorker do - let(:worker) { RemoveExpiredMembersWorker.new } + let(:worker) { described_class.new } describe '#perform' do context 'project members' do diff --git a/spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb b/spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb index 6d42946de38..1c183ce54f4 100644 --- a/spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb +++ b/spec/workers/remove_unreferenced_lfs_objects_worker_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe RemoveUnreferencedLfsObjectsWorker do - let(:worker) { RemoveUnreferencedLfsObjectsWorker.new } + let(:worker) { described_class.new } describe '#perform' do let!(:unreferenced_lfs_object1) { create(:lfs_object, oid: '1') } diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index 7d6a2db2972..5e1cb74c7fc 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -5,7 +5,7 @@ describe RepositoryForkWorker do let(:fork_project) { create(:project, :repository, forked_from_project: project) } let(:shell) { Gitlab::Shell.new } - subject { RepositoryForkWorker.new } + subject { described_class.new } before do allow(subject).to receive(:gitlab_shell).and_return(shell) |