diff options
author | Lin Jen-Shin <godfat@godfat.org> | 2017-02-24 17:14:35 +0800 |
---|---|---|
committer | Lin Jen-Shin <godfat@godfat.org> | 2017-02-24 17:14:35 +0800 |
commit | 83418ad846d07658303a9e79f14c51cebbb66cfa (patch) | |
tree | f0f06ed3f3654fc6337ad3a5ec0c6397a3871a95 /spec | |
parent | 91965cefebfa6b2199b9b48e79752bf62cd67305 (diff) | |
parent | c5b29ed6f36779dbb96f4cdc7b1b0bce8bb8dc5e (diff) | |
download | gitlab-ce-83418ad846d07658303a9e79f14c51cebbb66cfa.tar.gz |
Merge remote-tracking branch 'upstream/master' into 27762-add-default-artifacts-expiration
* upstream/master: (247 commits)
Switched CONTRIBUTING.md style guide recommendation for method chaining
Fix new offenses
Stylistic tweaks
Fix OAuth/SAML user blocking behavior
Revert "Enable Style/DotPosition"
Revert "Prefer leading style for Style/DotPosition"
Revert "Enable Style/BarePercentLiterals"
Manually correct autocorrect
Move up delegate calls
Exclude migrations from Style/MutableConstant
ActiveSupport delegation is preferred over Forwardable
Update haml_lint to work with newest rubocop
Add explanations to cops
Update rubocop and rubocop-rspec and regenerate .rubocop_todo.yml
Update rubocop and rubocop-rspec and regenerate .rubocop_todo.yml
Order cops alphabetically
Don’t exclude some file in lib from rubocop
Fix new offenses
Enable Rails/Delegate
Enable Style/WordArray
...
Diffstat (limited to 'spec')
192 files changed, 3789 insertions, 1076 deletions
diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb index 294fae95752..0b8ff006d22 100644 --- a/spec/config/mail_room_spec.rb +++ b/spec/config/mail_room_spec.rb @@ -8,7 +8,7 @@ describe 'mail_room.yml' do context 'when incoming email is disabled' do before do - ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] = Rails.root.join('spec/fixtures/mail_room_disabled.yml').to_s + ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] = Rails.root.join('spec/fixtures/config/mail_room_disabled.yml').to_s Gitlab::MailRoom.reset_config! end @@ -26,7 +26,7 @@ describe 'mail_room.yml' do let(:gitlab_redis) { Gitlab::Redis.new(Rails.env) } before do - ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] = Rails.root.join('spec/fixtures/mail_room_enabled.yml').to_s + ENV['MAIL_ROOM_GITLAB_CONFIG_FILE'] = Rails.root.join('spec/fixtures/config/mail_room_enabled.yml').to_s Gitlab::MailRoom.reset_config! end diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb index 0a3ac9f9512..7072bd5e87c 100644 --- a/spec/controllers/dashboard/todos_controller_spec.rb +++ b/spec/controllers/dashboard/todos_controller_spec.rb @@ -46,7 +46,7 @@ describe Dashboard::TodosController do expect(todo.reload).to be_pending expect(response).to have_http_status(200) - expect(json_response).to eq({ "count" => 1, "done_count" => 0 }) + expect(json_response).to eq({ "count" => "1", "done_count" => "0" }) end end end diff --git a/spec/controllers/profiles/personal_access_tokens_spec.rb b/spec/controllers/profiles/personal_access_tokens_spec.rb index 45534a3a587..9d5f4c99f6d 100644 --- a/spec/controllers/profiles/personal_access_tokens_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_spec.rb @@ -32,10 +32,10 @@ describe Profiles::PersonalAccessTokensController do context "scopes" do it "allows creation of a token with scopes" do - post :create, personal_access_token: { name: FFaker::Product.brand, scopes: ['api', 'read_user'] } + post :create, personal_access_token: { name: FFaker::Product.brand, scopes: %w(api read_user) } expect(created_token).not_to be_nil - expect(created_token.scopes).to eq(['api', 'read_user']) + expect(created_token.scopes).to eq(%w(api read_user)) end it "allows creation of a token with no scopes" do diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index 9de03876755..effd8bcd982 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -68,7 +68,7 @@ describe Projects::BranchesController do describe "created from the new branch button on issues" do let(:branch) { "1-feature-branch" } - let!(:issue) { create(:issue, project: project) } + let(:issue) { create(:issue, project: project) } before do sign_in(user) @@ -95,6 +95,43 @@ describe Projects::BranchesController do issue_iid: issue.iid end + context 'repository-less project' do + let(:project) { create :empty_project } + + it 'redirects to newly created branch' do + result = { status: :success, branch: double(name: branch) } + + expect_any_instance_of(CreateBranchService).to receive(:execute).and_return(result) + expect(SystemNoteService).to receive(:new_issue_branch).and_return(true) + + post :create, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + branch_name: branch, + issue_iid: issue.iid + + expect(response).to redirect_to namespace_project_tree_path(project.namespace, project, branch) + end + + it 'redirects to autodeploy setup page' do + result = { status: :success, branch: double(name: branch) } + + project.services << build(:kubernetes_service) + + expect_any_instance_of(CreateBranchService).to receive(:execute).and_return(result) + expect(SystemNoteService).to receive(:new_issue_branch).and_return(true) + + post :create, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + branch_name: branch, + issue_iid: issue.iid + + expect(response.location).to include(namespace_project_new_blob_path(project.namespace, project, branch)) + expect(response).to have_http_status(302) + end + end + context 'without issue feature access' do before do project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index e576bf9ef79..7871b6a9e10 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -152,6 +152,113 @@ describe Projects::IssuesController do end end + context 'Akismet is enabled' do + let(:project) { create(:project_empty_repo, :public) } + + before do + stub_application_setting(recaptcha_enabled: true) + allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true) + end + + context 'when an issue is not identified as spam' do + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(false) + end + + it 'normally updates the issue' do + expect { update_issue(title: 'Foo') }.to change { issue.reload.title }.to('Foo') + end + end + + context 'when an issue is identified as spam' do + before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) } + + context 'when captcha is not verified' do + def update_spam_issue + update_issue(title: 'Spam Title', description: 'Spam lives here') + end + + before { allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) } + + it 'rejects an issue recognized as a spam' do + expect { update_spam_issue }.not_to change{ issue.reload.title } + end + + it 'rejects an issue recognized as a spam when recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) + + expect { update_spam_issue }.not_to change{ issue.reload.title } + end + + it 'creates a spam log' do + update_spam_issue + + spam_logs = SpamLog.all + + expect(spam_logs.count).to eq(1) + expect(spam_logs.first.title).to eq('Spam Title') + expect(spam_logs.first.recaptcha_verified).to be_falsey + end + + it 'renders verify template' do + update_spam_issue + + expect(response).to render_template(:verify) + end + end + + context 'when captcha is verified' do + let(:spammy_title) { 'Whatever' } + let!(:spam_logs) { create_list(:spam_log, 2, user: user, title: spammy_title) } + + def update_verified_issue + update_issue({ title: spammy_title }, + { spam_log_id: spam_logs.last.id, + recaptcha_verification: true }) + end + + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha) + .and_return(true) + end + + it 'redirect to issue page' do + update_verified_issue + + expect(response). + to redirect_to(namespace_project_issue_path(project.namespace, project, issue)) + end + + it 'accepts an issue after recaptcha is verified' do + expect{ update_verified_issue }.to change{ issue.reload.title }.to(spammy_title) + end + + it 'marks spam log as recaptcha_verified' do + expect { update_verified_issue }.to change { SpamLog.last.recaptcha_verified }.from(false).to(true) + end + + it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do + spam_log = create(:spam_log) + + expect { update_issue(spam_log_id: spam_log.id, recaptcha_verification: true) }. + not_to change { SpamLog.last.recaptcha_verified } + end + end + end + end + + def update_issue(issue_params = {}, additional_params = {}) + params = { + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: issue.iid, + issue: issue_params + }.merge(additional_params) + + put :update, params + end + def move_issue put :update, namespace_id: project.namespace.to_param, @@ -384,7 +491,7 @@ describe Projects::IssuesController do allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true) end - context 'when an issue is not identified as a spam' do + context 'when an issue is not identified as spam' do before do allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(false) @@ -395,7 +502,7 @@ describe Projects::IssuesController do end end - context 'when an issue is identified as a spam' do + context 'when an issue is identified as spam' do before { allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) } context 'when captcha is not verified' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index f84f922ba5e..af13649eec5 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -54,7 +54,8 @@ describe Projects::MergeRequestsController do project_id: fork_project.to_param, merge_request: { source_branch: 'remove-submodule', - target_branch: 'master' }, + target_branch: 'master' + }, format: format end end @@ -772,7 +773,7 @@ describe Projects::MergeRequestsController do section['lines'].each do |line| if section['conflict'] - expect(line['type']).to be_in(['old', 'new']) + expect(line['type']).to be_in(%w(old new)) expect(line.values_at('old_line', 'new_line')).to contain_exactly(nil, a_kind_of(Integer)) else if line['type'].nil? diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 1ed2ee3ab4a..9a1e79c281a 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -12,7 +12,10 @@ describe Projects::PipelinesController do describe 'GET index.json' do before do - create_list(:ci_empty_pipeline, 2, project: project) + create(:ci_empty_pipeline, status: 'pending', project: project) + create(:ci_empty_pipeline, status: 'running', project: project) + create(:ci_empty_pipeline, status: 'created', project: project) + create(:ci_empty_pipeline, status: 'success', project: project) get :index, namespace_id: project.namespace.path, project_id: project.path, @@ -23,9 +26,11 @@ describe Projects::PipelinesController do expect(response).to have_http_status(:ok) expect(json_response).to include('pipelines') - expect(json_response['pipelines'].count).to eq 2 - expect(json_response['count']['all']).to eq 2 - expect(json_response['count']['running_or_pending']).to eq 2 + expect(json_response['pipelines'].count).to eq 4 + expect(json_response['count']['all']).to eq 4 + expect(json_response['count']['running']).to eq 1 + expect(json_response['count']['pending']).to eq 1 + expect(json_response['count']['finished']).to eq 1 end end diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 77ee10a1e15..8bab094a79e 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -70,7 +70,7 @@ describe Projects::SnippetsController do end describe 'POST #create' do - def create_snippet(project, snippet_params = {}) + def create_snippet(project, snippet_params = {}, additional_params = {}) sign_in(user) project.add_developer(user) @@ -79,7 +79,7 @@ describe Projects::SnippetsController do namespace_id: project.namespace.to_param, project_id: project.to_param, project_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params) - } + }.merge(additional_params) end context 'when the snippet is spam' do @@ -87,35 +87,179 @@ describe Projects::SnippetsController do allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) end - context 'when the project is private' do - let(:private_project) { create(:project_empty_repo, :private) } + context 'when the snippet is private' do + it 'creates the snippet' do + expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }. + to change { Snippet.count }.by(1) + end + end + + context 'when the snippet is public' do + it 'rejects the shippet' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + not_to change { Snippet.count } + expect(response).to render_template(:new) + end + + it 'creates a spam log' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) + end + + it 'renders :new with recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) + + create_snippet(project, visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:new) + end - context 'when the snippet is public' do - it 'creates the snippet' do - expect { create_snippet(private_project, visibility_level: Snippet::PUBLIC) }. - to change { Snippet.count }.by(1) + context 'recaptcha enabled' do + before do + stub_application_setting(recaptcha_enabled: true) end + + it 'renders :verify with recaptcha enabled' do + create_snippet(project, visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:verify) + end + + it 'renders snippet page when recaptcha verified' do + spammy_title = 'Whatever' + + spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title) + create_snippet(project, + { visibility_level: Snippet::PUBLIC }, + { spam_log_id: spam_logs.last.id, + recaptcha_verification: true }) + + expect(response).to redirect_to(Snippet.last) + end + end + end + end + end + + describe 'PUT #update' do + let(:project) { create :project, :public } + let(:snippet) { create :project_snippet, author: user, project: project, visibility_level: visibility_level } + + def update_snippet(snippet_params = {}, additional_params = {}) + sign_in(user) + + project.add_developer(user) + + put :update, { + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: snippet.id, + project_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params) + }.merge(additional_params) + + snippet.reload + end + + context 'when the snippet is spam' do + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end + + context 'when the snippet is private' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'updates the snippet' do + expect { update_snippet(title: 'Foo') }. + to change { snippet.reload.title }.to('Foo') end end - context 'when the project is public' do - context 'when the snippet is private' do - it 'creates the snippet' do - expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }. - to change { Snippet.count }.by(1) + context 'when the snippet is public' do + let(:visibility_level) { Snippet::PUBLIC } + + it 'rejects the shippet' do + expect { update_snippet(title: 'Foo') }. + not_to change { snippet.reload.title } + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo') }. + to change { SpamLog.count }.by(1) + end + + it 'renders :edit with recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) + + update_snippet(title: 'Foo') + + expect(response).to render_template(:edit) + end + + context 'recaptcha enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + end + + it 'renders :verify with recaptcha enabled' do + update_snippet(title: 'Foo') + + expect(response).to render_template(:verify) + end + + it 'renders snippet page when recaptcha verified' do + spammy_title = 'Whatever' + + spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title) + snippet = update_snippet({ title: spammy_title }, + { spam_log_id: spam_logs.last.id, + recaptcha_verification: true }) + + expect(response).to redirect_to(snippet) end end + end + + context 'when the private snippet is made public' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'rejects the shippet' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + not_to change { snippet.reload.title } + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) + end + + it 'renders :edit with recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) - context 'when the snippet is public' do - it 'rejects the shippet' do - expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. - not_to change { Snippet.count } - expect(response).to render_template(:new) + update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:edit) + end + + context 'recaptcha enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + end + + it 'renders :verify with recaptcha enabled' do + update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:verify) end - it 'creates a spam log' do - expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. - to change { SpamLog.count }.by(1) + it 'renders snippet page when recaptcha verified' do + spammy_title = 'Whatever' + + spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title) + snippet = update_snippet({ title: spammy_title, visibility_level: Snippet::PUBLIC }, + { spam_log_id: spam_logs.last.id, + recaptcha_verification: true }) + + expect(response).to redirect_to(snippet) end end end diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index f90c0d76ceb..5de3b9890ef 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -139,12 +139,14 @@ describe SnippetsController do end describe 'POST #create' do - def create_snippet(snippet_params = {}) + def create_snippet(snippet_params = {}, additional_params = {}) sign_in(user) post :create, { personal_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params) - } + }.merge(additional_params) + + Snippet.last end context 'when the snippet is spam' do @@ -163,13 +165,164 @@ describe SnippetsController do it 'rejects the shippet' do expect { create_snippet(visibility_level: Snippet::PUBLIC) }. not_to change { Snippet.count } - expect(response).to render_template(:new) end it 'creates a spam log' do expect { create_snippet(visibility_level: Snippet::PUBLIC) }. to change { SpamLog.count }.by(1) end + + it 'renders :new with recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) + + create_snippet(visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:new) + end + + context 'recaptcha enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + end + + it 'renders :verify with recaptcha enabled' do + create_snippet(visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:verify) + end + + it 'renders snippet page when recaptcha verified' do + spammy_title = 'Whatever' + + spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title) + snippet = create_snippet({ title: spammy_title }, + { spam_log_id: spam_logs.last.id, + recaptcha_verification: true }) + + expect(response).to redirect_to(snippet_path(snippet)) + end + end + end + end + end + + describe 'PUT #update' do + let(:project) { create :project } + let(:snippet) { create :personal_snippet, author: user, project: project, visibility_level: visibility_level } + + def update_snippet(snippet_params = {}, additional_params = {}) + sign_in(user) + + put :update, { + id: snippet.id, + personal_snippet: { title: 'Title', content: 'Content' }.merge(snippet_params) + }.merge(additional_params) + + snippet.reload + end + + context 'when the snippet is spam' do + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end + + context 'when the snippet is private' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'updates the snippet' do + expect { update_snippet(title: 'Foo') }. + to change { snippet.reload.title }.to('Foo') + end + end + + context 'when a private snippet is made public' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'rejects the snippet' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + not_to change { snippet.reload.title } + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) + end + + it 'renders :edit with recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) + + update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:edit) + end + + context 'recaptcha enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + end + + it 'renders :verify with recaptcha enabled' do + update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) + + expect(response).to render_template(:verify) + end + + it 'renders snippet page when recaptcha verified' do + spammy_title = 'Whatever' + + spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title) + snippet = update_snippet({ title: spammy_title, visibility_level: Snippet::PUBLIC }, + { spam_log_id: spam_logs.last.id, + recaptcha_verification: true }) + + expect(response).to redirect_to(snippet) + end + end + end + + context 'when the snippet is public' do + let(:visibility_level) { Snippet::PUBLIC } + + it 'rejects the shippet' do + expect { update_snippet(title: 'Foo') }. + not_to change { snippet.reload.title } + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo') }. + to change { SpamLog.count }.by(1) + end + + it 'renders :edit with recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) + + update_snippet(title: 'Foo') + + expect(response).to render_template(:edit) + end + + context 'recaptcha enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + end + + it 'renders :verify with recaptcha enabled' do + update_snippet(title: 'Foo') + + expect(response).to render_template(:verify) + end + + it 'renders snippet page when recaptcha verified' do + spammy_title = 'Whatever' + + spam_logs = create_list(:spam_log, 2, user: user, title: spammy_title) + snippet = update_snippet({ title: spammy_title }, + { spam_log_id: spam_logs.last.id, + recaptcha_verification: true }) + + expect(response).to redirect_to(snippet_path(snippet)) + end + end end end end diff --git a/spec/factories/services.rb b/spec/factories/services.rb index a14a46c803e..51335bdcf1d 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -2,4 +2,14 @@ FactoryGirl.define do factory :service do project factory: :empty_project end + + factory :kubernetes_service do + project factory: :empty_project + active true + properties({ + namespace: 'somepath', + api_url: 'https://kubernetes.example.com', + token: 'a' * 40, + }) + end end diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb index 21ee6cedbae..a7c22615b89 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/features/atom/dashboard_issues_spec.rb @@ -23,7 +23,7 @@ describe "Dashboard Issues Feed", feature: true do visit issues_dashboard_path(:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('state' => ['opened']) diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb index 863412d18eb..a01a050a013 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/features/atom/issues_spec.rb @@ -43,7 +43,7 @@ describe 'Issues Feed', feature: true do :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('state' => ['opened']) @@ -54,7 +54,7 @@ describe 'Issues Feed', feature: true do visit issues_group_path(group, :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id) link = find('link[type="application/atom+xml"]') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('state' => ['opened']) diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb index 2875fc1e533..a3e24bb5ffa 100644 --- a/spec/features/boards/add_issues_modal_spec.rb +++ b/spec/features/boards/add_issues_modal_spec.rb @@ -49,6 +49,12 @@ describe 'Issue Boards add issue modal', :feature, :js do expect(page).not_to have_selector('.add-issues-modal') end + + it 'does not show tooltip on add issues button' do + button = page.find('.issue-boards-search button', text: 'Add issues') + + expect(button[:title]).not_to eq("Please add a list to your board first") + end end context 'issues list' do diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 1b25b51cfb2..e247bfa2980 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -28,10 +28,10 @@ describe 'Issue Boards', feature: true, js: true do expect(page).to have_content('Welcome to your Issue Board!') end - it 'disables add issues button by default' do + it 'shows tooltip on add issues button' do button = page.find('.issue-boards-search button', text: 'Add issues') - expect(button[:disabled]).to eq true + expect(button[:"data-original-title"]).to eq("Please add a list to your board first") end it 'hides the blank state when clicking nevermind button' do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 8f561c8f90b..324ede798fe 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -153,7 +153,7 @@ describe 'Commits' do expect(page).to have_content pipeline.git_author_name expect(page).to have_link('Download artifacts') expect(page).not_to have_link('Cancel running') - expect(page).not_to have_link('Retry failed') + expect(page).not_to have_link('Retry') end end @@ -172,7 +172,7 @@ describe 'Commits' do expect(page).to have_content pipeline.git_author_name expect(page).not_to have_link('Download artifacts') expect(page).not_to have_link('Cancel running') - expect(page).not_to have_link('Retry failed') + expect(page).not_to have_link('Retry') end end end diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb index b898f9bc64f..aa75e1140f6 100644 --- a/spec/features/dashboard_issues_spec.rb +++ b/spec/features/dashboard_issues_spec.rb @@ -49,9 +49,9 @@ describe "Dashboard Issues filtering", feature: true, js: true do visit_issues(milestone_title: '', assignee_id: user.id) link = find('.nav-controls a', text: 'Subscribe') - params = CGI::parse(URI.parse(link[:href]).query) + params = CGI.parse(URI.parse(link[:href]).query) auto_discovery_link = find('link[type="application/atom+xml"]', visible: false) - auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query) + auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query) expect(params).to include('private_token' => [user.private_token]) expect(params).to include('milestone_title' => ['']) diff --git a/spec/features/groups/members/list_spec.rb b/spec/features/groups/members/list_spec.rb index 109de39b2dd..14c193f7450 100644 --- a/spec/features/groups/members/list_spec.rb +++ b/spec/features/groups/members/list_spec.rb @@ -30,6 +30,21 @@ feature 'Groups members list', feature: true do expect(second_row).to be_blank end + it 'updates user to owner level', :js do + group.add_owner(user1) + group.add_developer(user2) + + visit group_group_members_path(group) + + page.within(second_row) do + click_button('Developer') + + click_link('Owner') + + expect(page).to have_button('Owner') + end + end + def first_row page.all('ul.content-list > li')[0] end diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb index 40a1fced8d8..e0b2404e60a 100644 --- a/spec/features/help_pages_spec.rb +++ b/spec/features/help_pages_spec.rb @@ -33,4 +33,30 @@ describe 'Help Pages', feature: true do it_behaves_like 'help page', prefix: '/gitlab' end end + + context 'in a production environment with version check enabled', js: true do + before do + allow(Rails.env).to receive(:production?) { true } + allow(current_application_settings).to receive(:version_check_enabled) { true } + allow_any_instance_of(VersionCheck).to receive(:url) { '/version-check-url' } + + login_as :user + visit help_path + end + + it 'should display a version check image' do + expect(find('.js-version-status-badge')).to be_visible + end + + it 'should have a src url' do + expect(find('.js-version-status-badge')['src']).to match(/\/version-check-url/) + end + + it 'should hide the version check image if the image request fails' do + # We use '--load-images=no' with poltergeist so we must trigger manually + execute_script("$('.js-version-status-badge').trigger('error');") + + expect(find('.js-version-status-badge', visible: false)).not_to be_visible + end + end end diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index e31bc40adc3..b90bf6268fd 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -30,6 +30,13 @@ describe 'issuable list', feature: true do end end + it "counts merge requests closing issues icons for each issue" do + visit_issuable_list(:issue) + + expect(page).to have_selector('.icon-merge-request-unmerged', count: 1) + expect(first('.icon-merge-request-unmerged').find(:xpath, '..')).to have_content(1) + end + def visit_issuable_list(issuable_type) if issuable_type == :issue visit namespace_project_issues_path(project.namespace, project) @@ -40,11 +47,12 @@ describe 'issuable list', feature: true do def create_issuables(issuable_type) 3.times do - if issuable_type == :issue - issuable = create(:issue, project: project, author: user) - else - issuable = create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) - end + issuable = + if issuable_type == :issue + create(:issue, project: project, author: user) + else + create(:merge_request, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) + end 2.times do create(:note_on_issue, noteable: issuable, project: project, note: 'Test note') @@ -53,5 +61,15 @@ describe 'issuable list', feature: true do create(:award_emoji, :downvote, awardable: issuable) create(:award_emoji, :upvote, awardable: issuable) end + + if issuable_type == :issue + issue = Issue.reorder(:iid).first + merge_request = create(:merge_request, + title: FFaker::Lorem.sentence, + source_project: project, + source_branch: FFaker::Name.name) + + MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) + end end end diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 832757b24d4..2f59630b4fb 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -55,7 +55,7 @@ feature 'Issues > Labels bulk assignment', feature: true do context 'to all issues' do before do check 'check_all_issues' - open_labels_dropdown ['bug', 'feature'] + open_labels_dropdown %w(bug feature) update_issues end @@ -70,7 +70,7 @@ feature 'Issues > Labels bulk assignment', feature: true do context 'to a issue' do before do check "selected_issue_#{issue1.id}" - open_labels_dropdown ['bug', 'feature'] + open_labels_dropdown %w(bug feature) update_issues end @@ -112,7 +112,7 @@ feature 'Issues > Labels bulk assignment', feature: true do visit namespace_project_issues_path(project.namespace, project) check 'check_all_issues' - unmark_labels_in_dropdown ['bug', 'feature'] + unmark_labels_in_dropdown %w(bug feature) update_issues end diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb index c6a88e1b7b0..ab3b868fd3a 100644 --- a/spec/features/issues/filtered_search/dropdown_label_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe 'Dropdown label', js: true, feature: true do + include FilteredSearchHelpers + let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:filtered_search) { find('.filtered-search') } @@ -17,12 +19,6 @@ describe 'Dropdown label', js: true, feature: true do let!(:long_label) { create(:label, project: project, title: 'this is a very long title this is a very long title this is a very long title this is a very long title this is a very long title') } end - def init_label_search - filtered_search.set('label:') - # This ensures the dropdown is shown - expect(find(js_dropdown_label)).not_to have_css('.filter-dropdown-loading') - end - def search_for_label(label) init_label_search filtered_search.send_keys(label) diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 64f448a83b7..0420e64d42c 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -1,6 +1,7 @@ require 'rails_helper' describe 'Filter issues', js: true, feature: true do + include FilteredSearchHelpers include WaitForAjax let!(:group) { create(:group) } @@ -17,19 +18,6 @@ describe 'Filter issues', js: true, feature: true do let!(:multiple_words_label) { create(:label, project: project, title: "Two words") } let!(:closed_issue) { create(:issue, title: 'bug that is closed', project: project, state: :closed) } - let(:filtered_search) { find('.filtered-search') } - - def input_filtered_search(search_term, submit: true) - filtered_search.set(search_term) - - if submit - filtered_search.send_keys(:enter) - end - end - - def expect_filtered_search_input(input) - expect(find('.filtered-search').value).to eq(input) - end def expect_no_issues_list page.within '.issues-list' do diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index 1eb981942ea..7b9d4534ada 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -7,9 +7,9 @@ feature 'Issue Sidebar', feature: true do let(:project) { create(:project, :public) } let(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} + let!(:label) { create(:label, project: project, title: 'bug') } before do - create(:label, project: project, title: 'bug') login_as(user) end @@ -50,16 +50,6 @@ feature 'Issue Sidebar', feature: true do visit_issue(project, issue) end - describe 'when clicking on edit labels', js: true do - it 'shows dropdown option to create a new label' do - find('.block.labels .edit-link').click - - page.within('.block.labels') do - expect(page).to have_content 'Create new' - end - end - end - context 'sidebar', js: true do it 'changes size when the screen size is smaller' do sidebar_selector = 'aside.right-sidebar.right-sidebar-collapsed' @@ -77,36 +67,53 @@ feature 'Issue Sidebar', feature: true do end end - context 'creating a new label', js: true do - it 'shows option to crate a new label is present' do + context 'editing issue labels', js: true do + before do page.within('.block.labels') do find('.edit-link').click + end + end + it 'shows option to create a new label' do + page.within('.block.labels') do expect(page).to have_content 'Create new' end end - it 'shows dropdown switches to "create label" section' do - page.within('.block.labels') do - find('.edit-link').click - click_link 'Create new' + context 'creating a new label', js: true do + before do + page.within('.block.labels') do + click_link 'Create new' + end + end - expect(page).to have_content 'Create new label' + it 'shows dropdown switches to "create label" section' do + page.within('.block.labels') do + expect(page).to have_content 'Create new label' + end end - end - it 'adds new label' do - page.within('.block.labels') do - find('.edit-link').click - sleep 1 - click_link 'Create new' + it 'adds new label' do + page.within('.block.labels') do + fill_in 'new_label_name', with: 'wontfix' + page.find(".suggest-colors a", match: :first).click + click_button 'Create' + + page.within('.dropdown-page-one') do + expect(page).to have_content 'wontfix' + end + end + end - fill_in 'new_label_name', with: 'wontfix' - page.find(".suggest-colors a", match: :first).click - click_button 'Create' + it 'shows error message if label title is taken' do + page.within('.block.labels') do + fill_in 'new_label_name', with: label.title + page.find('.suggest-colors a', match: :first).click + click_button 'Create' - page.within('.dropdown-page-one') do - expect(page).to have_content 'wontfix' + page.within('.dropdown-page-two') do + expect(page).to have_content 'Title has already been taken' + end end end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 094f645a077..1e0db4a0499 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -150,7 +150,7 @@ describe 'Issues', feature: true do describe 'Filter issue' do before do - ['foobar', 'barbaz', 'gitlab'].each do |title| + %w(foobar barbaz gitlab).each do |title| create(:issue, author: @user, assignee: @user, @@ -577,6 +577,15 @@ describe 'Issues', feature: true do expect(page.find_field("issue_description").value).to have_content 'banana_sample' end + + it 'adds double newline to end of attachment markdown' do + drop_in_dropzone test_image_file + + # Wait for the file to upload + sleep 1 + + expect(page.find_field("issue_description").value).to match /\n\n$/ + end end end diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb index d710a780111..18508a44184 100644 --- a/spec/features/merge_requests/conflicts_spec.rb +++ b/spec/features/merge_requests/conflicts_spec.rb @@ -154,7 +154,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do 'conflict-binary-file' => 'when the conflicts contain a binary file', 'conflict-missing-side' => 'when the conflicts contain a file edited in one branch and deleted in another', 'conflict-non-utf8' => 'when the conflicts contain a non-UTF-8 file', - } + }.freeze UNRESOLVABLE_CONFLICTS.each do |source_branch, description| context description do diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb index 4c60329865c..55f3c1863ff 100644 --- a/spec/features/merge_requests/filter_by_labels_spec.rb +++ b/spec/features/merge_requests/filter_by_labels_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' feature 'Issue filtering by Labels', feature: true, js: true do + include FilteredSearchHelpers + include MergeRequestHelpers include WaitForAjax let(:project) { create(:project, :public) } @@ -32,123 +34,77 @@ feature 'Issue filtering by Labels', feature: true, js: true do context 'filter by label bug' do before do - select_labels('bug') + input_filtered_search('label:~bug') end it 'apply the filter' do expect(page).to have_content "Bugfix1" expect(page).to have_content "Bugfix2" expect(page).not_to have_content "Feature1" - expect(find('.filtered-labels')).to have_content "bug" - expect(find('.filtered-labels')).not_to have_content "feature" - expect(find('.filtered-labels')).not_to have_content "enhancement" - - find('.js-label-filter-remove').click - wait_for_ajax - expect(find('.filtered-labels', visible: false)).to have_no_content "bug" end end context 'filter by label feature' do before do - select_labels('feature') + input_filtered_search('label:~feature') end it 'applies the filter' do expect(page).to have_content "Feature1" expect(page).not_to have_content "Bugfix2" expect(page).not_to have_content "Bugfix1" - expect(find('.filtered-labels')).to have_content "feature" - expect(find('.filtered-labels')).not_to have_content "bug" - expect(find('.filtered-labels')).not_to have_content "enhancement" end end context 'filter by label enhancement' do before do - select_labels('enhancement') + input_filtered_search('label:~enhancement') end it 'applies the filter' do expect(page).to have_content "Bugfix2" expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Bugfix1" - expect(find('.filtered-labels')).to have_content "enhancement" - expect(find('.filtered-labels')).not_to have_content "bug" - expect(find('.filtered-labels')).not_to have_content "feature" end end context 'filter by label enhancement and bug in issues list' do before do - select_labels('bug', 'enhancement') + input_filtered_search('label:~bug label:~enhancement') end it 'applies the filters' do expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_content "Bugfix2" expect(page).not_to have_content "Feature1" - expect(find('.filtered-labels')).to have_content "bug" - expect(find('.filtered-labels')).to have_content "enhancement" - expect(find('.filtered-labels')).not_to have_content "feature" - - find('.js-label-filter-remove', match: :first).click - wait_for_ajax - - expect(page).to have_content "Bugfix2" - expect(page).not_to have_content "Feature1" - expect(page).not_to have_content "Bugfix1" - expect(find('.filtered-labels')).not_to have_content "bug" - expect(find('.filtered-labels')).to have_content "enhancement" - expect(find('.filtered-labels')).not_to have_content "feature" end end - context 'remove filtered labels' do + context 'clear button' do before do - page.within '.labels-filter' do - click_button 'Label' - wait_for_ajax - click_link 'bug' - find('.dropdown-menu-close').click - end - - page.within '.filtered-labels' do - expect(page).to have_content 'bug' - end + input_filtered_search('label:~bug') end it 'allows user to remove filtered labels' do - first('.js-label-filter-remove').click - wait_for_ajax + first('.clear-search').click + filtered_search.send_keys(:enter) - expect(find('.filtered-labels', visible: false)).not_to have_content 'bug' - expect(find('.labels-filter')).not_to have_content 'bug' + expect(page).to have_issuable_counts(open: 3, closed: 0, all: 3) + expect(page).to have_content "Bugfix2" + expect(page).to have_content "Feature1" + expect(page).to have_content "Bugfix1" end end - context 'dropdown filtering' do + context 'filter dropdown' do it 'filters by label name' do - page.within '.labels-filter' do - click_button 'Label' - wait_for_ajax - find('.dropdown-input input').set 'bug' - - page.within '.dropdown-content' do - expect(page).not_to have_content 'enhancement' - expect(page).to have_content 'bug' - end - end - end - end + init_label_search + filtered_search.send_keys('~bug') - def select_labels(*labels) - page.find('.js-label-select').click - wait_for_ajax - labels.each do |label| - execute_script("$('.dropdown-menu-labels li:contains(\"#{label}\") a').click()") + page.within '.filter-dropdown' do + expect(page).not_to have_content 'enhancement' + expect(page).to have_content 'bug' + end end - page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click - wait_for_ajax end end diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb index f6e9230c8da..5608cda28f8 100644 --- a/spec/features/merge_requests/filter_by_milestone_spec.rb +++ b/spec/features/merge_requests/filter_by_milestone_spec.rb @@ -1,10 +1,18 @@ require 'rails_helper' feature 'Merge Request filtering by Milestone', feature: true do + include FilteredSearchHelpers + include MergeRequestHelpers + let(:project) { create(:project, :public) } let!(:user) { create(:user)} let(:milestone) { create(:milestone, project: project) } + def filter_by_milestone(title) + find(".js-milestone-select").click + find(".milestone-filter a", text: title).click + end + before do project.team << [user, :master] login_as(user) @@ -15,42 +23,42 @@ feature 'Merge Request filtering by Milestone', feature: true do create(:merge_request, :simple, source_project: project, milestone: milestone) visit_merge_requests(project) - filter_by_milestone(Milestone::None.title) + input_filtered_search('milestone:none') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_css('.merge-request', count: 1) end context 'filters by upcoming milestone', js: true do - it 'does not show issues with no expiry' do + it 'does not show merge requests with no expiry' do create(:merge_request, :with_diffs, source_project: project) create(:merge_request, :simple, source_project: project, milestone: milestone) visit_merge_requests(project) - filter_by_milestone(Milestone::Upcoming.title) + input_filtered_search('milestone:upcoming') expect(page).to have_css('.merge-request', count: 0) end - it 'shows issues in future' do + it 'shows merge requests in future' do milestone = create(:milestone, project: project, due_date: Date.tomorrow) create(:merge_request, :with_diffs, source_project: project) create(:merge_request, :simple, source_project: project, milestone: milestone) visit_merge_requests(project) - filter_by_milestone(Milestone::Upcoming.title) + input_filtered_search('milestone:upcoming') expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_css('.merge-request', count: 1) end - it 'does not show issues in past' do + it 'does not show merge requests in past' do milestone = create(:milestone, project: project, due_date: Date.yesterday) create(:merge_request, :with_diffs, source_project: project) create(:merge_request, :simple, source_project: project, milestone: milestone) visit_merge_requests(project) - filter_by_milestone(Milestone::Upcoming.title) + input_filtered_search('milestone:upcoming') expect(page).to have_css('.merge-request', count: 0) end @@ -61,7 +69,7 @@ feature 'Merge Request filtering by Milestone', feature: true do create(:merge_request, :simple, source_project: project) visit_merge_requests(project) - filter_by_milestone(milestone.title) + input_filtered_search("milestone:%'#{milestone.title}'") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_css('.merge-request', count: 1) @@ -77,19 +85,10 @@ feature 'Merge Request filtering by Milestone', feature: true do create(:merge_request, :simple, source_project: project) visit_merge_requests(project) - filter_by_milestone(milestone.title) + input_filtered_search("milestone:%\"#{milestone.title}\"") expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_css('.merge-request', count: 1) end end - - def visit_merge_requests(project) - visit namespace_project_merge_requests_path(project.namespace, project) - end - - def filter_by_milestone(title) - find(".js-milestone-select").click - find(".milestone-filter a", text: title).click - end end diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb index 4642b5a530d..6579a88d4ab 100644 --- a/spec/features/merge_requests/filter_merge_requests_spec.rb +++ b/spec/features/merge_requests/filter_merge_requests_spec.rb @@ -1,11 +1,13 @@ require 'rails_helper' describe 'Filter merge requests', feature: true do + include FilteredSearchHelpers + include MergeRequestHelpers include WaitForAjax let!(:project) { create(:project) } let!(:group) { create(:group) } - let!(:user) { create(:user)} + let!(:user) { create(:user) } let!(:milestone) { create(:milestone, project: project) } let!(:label) { create(:label, project: project) } let!(:wontfix) { create(:label, project: project, title: "Won't fix") } @@ -15,183 +17,134 @@ describe 'Filter merge requests', feature: true do group.add_developer(user) login_as(user) create(:merge_request, source_project: project, target_project: project) + + visit namespace_project_merge_requests_path(project.namespace, project) end describe 'for assignee from mr#index' do - before do - visit namespace_project_merge_requests_path(project.namespace, project) + let(:search_query) { "assignee:@#{user.username}" } - find('.js-assignee-search').click - - find('.dropdown-menu-user-link', text: user.username).click + before do + input_filtered_search(search_query) - wait_for_ajax + expect_mr_list_count(0) end context 'assignee', js: true do it 'updates to current user' do - expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) + expect_filtered_search_input(search_query) end it 'does not change when closed link is clicked' do find('.issues-state-filters a', text: "Closed").click - expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) + expect_filtered_search_input(search_query) end it 'does not change when all link is clicked' do find('.issues-state-filters a', text: "All").click - expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) + expect_filtered_search_input(search_query) end end end describe 'for milestone from mr#index' do - before do - visit namespace_project_merge_requests_path(project.namespace, project) - - find('.js-milestone-select').click + let(:search_query) { "milestone:%#{milestone.title}" } - find('.milestone-filter .dropdown-content a', text: milestone.title).click + before do + input_filtered_search(search_query) - wait_for_ajax + expect_mr_list_count(0) end context 'milestone', js: true do it 'updates to current milestone' do - expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title) + expect_filtered_search_input(search_query) end it 'does not change when closed link is clicked' do find('.issues-state-filters a', text: "Closed").click - expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title) + expect_filtered_search_input(search_query) end it 'does not change when all link is clicked' do find('.issues-state-filters a', text: "All").click - expect(find('.js-milestone-select .dropdown-toggle-text')).to have_content(milestone.title) + expect_filtered_search_input(search_query) end end end describe 'for label from mr#index', js: true do - before do - visit namespace_project_merge_requests_path(project.namespace, project) - find('.js-label-select').click - wait_for_ajax - end - - it 'filters by any label' do - find('.dropdown-menu-labels a', text: 'Any Label').click - page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click - wait_for_ajax - - expect(find('.labels-filter')).to have_content 'Label' - end - it 'filters by no label' do - find('.dropdown-menu-labels a', text: 'No Label').click - page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click - wait_for_ajax + input_filtered_search('label:none') - page.within '.labels-filter' do - expect(page).to have_content 'Labels' - end - expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Labels') + expect_mr_list_count(1) + expect_filtered_search_input('label:none') end it 'filters by a label' do - find('.dropdown-menu-labels a', text: label.title).click - page.within '.labels-filter' do - expect(page).to have_content label.title - end - expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title) + input_filtered_search("label:~#{label.title}") + + expect_mr_list_count(0) + expect_filtered_search_input("label:~#{label.title}") end it "filters by `won't fix` and another label" do - page.within '.labels-filter' do - click_link wontfix.title - expect(page).to have_content wontfix.title - click_link label.title - end + input_filtered_search("label:~\"#{wontfix.title}\" label:~#{label.title}") - expect(find('.js-label-select .dropdown-toggle-text')).to have_content("#{wontfix.title} +1 more") + expect_mr_list_count(0) + expect_filtered_search_input("label:~\"#{wontfix.title}\" label:~#{label.title}") end it "filters by `won't fix` label followed by another label after page load" do - page.within '.labels-filter' do - click_link wontfix.title - expect(page).to have_content wontfix.title - end - - find('body').click - - expect(find('.filtered-labels')).to have_content(wontfix.title) - - find('.js-label-select').click - wait_for_ajax - find('.dropdown-menu-labels a', text: label.title).click - - find('body').click + input_filtered_search("label:~\"#{wontfix.title}\"") - expect(find('.filtered-labels')).to have_content(wontfix.title) - expect(find('.filtered-labels')).to have_content(label.title) + expect_mr_list_count(0) + expect_filtered_search_input("label:~\"#{wontfix.title}\"") - find('.js-label-select').click - wait_for_ajax + input_filtered_search_keys(" label:~#{label.title}") - expect(find('.dropdown-menu-labels li', text: wontfix.title)).to have_css('.is-active') - expect(find('.dropdown-menu-labels li', text: label.title)).to have_css('.is-active') - end + expect_filtered_search_input("label:~\"#{wontfix.title}\" label:~#{label.title}") - it "selects and unselects `won't fix`" do - find('.dropdown-menu-labels a', text: wontfix.title).click - find('.dropdown-menu-labels a', text: wontfix.title).click - # Close label dropdown to load - find('body').click - expect(page).not_to have_css('.filtered-labels') + expect_mr_list_count(0) + expect_filtered_search_input("label:~\"#{wontfix.title}\" label:~#{label.title}") end end describe 'for assignee and label from issues#index' do - before do - visit namespace_project_merge_requests_path(project.namespace, project) - - find('.js-assignee-search').click + let(:search_query) { "assignee:@#{user.username} label:~#{label.title}" } - find('.dropdown-menu-user-link', text: user.username).click + before do + input_filtered_search("assignee:@#{user.username}") - expect(page).not_to have_selector('.mr-list .merge-request') + expect_mr_list_count(1) + expect_filtered_search_input("assignee:@#{user.username}") - find('.js-label-select').click + input_filtered_search_keys(" label:~#{label.title}") - find('.dropdown-menu-labels .dropdown-content a', text: label.title).click - page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + expect_mr_list_count(1) - wait_for_ajax + find("#state-opened[href=\"#{URI.parse(current_url).path}?assignee_username=#{user.username}&label_name%5B%5D=#{label.title}&scope=all&state=opened\"]") end context 'assignee and label', js: true do it 'updates to current assignee and label' do - expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) - expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title) + expect_filtered_search_input(search_query) end it 'does not change when closed link is clicked' do find('.issues-state-filters a', text: "Closed").click - expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) - expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title) + expect_filtered_search_input(search_query) end it 'does not change when all link is clicked' do find('.issues-state-filters a', text: "All").click - expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) - expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title) + expect_filtered_search_input(search_query) end end end @@ -203,11 +156,11 @@ describe 'Filter merge requests', feature: true do bug_label = create(:label, project: project, title: 'bug') milestone = create(:milestone, title: "8", project: project) - mr = create(:merge_request, - title: "Bug 2", - source_project: project, - target_project: project, - source_branch: "bug2", + mr = create(:merge_request, + title: "Bug 2", + source_project: project, + target_project: project, + source_branch: "bug2", milestone: milestone, author: user, assignee: user) @@ -218,15 +171,13 @@ describe 'Filter merge requests', feature: true do context 'only text', js: true do it 'filters merge requests by searched text' do - fill_in 'issuable_search', with: 'Bug' + input_filtered_search('bug') - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 2) - end + expect_mr_list_count(2) end it 'does not show any merge requests' do - fill_in 'issuable_search', with: 'testing' + input_filtered_search('testing') page.within '.mr-list' do expect(page).not_to have_selector('.merge-request') @@ -234,82 +185,49 @@ describe 'Filter merge requests', feature: true do end end - context 'text and dropdown options', js: true do + context 'filters and searches', js: true do it 'filters by text and label' do - fill_in 'issuable_search', with: 'Bug' + input_filtered_search('Bug') - expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 2) - end + expect_mr_list_count(2) + expect_filtered_search_input('Bug') - click_button 'Label' - page.within '.labels-filter' do - click_link 'bug' - end - find('.dropdown-menu-close-icon').click + input_filtered_search_keys(' label:~bug') - expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 1) - end + expect_mr_list_count(1) end it 'filters by text and milestone' do - fill_in 'issuable_search', with: 'Bug' + input_filtered_search('Bug') - expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 2) - end + expect_mr_list_count(2) + expect_filtered_search_input('Bug') - click_button 'Milestone' - page.within '.milestone-filter' do - click_link '8' - end + input_filtered_search_keys(' milestone:%8') - expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 1) - end + expect_mr_list_count(1) end it 'filters by text and assignee' do - fill_in 'issuable_search', with: 'Bug' + input_filtered_search('Bug') - expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 2) - end + expect_mr_list_count(2) + expect_filtered_search_input('Bug') - click_button 'Assignee' - page.within '.dropdown-menu-assignee' do - click_link user.name - end + input_filtered_search_keys(" assignee:@#{user.username}") - expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 1) - end + expect_mr_list_count(1) end it 'filters by text and author' do - fill_in 'issuable_search', with: 'Bug' + input_filtered_search('Bug') - expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 2) - end + expect_mr_list_count(2) + expect_filtered_search_input('Bug') - click_button 'Author' - page.within '.dropdown-menu-author' do - click_link user.name - end + input_filtered_search_keys(" author:@#{user.username}") - expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 1) - end + expect_mr_list_count(1) end end end @@ -328,18 +246,9 @@ describe 'Filter merge requests', feature: true do end it 'is able to filter and sort merge requests' do - click_button 'Label' - wait_for_ajax - page.within '.labels-filter' do - click_link 'bug' - end - find('.dropdown-menu-close-icon').click - wait_for_ajax + input_filtered_search('label:~bug') - expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) - page.within '.mr-list' do - expect(page).to have_selector('.merge-request', count: 2) - end + expect_mr_list_count(2) click_button 'Last created' page.within '.dropdown-menu-sort' do @@ -352,4 +261,38 @@ describe 'Filter merge requests', feature: true do end end end + + describe 'filter by assignee id', js: true do + it 'filter by current user' do + visit namespace_project_merge_requests_path(project.namespace, project, assignee_id: user.id) + + expect_filtered_search_input("assignee:@#{user.username}") + end + + it 'filter by new user' do + new_user = create(:user) + project.add_developer(new_user) + + visit namespace_project_merge_requests_path(project.namespace, project, assignee_id: new_user.id) + + expect_filtered_search_input("assignee:@#{new_user.username}") + end + end + + describe 'filter by author id', js: true do + it 'filter by current user' do + visit namespace_project_merge_requests_path(project.namespace, project, author_id: user.id) + + expect_filtered_search_input("author:@#{user.username}") + end + + it 'filter by new user' do + new_user = create(:user) + project.add_developer(new_user) + + visit namespace_project_merge_requests_path(project.namespace, project, author_id: new_user.id) + + expect_filtered_search_input("author:@#{new_user.username}") + end + end end diff --git a/spec/features/merge_requests/reset_filters_spec.rb b/spec/features/merge_requests/reset_filters_spec.rb index 3a7ece7e1d6..58f11499e3f 100644 --- a/spec/features/merge_requests/reset_filters_spec.rb +++ b/spec/features/merge_requests/reset_filters_spec.rb @@ -1,17 +1,20 @@ require 'rails_helper' feature 'Issues filter reset button', feature: true, js: true do + include FilteredSearchHelpers + include MergeRequestHelpers include WaitForAjax include IssueHelpers - let!(:project) { create(:project, :public) } - let!(:user) { create(:user)} - let!(:milestone) { create(:milestone, project: project) } - let!(:bug) { create(:label, project: project, name: 'bug')} + let!(:project) { create(:project, :public) } + let!(:user) { create(:user) } + let!(:milestone) { create(:milestone, project: project) } + let!(:bug) { create(:label, project: project, name: 'bug')} let!(:mr1) { create(:merge_request, title: "Feature", source_project: project, target_project: project, source_branch: "Feature", milestone: milestone, author: user, assignee: user) } let!(:mr2) { create(:merge_request, title: "Bugfix1", source_project: project, target_project: project, source_branch: "Bugfix1") } - let(:merge_request_css) { '.merge-request' } + let(:merge_request_css) { '.merge-request' } + let(:clear_search_css) { '.filtered-search-input-container .clear-search' } before do mr2.labels << bug @@ -50,7 +53,7 @@ feature 'Issues filter reset button', feature: true, js: true do context 'when author filter has been applied' do it 'resets the author filter' do - visit_merge_requests(project, author_id: user.id) + visit_merge_requests(project, author_username: user.username) expect(page).to have_css(merge_request_css, count: 1) reset_filters @@ -60,7 +63,7 @@ feature 'Issues filter reset button', feature: true, js: true do context 'when assignee filter has been applied' do it 'resets the assignee filter' do - visit_merge_requests(project, assignee_id: user.id) + visit_merge_requests(project, assignee_username: user.username) expect(page).to have_css(merge_request_css, count: 1) reset_filters @@ -70,7 +73,7 @@ feature 'Issues filter reset button', feature: true, js: true do context 'when all filters have been applied' do it 'resets all filters' do - visit_merge_requests(project, assignee_id: user.id, author_id: user.id, milestone_title: milestone.title, label_name: bug.name, search: 'Bug') + visit_merge_requests(project, assignee_username: user.username, author_username: user.username, milestone_title: milestone.title, label_name: bug.name, search: 'Bug') expect(page).to have_css(merge_request_css, count: 0) reset_filters @@ -82,15 +85,7 @@ feature 'Issues filter reset button', feature: true, js: true do it 'the reset link should not be visible' do visit_merge_requests(project) expect(page).to have_css(merge_request_css, count: 2) - expect(page).not_to have_css '.reset_filters' + expect(page).not_to have_css(clear_search_css) end end - - def visit_merge_requests(project, opts = {}) - visit namespace_project_merge_requests_path project.namespace, project, opts - end - - def reset_filters - find('.reset-filters').click - end end diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb index 67a4a5d1ab1..ae9db0c0d6e 100644 --- a/spec/features/projects/badges/list_spec.rb +++ b/spec/features/projects/badges/list_spec.rb @@ -14,7 +14,8 @@ feature 'list of badges' do expect(page).to have_content 'build status' expect(page).to have_content 'Markdown' expect(page).to have_content 'HTML' - expect(page).to have_css('.highlight', count: 2) + expect(page).to have_content 'AsciiDoc' + expect(page).to have_css('.highlight', count: 3) expect(page).to have_xpath("//img[@alt='build status']") page.within('.highlight', match: :first) do @@ -28,7 +29,8 @@ feature 'list of badges' do expect(page).to have_content 'coverage report' expect(page).to have_content 'Markdown' expect(page).to have_content 'HTML' - expect(page).to have_css('.highlight', count: 2) + expect(page).to have_content 'AsciiDoc' + expect(page).to have_css('.highlight', count: 3) expect(page).to have_xpath("//img[@alt='coverage report']") page.within('.highlight', match: :first) do diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb index 64094af29c0..f8ef4577a26 100644 --- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb +++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb @@ -25,7 +25,7 @@ feature 'project owner creates a license file', feature: true, js: true do select_template('MIT License') file_content = first('.file-editor') - expect(file_content).to have_content('The MIT License (MIT)') + expect(file_content).to have_content('MIT License') expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") fill_in :commit_message, with: 'Add a LICENSE file', visible: true @@ -33,7 +33,7 @@ feature 'project owner creates a license file', feature: true, js: true do expect(current_path).to eq( namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) - expect(page).to have_content('The MIT License (MIT)') + expect(page).to have_content('MIT License') expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") end @@ -49,7 +49,7 @@ feature 'project owner creates a license file', feature: true, js: true do select_template('MIT License') file_content = first('.file-editor') - expect(file_content).to have_content('The MIT License (MIT)') + expect(file_content).to have_content('MIT License') expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") fill_in :commit_message, with: 'Add a LICENSE file', visible: true @@ -57,7 +57,7 @@ feature 'project owner creates a license file', feature: true, js: true do expect(current_path).to eq( namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) - expect(page).to have_content('The MIT License (MIT)') + expect(page).to have_content('MIT License') expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") end diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb index 4453b6d485f..420db962318 100644 --- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb +++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb @@ -24,7 +24,7 @@ feature 'project owner sees a link to create a license file in empty project', f select_template('MIT License') file_content = first('.file-editor') - expect(file_content).to have_content('The MIT License (MIT)') + expect(file_content).to have_content('MIT License') expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") fill_in :commit_message, with: 'Add a LICENSE file', visible: true @@ -34,7 +34,7 @@ feature 'project owner sees a link to create a license file in empty project', f expect(current_path).to eq( namespace_project_blob_path(project.namespace, project, 'master/LICENSE')) - expect(page).to have_content('The MIT License (MIT)') + expect(page).to have_content('MIT License') expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}") end diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 16dddb2a86b..40caf89dd54 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -9,7 +9,7 @@ feature 'Import/Export - project export integration test', feature: true, js: tr include ExportFileHelper let(:user) { create(:admin) } - let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } + let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } let(:sensitive_words) { %w[pass secret token key] } diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 3015576f6f8..2d1106ea3e8 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -4,7 +4,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr include Select2Helper let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } - let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } + let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } background do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb index d0bafc6168c..cb399ea55df 100644 --- a/spec/features/projects/import_export/namespace_export_file_spec.rb +++ b/spec/features/projects/import_export/namespace_export_file_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'Import/Export - Namespace export file cleanup', feature: true, js: true do - let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } + let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } let(:project) { create(:empty_project) } diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb index 81b0c991d4f..7414ce21f59 100644 --- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb +++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb @@ -37,7 +37,7 @@ feature 'Issue prioritization', feature: true do page.within('.issues-holder') do issue_titles = all('.issues-list .issue-title-text').map(&:text) - expect(issue_titles).to eq(['issue_4', 'issue_3', 'issue_5', 'issue_2', 'issue_1']) + expect(issue_titles).to eq(%w(issue_4 issue_3 issue_5 issue_2 issue_1)) end end end @@ -77,7 +77,7 @@ feature 'Issue prioritization', feature: true do expect(issue_titles[0..1]).to contain_exactly('issue_5', 'issue_8') expect(issue_titles[2..4]).to contain_exactly('issue_1', 'issue_3', 'issue_7') - expect(issue_titles[5..-1]).to eq(['issue_2', 'issue_4', 'issue_6']) + expect(issue_titles[5..-1]).to eq(%w(issue_2 issue_4 issue_6)) end end end diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index b56e562b2b6..45185f2dd1f 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -19,6 +19,51 @@ feature "New project", feature: true do end end + context "Namespace selector" do + context "with user namespace" do + before do + visit new_project_path + end + + it "selects the user namespace" do + namespace = find("#project_namespace_id") + + expect(namespace.text).to eq user.username + end + end + + context "with group namespace" do + let(:group) { create(:group, :private, owner: user) } + + before do + group.add_owner(user) + visit new_project_path(namespace_id: group.id) + end + + it "selects the group namespace" do + namespace = find("#project_namespace_id option[selected]") + + expect(namespace.text).to eq group.name + end + + context "on validation error" do + before do + fill_in('project_path', with: 'private-group-project') + choose('Internal') + click_button('Create project') + + expect(page).to have_css '.project-edit-errors .alert.alert-danger' + end + + it "selects the group namespace" do + namespace = find("#project_namespace_id option[selected]") + + expect(namespace.text).to eq group.name + end + end + end + end + context 'Import project options' do before do visit new_project_path diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 0b5ccc8c515..9f06e52ab55 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -54,7 +54,7 @@ describe 'Pipeline', :feature, :js do expect(page).to have_content('Build') expect(page).to have_content('Test') expect(page).to have_content('Deploy') - expect(page).to have_content('Retry failed') + expect(page).to have_content('Retry') expect(page).to have_content('Cancel running') end @@ -164,9 +164,9 @@ describe 'Pipeline', :feature, :js do it { expect(page).not_to have_content('retried') } context 'when retrying' do - before { click_on 'Retry failed' } + before { find('.js-retry-button').trigger('click') } - it { expect(page).not_to have_content('Retry failed') } + it { expect(page).not_to have_content('Retry') } end end @@ -198,7 +198,7 @@ describe 'Pipeline', :feature, :js do expect(page).to have_content(build_failed.id) expect(page).to have_content(build_running.id) expect(page).to have_content(build_external.id) - expect(page).to have_content('Retry failed') + expect(page).to have_content('Retry') expect(page).to have_content('Cancel running') expect(page).to have_link('Play') end @@ -226,9 +226,9 @@ describe 'Pipeline', :feature, :js do it { expect(page).not_to have_content('retried') } context 'when retrying' do - before { click_on 'Retry failed' } + before { find('.js-retry-button').trigger('click') } - it { expect(page).not_to have_content('Retry failed') } + it { expect(page).not_to have_content('Retry') } it { expect(page).to have_selector('.retried') } end end diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 8d1214dedb4..592dc4483d2 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -26,22 +26,66 @@ describe 'Pipelines', :feature, :js do ) end - [:all, :running, :branches].each do |scope| - context "when displaying #{scope}" do - before do - visit_project_pipelines(scope: scope) - end + context 'scope' do + before do + create(:ci_empty_pipeline, status: 'pending', project: project, sha: project.commit.id, ref: 'master') + create(:ci_empty_pipeline, status: 'running', project: project, sha: project.commit.id, ref: 'master') + create(:ci_empty_pipeline, status: 'created', project: project, sha: project.commit.id, ref: 'master') + create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master') + end - it 'contains pipeline commit short SHA' do - expect(page).to have_content(pipeline.short_sha) - end + [:all, :running, :pending, :finished, :branches].each do |scope| + context "when displaying #{scope}" do + before do + visit_project_pipelines(scope: scope) + end + + it 'contains pipeline commit short SHA' do + expect(page).to have_content(pipeline.short_sha) + end - it 'contains branch name' do - expect(page).to have_content(pipeline.ref) + it 'contains branch name' do + expect(page).to have_content(pipeline.ref) + end end end end + context 'header tabs' do + before do + visit namespace_project_pipelines_path(project.namespace, project) + wait_for_vue_resource + end + + it 'shows a tab for All pipelines and count' do + expect(page.find('.js-pipelines-tab-all a').text).to include('All') + expect(page.find('.js-pipelines-tab-all .badge').text).to include('1') + end + + it 'shows a tab for Pending pipelines and count' do + expect(page.find('.js-pipelines-tab-pending a').text).to include('Pending') + expect(page.find('.js-pipelines-tab-pending .badge').text).to include('0') + end + + it 'shows a tab for Running pipelines and count' do + expect(page.find('.js-pipelines-tab-running a').text).to include('Running') + expect(page.find('.js-pipelines-tab-running .badge').text).to include('1') + end + + it 'shows a tab for Finished pipelines and count' do + expect(page.find('.js-pipelines-tab-finished a').text).to include('Finished') + expect(page.find('.js-pipelines-tab-finished .badge').text).to include('0') + end + + it 'shows a tab for Branches' do + expect(page.find('.js-pipelines-tab-branches a').text).to include('Branches') + end + + it 'shows a tab for Tags' do + expect(page.find('.js-pipelines-tab-tags a').text).to include('Tags') + end + end + context 'when pipeline is cancelable' do let!(:build) do create(:ci_build, pipeline: pipeline, diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb index 4eafac1acd8..3b8f0b2d3f8 100644 --- a/spec/features/projects/ref_switcher_spec.rb +++ b/spec/features/projects/ref_switcher_spec.rb @@ -20,6 +20,8 @@ feature 'Ref switcher', feature: true, js: true do input.set 'binary' wait_for_ajax + expect(find('.dropdown-content ul')).to have_selector('li', count: 6) + page.within '.dropdown-content ul' do input.native.send_keys :enter end diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 0fe5a897565..7da05defa81 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -186,7 +186,7 @@ describe "Search", feature: true do sleep 2 expect(page).to have_selector('.merge-requests-holder') - expect(find('.js-assignee-search .dropdown-toggle-text')).to have_content(user.name) + expect(find('.filtered-search').value).to eq("assignee:@#{user.username}") end it 'takes user to her MR page when MR authored is clicked' do @@ -194,7 +194,7 @@ describe "Search", feature: true do sleep 2 expect(page).to have_selector('.merge-requests-holder') - expect(find('.js-author-search .dropdown-toggle-text')).to have_content(user.name) + expect(find('.filtered-search').value).to eq("author:@#{user.username}") end end diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb index fb19dac1d6a..3495091a0d5 100644 --- a/spec/features/todos/todos_spec.rb +++ b/spec/features/todos/todos_spec.rb @@ -171,6 +171,29 @@ describe 'Dashboard Todos', feature: true do end end + context 'User have large number of todos' do + before do + create_list(:todo, 101, :mentioned, user: user, project: project, target: issue, author: author) + + login_as(user) + visit dashboard_todos_path + end + + it 'shows 99+ for count >= 100 in notification' do + expect(page).to have_selector('.todos-pending-count', text: '99+') + end + + it 'shows exact number in To do tab' do + expect(page).to have_selector('.todos-pending .badge', text: '101') + end + + it 'shows exact number for count < 100' do + 3.times { first('.js-done-todo').click } + + expect(page).to have_selector('.todos-pending-count', text: '98') + end + end + context 'User has a Build Failed todo' do let!(:todo) { create(:todo, :build_failed, user: user, project: project, author: author) } diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index 9a4bc027004..a362d6fd3b6 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'Project variables', js: true do let(:user) { create(:user) } let(:project) { create(:project) } - let(:variable) { create(:ci_variable, key: 'test') } + let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') } before do login_as(user) @@ -24,11 +24,23 @@ describe 'Project variables', js: true do fill_in('variable_value', with: 'key value') click_button('Add new variable') + expect(page).to have_content('Variables were successfully updated.') page.within('.variables-table') do expect(page).to have_content('key') end end + it 'adds empty variable' do + fill_in('variable_key', with: 'new_key') + fill_in('variable_value', with: '') + click_button('Add new variable') + + expect(page).to have_content('Variables were successfully updated.') + page.within('.variables-table') do + expect(page).to have_content('new_key') + end + end + it 'reveals and hides new variable' do fill_in('variable_key', with: 'key') fill_in('variable_value', with: 'key value') @@ -72,8 +84,20 @@ describe 'Project variables', js: true do fill_in('variable_value', with: 'key value') click_button('Save variable') + expect(page).to have_content('Variable was successfully updated.') + expect(project.variables.first.value).to eq('key value') + end + + it 'edits variable with empty value' do page.within('.variables-table') do - expect(page).to have_content('key') + find('.btn-variable-edit').click end + + expect(page).to have_content('Update variable') + fill_in('variable_value', with: '') + click_button('Save variable') + + expect(page).to have_content('Variable was successfully updated.') + expect(project.variables.first.value).to eq('') end end diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb index f8b05d4e9bc..77a04507be1 100644 --- a/spec/finders/notes_finder_spec.rb +++ b/spec/finders/notes_finder_spec.rb @@ -111,7 +111,7 @@ describe NotesFinder do end it 'raises an exception for an invalid target_type' do - params.merge!(target_type: 'invalid') + params[:target_type] = 'invalid' expect { described_class.new(project, user, params).execute }.to raise_error('invalid target_type') end diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb index fdc8215aa47..6bada7b3eb9 100644 --- a/spec/finders/pipelines_finder_spec.rb +++ b/spec/finders/pipelines_finder_spec.rb @@ -39,8 +39,8 @@ describe PipelinesFinder do end end - # Scoping to running will speed up the test as it doesn't hit the FS - let(:params) { { scope: 'running' } } + # Scoping to pending will speed up the test as it doesn't hit the FS + let(:params) { { scope: 'pending' } } it 'orders in descending order on ID' do feature_pipeline = create(:ci_pipeline, project: project, ref: 'feature') diff --git a/spec/fixtures/mail_room_disabled.yml b/spec/fixtures/config/mail_room_disabled.yml index 97f8cff051f..97f8cff051f 100644 --- a/spec/fixtures/mail_room_disabled.yml +++ b/spec/fixtures/config/mail_room_disabled.yml diff --git a/spec/fixtures/mail_room_enabled.yml b/spec/fixtures/config/mail_room_enabled.yml index 9c94649244d..9c94649244d 100644 --- a/spec/fixtures/mail_room_enabled.yml +++ b/spec/fixtures/config/mail_room_enabled.yml diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index 49ea4fa6d3e..cd3281d6f51 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -55,7 +55,7 @@ describe AuthHelper do context 'all the button based providers are disabled via application_setting' do it 'returns false' do stub_application_setting( - disabled_oauth_sign_in_sources: ['github', 'twitter'] + disabled_oauth_sign_in_sources: %w(github twitter) ) expect(helper.button_based_providers_enabled?).to be false diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index 3223556e1d3..cd112dbb2fb 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -43,4 +43,36 @@ describe EmailsHelper do end end end + + describe '#header_logo' do + context 'there is a brand item with a logo' do + it 'returns the brand header logo' do + appearance = create :appearance, header_logo: fixture_file_upload( + Rails.root.join('spec/fixtures/dk.png') + ) + + expect(header_logo).to eq( + %{<img style="height: 50px" src="/uploads/appearance/header_logo/#{appearance.id}/dk.png" alt="Dk" />} + ) + end + end + + context 'there is a brand item without a logo' do + it 'returns the default header logo' do + create :appearance, header_logo: nil + + expect(header_logo).to eq( + %{<img alt="GitLab" src="/images/mailers/gitlab_header_logo.gif" width="55" height="50" />} + ) + end + end + + context 'there is no brand item' do + it 'returns the default header logo' do + expect(header_logo).to eq( + %{<img alt="GitLab" src="/images/mailers/gitlab_header_logo.gif" width="55" height="50" />} + ) + end + end + end end diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index df71680e44c..93bb711f29a 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -51,7 +51,7 @@ describe IssuablesHelper do utf8: '✓', author_id: '11', assignee_id: '18', - label_name: ['bug', 'discussion', 'documentation'], + label_name: %w(bug discussion documentation), milestone_title: 'v4.0', sort: 'due_date_asc', namespace_id: 'gitlab-org', diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 13fb9c1f1a7..88d853935c7 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -55,8 +55,8 @@ describe IssuesHelper do describe "merge_requests_sentence" do subject { merge_requests_sentence(merge_requests)} let(:merge_requests) do - [ build(:merge_request, iid: 1), build(:merge_request, iid: 2), - build(:merge_request, iid: 3)] + [build(:merge_request, iid: 1), build(:merge_request, iid: 2), + build(:merge_request, iid: 3)] end it { is_expected.to eq("!1, !2, or !3") } @@ -113,7 +113,7 @@ describe IssuesHelper do describe "awards_sort" do it "sorts a hash so thumbsup and thumbsdown are always on top" do data = { "thumbsdown" => "some value", "lifter" => "some value", "thumbsup" => "some value" } - expect(awards_sort(data).keys).to eq(["thumbsup", "thumbsdown", "lifter"]) + expect(awards_sort(data).keys).to eq(%w(thumbsup thumbsdown lifter)) end end diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index 4da1569e59f..28b8def331d 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -20,97 +20,97 @@ describe SubmoduleHelper do it 'detects ssh on standard port' do allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(22) # set this just to be sure allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) - stub_url([ config.user, '@', config.host, ':gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url([config.user, '@', config.host, ':gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'detects ssh on non-standard port' do allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(2222) allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix)) - stub_url([ 'ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'detects http on standard port' do allow(Gitlab.config.gitlab).to receive(:port).and_return(80) allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) - stub_url([ 'http://', config.host, '/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['http://', config.host, '/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'detects http on non-standard port' do allow(Gitlab.config.gitlab).to receive(:port).and_return(3000) allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) - stub_url([ 'http://', config.host, ':3000/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['http://', config.host, ':3000/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end it 'works with relative_url_root' do allow(Gitlab.config.gitlab).to receive(:port).and_return(80) # set this just to be sure allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root') allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url)) - stub_url([ 'http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git' ].join('')) - expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ]) + stub_url(['http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git'].join('')) + expect(submodule_links(submodule_item)).to eq([namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash')]) end end context 'submodule on github.com' do it 'detects ssh' do stub_url('git@github.com:gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects http' do stub_url('http://github.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects https' do stub_url('https://github.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://github.com/gitlab-org/gitlab-ce', 'https://github.com/gitlab-org/gitlab-ce/tree/hash']) end it 'returns original with non-standard url' do stub_url('http://github.com/gitlab-org/gitlab-ce') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) stub_url('http://github.com/another/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) end end context 'submodule on gitlab.com' do it 'detects ssh' do stub_url('git@gitlab.com:gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects http' do stub_url('http://gitlab.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash']) end it 'detects https' do stub_url('https://gitlab.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ 'https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash' ]) + expect(submodule_links(submodule_item)).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash']) end it 'returns original with non-standard url' do stub_url('http://gitlab.com/gitlab-org/gitlab-ce') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) stub_url('http://gitlab.com/another/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) end end context 'submodule on unsupported' do it 'returns original' do stub_url('http://mygitserver.com/gitlab-org/gitlab-ce') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) stub_url('http://mygitserver.com/gitlab-org/gitlab-ce.git') - expect(submodule_links(submodule_item)).to eq([ repo.submodule_url_for, nil ]) + expect(submodule_links(submodule_item)).to eq([repo.submodule_url_for, nil]) end end diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb new file mode 100644 index 00000000000..889fe441171 --- /dev/null +++ b/spec/helpers/version_check_helper_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe VersionCheckHelper do + describe '#version_status_badge' do + it 'should return nil if not dev environment and not enabled' do + allow(Rails.env).to receive(:production?) { false } + allow(current_application_settings).to receive(:version_check_enabled) { false } + + expect(helper.version_status_badge).to be(nil) + end + + context 'when production and enabled' do + before do + allow(Rails.env).to receive(:production?) { true } + allow(current_application_settings).to receive(:version_check_enabled) { true } + allow_any_instance_of(VersionCheck).to receive(:url) { 'https://version.host.com/check.svg?gitlab_info=xxx' } + + @image_tag = helper.version_status_badge + end + + it 'should return an image tag' do + expect(@image_tag).to match(/^<img/) + end + + it 'should have a js prefixed css class' do + expect(@image_tag).to match(/class="js-version-status-badge"/) + end + + it 'should have a VersionCheck url as the src' do + expect(@image_tag).to match(/src="https:\/\/version\.host\.com\/check\.svg\?gitlab_info=xxx"/) + end + end + end +end diff --git a/spec/initializers/trusted_proxies_spec.rb b/spec/initializers/trusted_proxies_spec.rb index 290e47763eb..ff8b8daa347 100644 --- a/spec/initializers/trusted_proxies_spec.rb +++ b/spec/initializers/trusted_proxies_spec.rb @@ -27,7 +27,7 @@ describe 'trusted_proxies', lib: true do context 'with private IP ranges added' do before do - set_trusted_proxies([ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ]) + set_trusted_proxies(["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]) end it 'filters out private and local IPs' do @@ -39,7 +39,7 @@ describe 'trusted_proxies', lib: true do context 'with proxy IP added' do before do - set_trusted_proxies([ "60.98.25.47" ]) + set_trusted_proxies(["60.98.25.47"]) end it 'filters out proxy IP' do diff --git a/spec/javascripts/.eslintrc b/spec/javascripts/.eslintrc index fbd9bb9f0ff..3d922021978 100644 --- a/spec/javascripts/.eslintrc +++ b/spec/javascripts/.eslintrc @@ -18,7 +18,8 @@ "sandbox": false, "setFixtures": false, "setStyleFixtures": false, - "spyOnEvent": false + "spyOnEvent": false, + "ClassSpecHelper": false }, "plugins": ["jasmine"], "rules": { diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js new file mode 100644 index 00000000000..a68bccb16f4 --- /dev/null +++ b/spec/javascripts/ajax_loading_spinner_spec.js @@ -0,0 +1,58 @@ +require('~/extensions/array'); +require('jquery'); +require('jquery-ujs'); +require('~/ajax_loading_spinner'); + +describe('Ajax Loading Spinner', () => { + const fixtureTemplate = 'static/ajax_loading_spinner.html.raw'; + preloadFixtures(fixtureTemplate); + + beforeEach(() => { + loadFixtures(fixtureTemplate); + gl.AjaxLoadingSpinner.init(); + }); + + it('change current icon with spinner icon and disable link while waiting ajax response', (done) => { + spyOn(jQuery, 'ajax').and.callFake((req) => { + const xhr = new XMLHttpRequest(); + const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); + const icon = ajaxLoadingSpinner.querySelector('i'); + + req.beforeSend(xhr, { dataType: 'text/html' }); + + expect(icon).not.toHaveClass('fa-trash-o'); + expect(icon).toHaveClass('fa-spinner'); + expect(icon).toHaveClass('fa-spin'); + expect(icon.dataset.icon).toEqual('fa-trash-o'); + expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(''); + + req.complete({}); + + done(); + const deferred = $.Deferred(); + return deferred.promise(); + }); + document.querySelector('.js-ajax-loading-spinner').click(); + }); + + it('use original icon again and enabled the link after complete the ajax request', (done) => { + spyOn(jQuery, 'ajax').and.callFake((req) => { + const xhr = new XMLHttpRequest(); + const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); + + req.beforeSend(xhr, { dataType: 'text/html' }); + req.complete({}); + + const icon = ajaxLoadingSpinner.querySelector('i'); + expect(icon).toHaveClass('fa-trash-o'); + expect(icon).not.toHaveClass('fa-spinner'); + expect(icon).not.toHaveClass('fa-spin'); + expect(ajaxLoadingSpinner.getAttribute('disabled')).toEqual(null); + + done(); + const deferred = $.Deferred(); + return deferred.promise(); + }); + document.querySelector('.js-ajax-loading-spinner').click(); + }); +}); diff --git a/spec/javascripts/fixtures/ajax_loading_spinner.html.haml b/spec/javascripts/fixtures/ajax_loading_spinner.html.haml new file mode 100644 index 00000000000..09d8c9df3b2 --- /dev/null +++ b/spec/javascripts/fixtures/ajax_loading_spinner.html.haml @@ -0,0 +1,2 @@ +%a.js-ajax-loading-spinner{href: "http://goesnowhere.nothing/whereami", data: {remote: true}} + %i.fa.fa-trash-o diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js index 576b6cfefdc..46a27b8c98f 100644 --- a/spec/javascripts/header_spec.js +++ b/spec/javascripts/header_spec.js @@ -45,8 +45,8 @@ require('~/lib/utils/text_utility'); expect(isTodosCountHidden()).toEqual(false); }); - it('should add delimiter to todos-pending-count', function() { - expect($(todosPendingCount).text()).toEqual('1,000'); + it('should show 99+ for todos-pending-count', function() { + expect($(todosPendingCount).text()).toEqual('99+'); }); }); }); diff --git a/spec/javascripts/helpers/class_spec_helper.js.es6 b/spec/javascripts/helpers/class_spec_helper.js.es6 index d3c37d39431..61db27a8fcc 100644 --- a/spec/javascripts/helpers/class_spec_helper.js.es6 +++ b/spec/javascripts/helpers/class_spec_helper.js.es6 @@ -7,3 +7,5 @@ class ClassSpecHelper { } window.ClassSpecHelper = ClassSpecHelper; + +module.exports = ClassSpecHelper; diff --git a/spec/javascripts/lib/utils/text_utility_spec.js.es6 b/spec/javascripts/lib/utils/text_utility_spec.js.es6 index 86ade66ec29..06b69b8ac17 100644 --- a/spec/javascripts/lib/utils/text_utility_spec.js.es6 +++ b/spec/javascripts/lib/utils/text_utility_spec.js.es6 @@ -35,5 +35,16 @@ require('~/lib/utils/text_utility'); expect(gl.text.pluralize('test', 1)).toBe('test'); }); }); + + describe('gl.text.highCountTrim', () => { + it('returns 99+ for count >= 100', () => { + expect(gl.text.highCountTrim(105)).toBe('99+'); + expect(gl.text.highCountTrim(100)).toBe('99+'); + }); + + it('returns exact number for count < 100', () => { + expect(gl.text.highCountTrim(45)).toBe(45); + }); + }); }); })(); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index cb8754581c0..aaf058bd755 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -89,8 +89,8 @@ require('vendor/fuzzaldrin-plus'); var a1, a2, a3, a4, issuesAssignedToMeLink, issuesIHaveCreatedLink, mrsAssignedToMeLink, mrsIHaveCreatedLink; issuesAssignedToMeLink = issuesPath + "/?assignee_username=" + userName; issuesIHaveCreatedLink = issuesPath + "/?author_username=" + userName; - mrsAssignedToMeLink = mrsPath + "/?assignee_id=" + userId; - mrsIHaveCreatedLink = mrsPath + "/?author_id=" + userId; + mrsAssignedToMeLink = mrsPath + "/?assignee_username=" + userName; + mrsIHaveCreatedLink = mrsPath + "/?author_username=" + userName; a1 = "a[href='" + issuesAssignedToMeLink + "']"; a2 = "a[href='" + issuesIHaveCreatedLink + "']"; a3 = "a[href='" + mrsAssignedToMeLink + "']"; diff --git a/spec/javascripts/version_check_image_spec.js.es6 b/spec/javascripts/version_check_image_spec.js.es6 new file mode 100644 index 00000000000..464c1fce210 --- /dev/null +++ b/spec/javascripts/version_check_image_spec.js.es6 @@ -0,0 +1,33 @@ +const ClassSpecHelper = require('./helpers/class_spec_helper'); +const VersionCheckImage = require('~/version_check_image'); +require('jquery'); + +describe('VersionCheckImage', function () { + describe('.bindErrorEvent', function () { + ClassSpecHelper.itShouldBeAStaticMethod(VersionCheckImage, 'bindErrorEvent'); + + beforeEach(function () { + this.imageElement = $('<div></div>'); + }); + + it('registers an error event', function () { + spyOn($.prototype, 'on'); + spyOn($.prototype, 'off').and.callFake(function () { return this; }); + + VersionCheckImage.bindErrorEvent(this.imageElement); + + expect($.prototype.off).toHaveBeenCalledWith('error'); + expect($.prototype.on).toHaveBeenCalledWith('error', jasmine.any(Function)); + }); + + it('hides the imageElement on error', function () { + spyOn($.prototype, 'hide'); + + VersionCheckImage.bindErrorEvent(this.imageElement); + + this.imageElement.trigger('error'); + + expect($.prototype.hide).toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/lib/bitbucket/collection_spec.rb b/spec/lib/bitbucket/collection_spec.rb index 015a7f80e03..9008cb3e870 100644 --- a/spec/lib/bitbucket/collection_spec.rb +++ b/spec/lib/bitbucket/collection_spec.rb @@ -19,6 +19,6 @@ describe Bitbucket::Collection do it "iterates paginator" do collection = described_class.new(TestPaginator.new) - expect(collection.to_a).to match(["result_1_page_1", "result_2_page_1", "result_1_page_2", "result_2_page_2"]) + expect(collection.to_a).to match(%w(result_1_page_1 result_2_page_1 result_1_page_2 result_2_page_2)) end end diff --git a/spec/lib/bitbucket/representation/repo_spec.rb b/spec/lib/bitbucket/representation/repo_spec.rb index adcd978e1b3..405265cc669 100644 --- a/spec/lib/bitbucket/representation/repo_spec.rb +++ b/spec/lib/bitbucket/representation/repo_spec.rb @@ -29,7 +29,7 @@ describe Bitbucket::Representation::Repo do end describe '#owner_and_slug' do - it { expect(described_class.new({ 'full_name' => 'ben/test' }).owner_and_slug).to eq(['ben', 'test']) } + it { expect(described_class.new({ 'full_name' => 'ben/test' }).owner_and_slug).to eq(%w(ben test)) } end describe '#owner' do @@ -42,7 +42,7 @@ describe Bitbucket::Representation::Repo do describe '#clone_url' do it 'builds url' do - data = { 'links' => { 'clone' => [ { 'name' => 'https', 'href' => 'https://bibucket.org/test/test.git' }] } } + data = { 'links' => { 'clone' => [{ 'name' => 'https', 'href' => 'https://bibucket.org/test/test.git' }] } } expect(described_class.new(data).clone_url('abc')).to eq('https://x-token-auth:abc@bibucket.org/test/test.git') end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 68ad429608d..7145f0da1d3 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -96,7 +96,7 @@ module Ci it "returns builds if only has a list of branches including specified" do config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: type, only: ["master", "deploy"] } + rspec: { script: "rspec", type: type, only: %w(master deploy) } }) config_processor = GitlabCiYamlProcessor.new(config, path) @@ -173,8 +173,8 @@ module Ci it "returns build only for specified type" do config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: "test", only: ["master", "deploy"] }, - staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + rspec: { script: "rspec", type: "test", only: %w(master deploy) }, + staging: { script: "deploy", type: "deploy", only: %w(master deploy) }, production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] }, }) @@ -252,7 +252,7 @@ module Ci it "does not return builds if except has a list of branches including specified" do config = YAML.dump({ before_script: ["pwd"], - rspec: { script: "rspec", type: type, except: ["master", "deploy"] } + rspec: { script: "rspec", type: type, except: %w(master deploy) } }) config_processor = GitlabCiYamlProcessor.new(config, path) @@ -580,7 +580,7 @@ module Ci context 'when syntax is incorrect' do context 'when variables defined but invalid' do let(:variables) do - [ 'VAR1', 'value1', 'VAR2', 'value2' ] + %w(VAR1 value1 VAR2 value2) end it 'raises error' do @@ -909,7 +909,7 @@ module Ci end context 'dependencies to builds' do - let(:dependencies) { ['build1', 'build2'] } + let(:dependencies) { %w(build1 build2) } it { expect { subject }.not_to raise_error } end @@ -1215,7 +1215,7 @@ EOT end it "returns errors if job stage is not a defined stage" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance" } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", type: "acceptance" } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") @@ -1257,42 +1257,42 @@ EOT end it "returns errors if job artifacts:name is not an a string" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { name: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { name: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts name should be a string") end it "returns errors if job artifacts:when is not an a predefined value" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { when: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { when: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts when should be on_success, on_failure or always") end it "returns errors if job artifacts:expire_in is not an a string" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { expire_in: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration") end it "returns errors if job artifacts:expire_in is not an a valid duration" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { expire_in: "7 elephants" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts expire in should be a duration") end it "returns errors if job artifacts:untracked is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { untracked: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { untracked: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts untracked should be a boolean value") end it "returns errors if job artifacts:paths is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { paths: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", artifacts: { paths: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:artifacts paths should be an array of strings") @@ -1320,28 +1320,28 @@ EOT end it "returns errors if job cache:key is not an a string" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { key: 1 } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: 1 } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:key config should be a string or symbol") end it "returns errors if job cache:untracked is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { untracked: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { untracked: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:untracked config should be a boolean value") end it "returns errors if job cache:paths is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { paths: "string" } } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { paths: "string" } } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:cache:paths config should be an array of strings") end it "returns errors if job dependencies is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", dependencies: "string" } }) + config = YAML.dump({ types: %w(build test), rspec: { script: "test", dependencies: "string" } }) expect do GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec dependencies should be an array of strings") diff --git a/spec/lib/expand_variables_spec.rb b/spec/lib/expand_variables_spec.rb index 90bc7dad379..730ca1f7c0a 100644 --- a/spec/lib/expand_variables_spec.rb +++ b/spec/lib/expand_variables_spec.rb @@ -7,58 +7,49 @@ describe ExpandVariables do tests = [ { value: 'key', result: 'key', - variables: [] - }, + variables: [] }, { value: 'key$variable', result: 'key', - variables: [] - }, + variables: [] }, { value: 'key$variable', result: 'keyvalue', variables: [ { key: 'variable', value: 'value' } - ] - }, + ] }, { value: 'key${variable}', result: 'keyvalue', variables: [ { key: 'variable', value: 'value' } - ] - }, + ] }, { value: 'key$variable$variable2', result: 'keyvalueresult', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' }, - ] - }, + ] }, { value: 'key${variable}${variable2}', result: 'keyvalueresult', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' } - ] - }, + ] }, { value: 'key$variable2$variable', result: 'keyresultvalue', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' }, - ] - }, + ] }, { value: 'key${variable2}${variable}', result: 'keyresultvalue', variables: [ { key: 'variable', value: 'value' }, { key: 'variable2', value: 'result' } - ] - }, + ] }, { value: 'review/$CI_BUILD_REF_NAME', result: 'review/feature/add-review-apps', variables: [ { key: 'CI_BUILD_REF_NAME', value: 'feature/add-review-apps' } - ] - }, + ] }, ] tests.each do |test| diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 29c07655ae8..33ab005667a 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -177,12 +177,12 @@ describe ExtractsPath, lib: true do it "extracts a valid commit SHA" do expect(extract_ref('f4b14494ef6abf3d144c28e4af0c20143383e062/CHANGELOG')).to eq( - ['f4b14494ef6abf3d144c28e4af0c20143383e062', 'CHANGELOG'] + %w(f4b14494ef6abf3d144c28e4af0c20143383e062 CHANGELOG) ) end it "falls back to a primitive split for an invalid ref" do - expect(extract_ref('stable/CHANGELOG')).to eq(['stable', 'CHANGELOG']) + expect(extract_ref('stable/CHANGELOG')).to eq(%w(stable CHANGELOG)) end end end diff --git a/spec/lib/gitlab/badge/shared/metadata.rb b/spec/lib/gitlab/badge/shared/metadata.rb index 0cf18514251..63c7ca5a915 100644 --- a/spec/lib/gitlab/badge/shared/metadata.rb +++ b/spec/lib/gitlab/badge/shared/metadata.rb @@ -18,4 +18,14 @@ shared_examples 'badge metadata' do it { is_expected.to include metadata.image_url } it { is_expected.to include metadata.link_url } end + + describe '#to_asciidoc' do + subject { metadata.to_asciidoc } + + it { is_expected.to include metadata.image_url } + it { is_expected.to include metadata.link_url } + it { is_expected.to include 'image:' } + it { is_expected.to include 'link=' } + it { is_expected.to include 'title=' } + end end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index 0a2fe5af2c3..a7ee7f53a6b 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -87,10 +87,10 @@ describe Gitlab::BitbucketImport::Importer, lib: true do body: issues_statuses_sample_data.to_json) stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on"). - with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }). - to_return(status: 200, - body: "", - headers: {}) + with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }). + to_return(status: 200, + body: "", + headers: {}) sample_issues_statuses.each_with_index do |issue, index| stub_request( diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb b/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb index 5b678d31fce..3916fc704a4 100644 --- a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb +++ b/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb @@ -26,6 +26,21 @@ describe Gitlab::ChatCommands::Presenters::IssueShow do end end + context 'with labels' do + let(:label) { create(:label, project: project, title: 'mep') } + let(:label1) { create(:label, project: project, title: 'mop') } + + before do + issue.labels << [label, label1] + end + + it 'shows the labels' do + labels = attachment[:fields].find { |f| f[:title] == 'Labels' } + + expect(labels[:value]).to eq("mep, mop") + end + end + context 'confidential issue' do let(:issue) { create(:issue, project: project) } diff --git a/spec/lib/gitlab/ci/config/entry/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb index b8b0825a1c7..afa4a089418 100644 --- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::Ci::Config::Entry::Commands do let(:entry) { described_class.new(config) } context 'when entry config value is an array' do - let(:config) { ['ls', 'pwd'] } + let(:config) { %w(ls pwd) } describe '#value' do it 'returns array of strings' do diff --git a/spec/lib/gitlab/ci/config/entry/factory_spec.rb b/spec/lib/gitlab/ci/config/entry/factory_spec.rb index 00dad5d9591..3395b3c645b 100644 --- a/spec/lib/gitlab/ci/config/entry/factory_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/factory_spec.rb @@ -8,20 +8,20 @@ describe Gitlab::Ci::Config::Entry::Factory do context 'when setting a concrete value' do it 'creates entry with valid value' do entry = factory - .value(['ls', 'pwd']) + .value(%w(ls pwd)) .create! - expect(entry.value).to eq ['ls', 'pwd'] + expect(entry.value).to eq %w(ls pwd) end context 'when setting description' do it 'creates entry with description' do entry = factory - .value(['ls', 'pwd']) + .value(%w(ls pwd)) .with(description: 'test description') .create! - expect(entry.value).to eq ['ls', 'pwd'] + expect(entry.value).to eq %w(ls pwd) expect(entry.description).to eq 'test description' end end @@ -29,7 +29,7 @@ describe Gitlab::Ci::Config::Entry::Factory do context 'when setting key' do it 'creates entry with custom key' do entry = factory - .value(['ls', 'pwd']) + .value(%w(ls pwd)) .with(key: 'test key') .create! diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb index 432a99dce33..ebd80ac5e1d 100644 --- a/spec/lib/gitlab/ci/config/entry/global_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb @@ -21,12 +21,12 @@ describe Gitlab::Ci::Config::Entry::Global do context 'when configuration is valid' do context 'when some entries defined' do let(:hash) do - { before_script: ['ls', 'pwd'], + { before_script: %w(ls pwd), image: 'ruby:2.2', services: ['postgres:9.1', 'mysql:5.5'], variables: { VAR: 'value' }, after_script: ['make clean'], - stages: ['build', 'pages'], + stages: %w(build pages), cache: { key: 'k', untracked: true, paths: ['public/'] }, rspec: { script: %w[rspec ls] }, spinach: { before_script: [], variables: {}, script: 'spinach' } } @@ -89,7 +89,7 @@ describe Gitlab::Ci::Config::Entry::Global do describe '#before_script_value' do it 'returns correct script' do - expect(global.before_script_value).to eq ['ls', 'pwd'] + expect(global.before_script_value).to eq %w(ls pwd) end end @@ -126,7 +126,7 @@ describe Gitlab::Ci::Config::Entry::Global do context 'when deprecated types key defined' do let(:hash) do - { types: ['test', 'deploy'], + { types: %w(test deploy), rspec: { script: 'rspec' } } end @@ -148,7 +148,7 @@ describe Gitlab::Ci::Config::Entry::Global do expect(global.jobs_value).to eq( rspec: { name: :rspec, script: %w[rspec ls], - before_script: ['ls', 'pwd'], + before_script: %w(ls pwd), commands: "ls\npwd\nrspec\nls", image: 'ruby:2.2', services: ['postgres:9.1', 'mysql:5.5'], diff --git a/spec/lib/gitlab/ci/config/entry/key_spec.rb b/spec/lib/gitlab/ci/config/entry/key_spec.rb index a55e5b4b8ac..0dd36fe1f44 100644 --- a/spec/lib/gitlab/ci/config/entry/key_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/key_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Ci::Config::Entry::Key do end context 'when entry value is not correct' do - let(:config) { [ 'incorrect' ] } + let(:config) { ['incorrect'] } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/ci/config/entry/paths_spec.rb b/spec/lib/gitlab/ci/config/entry/paths_spec.rb index e60c9aaf661..1d9c5ddee9b 100644 --- a/spec/lib/gitlab/ci/config/entry/paths_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/paths_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Ci::Config::Entry::Paths do end context 'when entry value is not valid' do - let(:config) { [ 1 ] } + let(:config) { [1] } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/ci/config/entry/script_spec.rb b/spec/lib/gitlab/ci/config/entry/script_spec.rb index aa99cee2690..069eaa26422 100644 --- a/spec/lib/gitlab/ci/config/entry/script_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/script_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::Ci::Config::Entry::Script do describe 'validations' do context 'when entry config value is correct' do - let(:config) { ['ls', 'pwd'] } + let(:config) { %w(ls pwd) } describe '#value' do it 'returns array of strings' do diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb index 58327d08904..f15f02f403e 100644 --- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb @@ -29,7 +29,7 @@ describe Gitlab::Ci::Config::Entry::Variables do end context 'when entry value is not correct' do - let(:config) { [ :VAR, 'test' ] } + let(:config) { [:VAR, 'test'] } describe '#errors' do it 'saves errors' do diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb index fbf679c5215..7e5531d92dc 100644 --- a/spec/lib/gitlab/conflict/file_spec.rb +++ b/spec/lib/gitlab/conflict/file_spec.rb @@ -44,7 +44,7 @@ describe Gitlab::Conflict::File, lib: true do it 'returns a file containing only the chosen parts of the resolved sections' do expect(resolved_lines.chunk { |line| line.type || 'both' }.map(&:first)). - to eq(['both', 'new', 'both', 'old', 'both', 'new', 'both']) + to eq(%w(both new both old both new both)) end end @@ -123,7 +123,7 @@ describe Gitlab::Conflict::File, lib: true do it 'sets conflict to true for sections with only changed lines' do conflict_file.sections.select { |section| section[:conflict] }.each do |section| section[:lines].each do |line| - expect(line.type).to be_in(['new', 'old']) + expect(line.type).to be_in(%w(new old)) end end end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index e94ca4fcfd2..e007044868c 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -101,6 +101,16 @@ describe Gitlab::Database::MigrationHelpers, lib: true do end end + describe '#concurrent_foreign_key_name' do + it 'returns the name for a foreign key' do + name = model.concurrent_foreign_key_name(:this_is_a_very_long_table_name, + :with_a_very_long_column_name) + + expect(name).to be_an_instance_of(String) + expect(name.length).to eq(13) + end + end + describe '#disable_statement_timeout' do context 'using PostgreSQL' do it 'disables statement timeouts' do diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index f01c42aff91..edd01d032c8 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -119,9 +119,24 @@ describe Gitlab::Database, lib: true do it 'creates a new connection pool with specific pool size' do pool = described_class.create_connection_pool(5) - expect(pool) - .to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool) - expect(pool.spec.config[:pool]).to eq(5) + begin + expect(pool) + .to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool) + + expect(pool.spec.config[:pool]).to eq(5) + ensure + pool.disconnect! + end + end + + it 'allows setting of a custom hostname' do + pool = described_class.create_connection_pool(5, '127.0.0.1') + + begin + expect(pool.spec.config[:host]).to eq('127.0.0.1') + ensure + pool.disconnect! + end end end diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb index 5893485634d..0e9309d278e 100644 --- a/spec/lib/gitlab/diff/highlight_spec.rb +++ b/spec/lib/gitlab/diff/highlight_spec.rb @@ -53,21 +53,21 @@ describe Gitlab::Diff::Highlight, lib: true do end it 'marks unchanged lines' do - code = %Q{ def popen(cmd, path=nil)} + code = %q{ def popen(cmd, path=nil)} expect(subject[2].text).to eq(code) expect(subject[2].text).not_to be_html_safe end it 'marks removed lines' do - code = %Q{- raise "System commands must be given as an array of strings"} + code = %q{- raise "System commands must be given as an array of strings"} expect(subject[4].text).to eq(code) expect(subject[4].text).not_to be_html_safe end it 'marks added lines' do - code = %Q{+ raise <span class='idiff left right'>RuntimeError, </span>"System commands must be given as an array of strings"} + code = %q{+ raise <span class='idiff left right'>RuntimeError, </span>"System commands must be given as an array of strings"} expect(subject[5].text).to eq(code) expect(subject[5].text).to be_html_safe diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb index 79b1311ac91..17d6be470ac 100644 --- a/spec/lib/gitlab/git/blob_snippet_spec.rb +++ b/spec/lib/gitlab/git/blob_snippet_spec.rb @@ -11,7 +11,7 @@ describe Gitlab::Git::BlobSnippet, seed_helper: true do end context 'present lines' do - let(:snippet) { Gitlab::Git::BlobSnippet.new('master', ['wow', 'much'], 1, 'wow.rb') } + let(:snippet) { Gitlab::Git::BlobSnippet.new('master', %w(wow much), 1, 'wow.rb') } it { expect(snippet.data).to eq("wow\nmuch") } end diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 84f79ec2391..0c321f0343c 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -292,7 +292,7 @@ describe Gitlab::Git::Blob, seed_helper: true do it 'should preserve file modes with commit' do commit_options[:file][:path] = 'files/executables/ls' - entry = Gitlab::Git::Blob::find_entry_by_path(repository, commit.tree.oid, commit_options[:file][:path]) + entry = Gitlab::Git::Blob.find_entry_by_path(repository, commit.tree.oid, commit_options[:file][:path]) expect(entry[:filemode]).to eq(0100755) end end diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb index 4fa72c565ae..47bdd7310d5 100644 --- a/spec/lib/gitlab/git/diff_collection_spec.rb +++ b/spec/lib/gitlab/git/diff_collection_spec.rb @@ -365,7 +365,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do end context 'when go over safe limits on files' do - let(:iterator) { [ fake_diff(1, 1) ] * 4 } + let(:iterator) { [fake_diff(1, 1)] * 4 } before(:each) do stub_const('Gitlab::Git::DiffCollection::DEFAULT_LIMITS', { max_files: 2, max_lines: max_lines }) diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index a55bd4387e0..d37890de9ae 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -199,7 +199,9 @@ describe Gitlab::GitAccess, lib: true do def stub_git_hooks # Running the `pre-receive` hook is expensive, and not necessary for this test. - allow_any_instance_of(GitHooksService).to receive(:execute).and_yield + allow_any_instance_of(GitHooksService).to receive(:execute) do |service, &block| + block.call(service) + end end def merge_into_protected_branch @@ -232,11 +234,18 @@ describe Gitlab::GitAccess, lib: true do else project.team << [user, role] end + end + + permissions_matrix[role].each do |action, allowed| + context action do + subject { access.send(:check_push_access!, changes[action]) } - permissions_matrix[role].each do |action, allowed| - context action do - subject { access.send(:check_push_access!, changes[action]) } - it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey } + it do + if allowed + expect { subject }.not_to raise_error + else + expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError) + end end end end @@ -301,7 +310,7 @@ describe Gitlab::GitAccess, lib: true do } } - [['feature', 'exact'], ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type| + [%w(feature exact), ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type| context do before { create(:protected_branch, name: protected_branch_name, project: project) } diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb index 219198eff60..8eaf7aac264 100644 --- a/spec/lib/gitlab/git_spec.rb +++ b/spec/lib/gitlab/git_spec.rb @@ -19,7 +19,7 @@ describe Gitlab::Git, lib: true do describe 'committer_hash' do it "returns a hash containing the given email and name" do - committer_hash = Gitlab::Git::committer_hash(email: committer_email, name: committer_name) + committer_hash = Gitlab::Git.committer_hash(email: committer_email, name: committer_name) expect(committer_hash[:email]).to eq(committer_email) expect(committer_hash[:name]).to eq(committer_name) @@ -28,7 +28,7 @@ describe Gitlab::Git, lib: true do context 'when email is nil' do it "returns nil" do - committer_hash = Gitlab::Git::committer_hash(email: nil, name: committer_name) + committer_hash = Gitlab::Git.committer_hash(email: nil, name: committer_name) expect(committer_hash).to be_nil end @@ -36,7 +36,7 @@ describe Gitlab::Git, lib: true do context 'when name is nil' do it "returns nil" do - committer_hash = Gitlab::Git::committer_hash(email: committer_email, name: nil) + committer_hash = Gitlab::Git.committer_hash(email: committer_email, name: nil) expect(committer_hash).to be_nil end diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb index ea65a5dfed1..e24d070706a 100644 --- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb @@ -17,7 +17,7 @@ describe 'Import/Export attribute configuration', lib: true do # Remove duplicated or add missing models # - project is not part of the tree, so it has to be added manually. # - milestone, labels have both singular and plural versions in the tree, so remove the duplicates. - names.flatten.uniq - ['milestones', 'labels'] + ['project'] + names.flatten.uniq - %w(milestones labels) + ['project'] end let(:safe_attributes_file) { 'spec/lib/gitlab/import_export/safe_model_attributes.yml' } diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb index d6ee94442cb..579a31ead58 100644 --- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb +++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ImportExport::AvatarSaver, lib: true do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:project_with_avatar) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } let(:project) { create(:empty_project) } diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb index a88ddd17aca..b88b9c18c15 100644 --- a/spec/lib/gitlab/import_export/file_importer_spec.rb +++ b/spec/lib/gitlab/import_export/file_importer_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ImportExport::FileImporter, lib: true do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') } - let(:export_path) { "#{Dir::tmpdir}/file_importer_spec" } + let(:export_path) { "#{Dir.tmpdir}/file_importer_spec" } let(:valid_file) { "#{shared.export_path}/valid.json" } let(:symlink_file) { "#{shared.export_path}/invalid.json" } let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" } diff --git a/spec/lib/gitlab/import_export/model_configuration_spec.rb b/spec/lib/gitlab/import_export/model_configuration_spec.rb index 9b492d1b9c7..2ede5cdd2ad 100644 --- a/spec/lib/gitlab/import_export/model_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/model_configuration_spec.rb @@ -14,7 +14,7 @@ describe 'Import/Export model configuration', lib: true do # - project is not part of the tree, so it has to be added manually. # - milestone, labels have both singular and plural versions in the tree, so remove the duplicates. # - User, Author... Models we do not care about for checking models - names.flatten.uniq - ['milestones', 'labels', 'user', 'author'] + ['project'] + names.flatten.uniq - %w(milestones labels user author) + ['project'] end let(:all_models_yml) { 'spec/lib/gitlab/import_export/all_models.yml' } 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 3628adefc0c..012c22ec5ad 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do describe 'saves the project tree into a json object' do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:user) { create(:user) } let(:project) { setup_project } @@ -114,7 +114,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do it 'has project and group labels' do label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type'] } - expect(label_types).to match_array(['ProjectLabel', 'GroupLabel']) + expect(label_types).to match_array(%w(ProjectLabel GroupLabel)) end it 'has priorities associated to labels' do diff --git a/spec/lib/gitlab/import_export/repo_bundler_spec.rb b/spec/lib/gitlab/import_export/repo_bundler_spec.rb index d39ea60ff7f..a7f4e11271e 100644 --- a/spec/lib/gitlab/import_export/repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/repo_bundler_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::ImportExport::RepoSaver, services: true do describe 'bundle a project Git repo' do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:bundler) { described_class.new(project: project, shared: shared) } diff --git a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb index 47d5d2fc150..071e5fac3f0 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::ImportExport::WikiRepoSaver, services: true do describe 'bundle a wiki Git repo' do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } - let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:wiki_bundler) { described_class.new(project: project, shared: shared) } let!(:project_wiki) { ProjectWiki.new(project, user) } diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb index 8cea38e9ff8..b3b5e5e7e33 100644 --- a/spec/lib/gitlab/import_sources_spec.rb +++ b/spec/lib/gitlab/import_sources_spec.rb @@ -22,16 +22,16 @@ describe Gitlab::ImportSources do describe '.values' do it 'returns an array' do expected = - [ - 'github', - 'bitbucket', - 'gitlab', - 'google_code', - 'fogbugz', - 'git', - 'gitlab_project', - 'gitea' - ] + %w( + github + bitbucket + gitlab + google_code + fogbugz + git + gitlab_project + gitea + ) expect(described_class.values).to eq(expected) end @@ -40,15 +40,15 @@ describe Gitlab::ImportSources do describe '.importer_names' do it 'returns an array of importer names' do expected = - [ - 'github', - 'bitbucket', - 'gitlab', - 'google_code', - 'fogbugz', - 'gitlab_project', - 'gitea' - ] + %w( + github + bitbucket + gitlab + google_code + fogbugz + gitlab_project + gitea + ) expect(described_class.importer_names).to eq(expected) end diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb index c9bd52a3b8f..91f9d06b85a 100644 --- a/spec/lib/gitlab/kubernetes_spec.rb +++ b/spec/lib/gitlab/kubernetes_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::Kubernetes do let(:pod_name) { 'pod1' } let(:container_name) { 'container1' } - subject(:result) { URI::parse(container_exec_url(api_url, namespace, pod_name, container_name)) } + subject(:result) { URI.parse(container_exec_url(api_url, namespace, pod_name, container_name)) } it { expect(result.scheme).to eq('wss') } it { expect(result.host).to eq('example.com') } diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb index 69c49051156..7a2f774b948 100644 --- a/spec/lib/gitlab/ldap/auth_hash_spec.rb +++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb @@ -44,7 +44,7 @@ describe Gitlab::LDAP::AuthHash, lib: true do context "with overridden attributes" do let(:attributes) do { - 'username' => ['mail', 'email'], + 'username' => %w(mail email), 'name' => 'fullName' } end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 89790c9e1af..2f3bd4393b7 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -95,10 +95,10 @@ describe Gitlab::LDAP::User, lib: true do it 'maintains an identity per provider' do existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter') - expect(existing_user.identities.count).to eql(1) + expect(existing_user.identities.count).to be(1) ldap_user.save - expect(ldap_user.gl_user.identities.count).to eql(2) + expect(ldap_user.gl_user.identities.count).to be(2) # Expect that find_by provider only returns a single instance of an identity and not an Enumerable expect(ldap_user.gl_user.identities.find_by(provider: 'twitter')).to be_instance_of Identity diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index d88bcae41fb..a986cb520fb 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -197,11 +197,13 @@ describe Gitlab::Metrics::Instrumentation do @child1 = Class.new(@dummy) do def self.child1_foo; end + def child1_bar; end end @child2 = Class.new(@child1) do def self.child2_foo; end + def child2_bar; end end end diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb index 8d05081eecb..a247f03b2da 100644 --- a/spec/lib/gitlab/metrics/method_call_spec.rb +++ b/spec/lib/gitlab/metrics/method_call_spec.rb @@ -23,7 +23,7 @@ describe Gitlab::Metrics::MethodCall do expect(metric.values[:duration]).to be_a_kind_of(Numeric) expect(metric.values[:cpu_duration]).to be_a_kind_of(Numeric) - expect(metric.values[:call_count]).to an_instance_of(Fixnum) + expect(metric.values[:call_count]).to be_an(Integer) expect(metric.tags).to eq({ method: 'Foo#bar' }) end diff --git a/spec/lib/gitlab/metrics/metric_spec.rb b/spec/lib/gitlab/metrics/metric_spec.rb index f26fca52c50..d240b8a01fd 100644 --- a/spec/lib/gitlab/metrics/metric_spec.rb +++ b/spec/lib/gitlab/metrics/metric_spec.rb @@ -62,7 +62,7 @@ describe Gitlab::Metrics::Metric do end it 'includes the timestamp' do - expect(hash[:timestamp]).to be_an_instance_of(Fixnum) + expect(hash[:timestamp]).to be_an(Integer) end end end diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb index 9e2ea89a712..4d94d8705fb 100644 --- a/spec/lib/gitlab/metrics/system_spec.rb +++ b/spec/lib/gitlab/metrics/system_spec.rb @@ -29,19 +29,19 @@ describe Gitlab::Metrics::System do describe '.cpu_time' do it 'returns a Fixnum' do - expect(described_class.cpu_time).to be_an_instance_of(Fixnum) + expect(described_class.cpu_time).to be_an(Integer) end end describe '.real_time' do it 'returns a Fixnum' do - expect(described_class.real_time).to be_an_instance_of(Fixnum) + expect(described_class.real_time).to be_an(Integer) end end describe '.monotonic_time' do it 'returns a Fixnum' do - expect(described_class.monotonic_time).to be_an_instance_of(Fixnum) + expect(described_class.monotonic_time).to be_an(Integer) end end end diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 3887c04c832..0c5a6246d85 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -134,7 +134,7 @@ describe Gitlab::Metrics::Transaction do series: 'rails_transactions', tags: { action: 'Foo#bar' }, values: { duration: 0.0, allocated_memory: a_kind_of(Numeric) }, - timestamp: an_instance_of(Fixnum) + timestamp: a_kind_of(Integer) } expect(Gitlab::Metrics).to receive(:submit_metrics). @@ -151,7 +151,7 @@ describe Gitlab::Metrics::Transaction do series: 'events', tags: { event: :meow }, values: { count: 1 }, - timestamp: an_instance_of(Fixnum) + timestamp: a_kind_of(Integer) } expect(Gitlab::Metrics).to receive(:submit_metrics). diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index fc9e1cb430a..6c84a4c8b73 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -148,12 +148,14 @@ describe Gitlab::OAuth::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql uid expect(gl_user.email).to eql 'johndoe@example.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( - [ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + [ + { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, { provider: 'twitter', extern_uid: uid } - ]) + ] + ) end end @@ -167,12 +169,14 @@ describe Gitlab::OAuth::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.email).to eql 'john@example.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( - [ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + [ + { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, { provider: 'twitter', extern_uid: uid } - ]) + ] + ) end end diff --git a/spec/lib/gitlab/optimistic_locking_spec.rb b/spec/lib/gitlab/optimistic_locking_spec.rb index 498dc514c8c..acce2be93f2 100644 --- a/spec/lib/gitlab/optimistic_locking_spec.rb +++ b/spec/lib/gitlab/optimistic_locking_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' describe Gitlab::OptimisticLocking, lib: true do - describe '#retry_lock' do - let!(:pipeline) { create(:ci_pipeline) } - let!(:pipeline2) { Ci::Pipeline.find(pipeline.id) } + let!(:pipeline) { create(:ci_pipeline) } + let!(:pipeline2) { Ci::Pipeline.find(pipeline.id) } + describe '#retry_lock' do it 'does not reload object if state changes' do expect(pipeline).not_to receive(:reload) expect(pipeline).to receive(:succeed).and_call_original @@ -36,4 +36,17 @@ describe Gitlab::OptimisticLocking, lib: true do end.to raise_error(ActiveRecord::StaleObjectError) end end + + describe '#retry_optimistic_lock' do + context 'when locking module is mixed in' do + let(:unlockable) do + Class.new.include(described_class).new + end + + it 'is an alias for retry_lock' do + expect(unlockable.method(:retry_optimistic_lock)) + .to eq unlockable.method(:retry_lock) + end + end + end end diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index 02c139f1a0d..4f6ef3c10fc 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -155,11 +155,10 @@ describe Gitlab::Saml::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql uid expect(gl_user.email).to eql 'john@mail.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: uid } - ]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'saml', extern_uid: uid }]) end end @@ -178,11 +177,10 @@ describe Gitlab::Saml::User, lib: true do expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' expect(gl_user.email).to eql 'john@mail.com' - expect(gl_user.identities.length).to eql 2 + expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: uid } - ]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'saml', extern_uid: uid }]) end it 'saves successfully on subsequent tries, when both identities are present' do @@ -204,11 +202,10 @@ describe Gitlab::Saml::User, lib: true do local_gl_user = local_saml_user.gl_user expect(local_gl_user).to be_valid - expect(local_gl_user.identities.length).to eql 2 + expect(local_gl_user.identities.length).to be 2 identities_as_hash = local_gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' } - ]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' }]) end end end diff --git a/spec/lib/gitlab/serializer/ci/variables_spec.rb b/spec/lib/gitlab/serializer/ci/variables_spec.rb index b810c68ea03..c4b7fda5dbb 100644 --- a/spec/lib/gitlab/serializer/ci/variables_spec.rb +++ b/spec/lib/gitlab/serializer/ci/variables_spec.rb @@ -13,6 +13,7 @@ describe Gitlab::Serializer::Ci::Variables do it 'converts keys into strings' do is_expected.to eq([ { key: 'key', value: 'value', public: true }, - { key: 'wee', value: 1, public: false }]) + { key: 'wee', value: 1, public: false } + ]) end end diff --git a/spec/lib/gitlab/template/issue_template_spec.rb b/spec/lib/gitlab/template/issue_template_spec.rb index 1335a2b8f35..9213ced7b19 100644 --- a/spec/lib/gitlab/template/issue_template_spec.rb +++ b/spec/lib/gitlab/template/issue_template_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::Template::IssueTemplate do create_template: { user: user, access: Gitlab::Access::MASTER, - path: 'issue_templates' }) + path: 'issue_templates' + }) end describe '.all' do diff --git a/spec/lib/gitlab/template/merge_request_template_spec.rb b/spec/lib/gitlab/template/merge_request_template_spec.rb index 320b870309a..77dd3079e22 100644 --- a/spec/lib/gitlab/template/merge_request_template_spec.rb +++ b/spec/lib/gitlab/template/merge_request_template_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::Template::MergeRequestTemplate do create_template: { user: user, access: Gitlab::Access::MASTER, - path: 'merge_request_templates' }) + path: 'merge_request_templates' + }) end describe '.all' do diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb index edadab043d7..fcfd8d58b70 100644 --- a/spec/lib/gitlab/upgrader_spec.rb +++ b/spec/lib/gitlab/upgrader_spec.rb @@ -32,7 +32,8 @@ describe Gitlab::Upgrader, lib: true do '43af3e65a486a9237f29f56d96c3b3da59c24ae0 refs/tags/v7.11.2', 'dac18e7728013a77410e926a1e64225703754a2d refs/tags/v7.11.2^{}', '0bf21fd4b46c980c26fd8c90a14b86a4d90cc950 refs/tags/v7.9.4', - 'b10de29edbaff7219547dc506cb1468ee35065c3 refs/tags/v7.9.4^{}']) + 'b10de29edbaff7219547dc506cb1468ee35065c3 refs/tags/v7.9.4^{}' + ]) expect(upgrader.latest_version_raw).to eq("v7.11.2") end end diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb index d5d87310874..56772409989 100644 --- a/spec/lib/gitlab/utils_spec.rb +++ b/spec/lib/gitlab/utils_spec.rb @@ -1,7 +1,5 @@ describe Gitlab::Utils, lib: true do - def to_boolean(value) - described_class.to_boolean(value) - end + delegate :to_boolean, to: :described_class describe '.to_boolean' do it 'accepts booleans' do diff --git a/spec/lib/mattermost/command_spec.rb b/spec/lib/mattermost/command_spec.rb index 5ccf1100898..4b5938edeb9 100644 --- a/spec/lib/mattermost/command_spec.rb +++ b/spec/lib/mattermost/command_spec.rb @@ -13,8 +13,7 @@ describe Mattermost::Command do describe '#create' do let(:params) do { team_id: 'abc', - trigger: 'gitlab' - } + trigger: 'gitlab' } end subject { described_class.new(nil).create(params) } @@ -24,7 +23,8 @@ describe Mattermost::Command do stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create'). with(body: { team_id: 'abc', - trigger: 'gitlab' }.to_json). + trigger: 'gitlab' + }.to_json). to_return( status: 200, headers: { 'Content-Type' => 'application/json' }, diff --git a/spec/lib/mattermost/team_spec.rb b/spec/lib/mattermost/team_spec.rb index 2d14be6bcc2..6a9c7cebfff 100644 --- a/spec/lib/mattermost/team_spec.rb +++ b/spec/lib/mattermost/team_spec.rb @@ -14,18 +14,19 @@ describe Mattermost::Team do context 'for valid request' do let(:response) do [{ - "id" => "xiyro8huptfhdndadpz8r3wnbo", - "create_at" => 1482174222155, - "update_at" => 1482174222155, - "delete_at" => 0, - "display_name" => "chatops", - "name" => "chatops", - "email" => "admin@example.com", - "type" => "O", - "company_name" => "", - "allowed_domains" => "", - "invite_id" => "o4utakb9jtb7imctdfzbf9r5ro", - "allow_open_invite" => false }] + "id" => "xiyro8huptfhdndadpz8r3wnbo", + "create_at" => 1482174222155, + "update_at" => 1482174222155, + "delete_at" => 0, + "display_name" => "chatops", + "name" => "chatops", + "email" => "admin@example.com", + "type" => "O", + "company_name" => "", + "allowed_domains" => "", + "invite_id" => "o4utakb9jtb7imctdfzbf9r5ro", + "allow_open_invite" => false + }] end before do diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb index 6a93deb5412..b6d678bac18 100644 --- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb +++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb @@ -62,7 +62,7 @@ describe MigrateProcessCommitWorkerJobs do end def pop_job - JSON.load(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) + JSON.parse(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) end before do @@ -198,7 +198,7 @@ describe MigrateProcessCommitWorkerJobs do let(:job) do migration.down - JSON.load(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) + JSON.parse(Sidekiq.redis { |r| r.lpop('queue:process_commit') }) end it 'includes the project ID' do diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b3eb206612e..01ca1584ed2 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -96,9 +96,9 @@ describe ApplicationSetting, models: true do describe 'inclusion' do it { is_expected.to allow_value('custom1').for(:repository_storages) } - it { is_expected.to allow_value(['custom2', 'custom3']).for(:repository_storages) } + it { is_expected.to allow_value(%w(custom2 custom3)).for(:repository_storages) } it { is_expected.not_to allow_value('alternative').for(:repository_storages) } - it { is_expected.not_to allow_value(['alternative', 'custom1']).for(:repository_storages) } + it { is_expected.not_to allow_value(%w(alternative custom1)).for(:repository_storages) } end describe 'presence' do @@ -117,7 +117,7 @@ describe ApplicationSetting, models: true do describe '#repository_storage' do it 'returns the first storage' do - setting.repository_storages = ['good', 'bad'] + setting.repository_storages = %w(good bad) expect(setting.repository_storage).to eq('good') end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 444ad40ae0d..4251cb06a2d 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1268,8 +1268,8 @@ describe Ci::Build, :models do context 'when build has user' do let(:user_variables) do - [ { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true }, - { key: 'GITLAB_USER_EMAIL', value: user.email, public: true } ] + [{ key: 'GITLAB_USER_ID', value: user.id.to_s, public: true }, + { key: 'GITLAB_USER_EMAIL', value: user.email, public: true }] end before do @@ -1426,7 +1426,7 @@ describe Ci::Build, :models do end context 'when runner is assigned to build' do - let(:runner) { create(:ci_runner, description: 'description', tag_list: ['docker', 'linux']) } + let(:runner) { create(:ci_runner, description: 'description', tag_list: %w(docker linux)) } before do build.update(runner: runner) diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 10c2bfbb400..c2fc8c02bb3 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -168,9 +168,9 @@ describe Ci::Pipeline, models: true do end it 'returns list of stages with correct statuses' do - expect(statuses).to eq([['build', 'failed'], - ['test', 'success'], - ['deploy', 'running']]) + expect(statuses).to eq([%w(build failed), + %w(test success), + %w(deploy running)]) end context 'when commit status is retried' do @@ -183,9 +183,9 @@ describe Ci::Pipeline, models: true do end it 'ignores the previous state' do - expect(statuses).to eq([['build', 'success'], - ['test', 'success'], - ['deploy', 'running']]) + expect(statuses).to eq([%w(build success), + %w(test success), + %w(deploy running)]) end end end @@ -199,7 +199,7 @@ describe Ci::Pipeline, models: true do describe '#stages_name' do it 'returns a valid names of stages' do - expect(pipeline.stages_name).to eq(['build', 'test', 'deploy']) + expect(pipeline.stages_name).to eq(%w(build test deploy)) end end end @@ -767,7 +767,7 @@ describe Ci::Pipeline, models: true do end it 'cancels created builds' do - expect(latest_status).to eq ['canceled', 'canceled'] + expect(latest_status).to eq %w(canceled canceled) end end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index f8513ac8b1c..76ce558eea0 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -113,7 +113,7 @@ describe Ci::Runner, models: true do context 'when runner has tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) end shared_examples 'tagged build picker' do @@ -169,7 +169,7 @@ describe Ci::Runner, models: true do context 'when having runner tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) end it 'cannot handle it for builds without matching tags' do @@ -189,7 +189,7 @@ describe Ci::Runner, models: true do context 'when having runner tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) build.tag_list = ['bb'] end @@ -212,7 +212,7 @@ describe Ci::Runner, models: true do context 'when having runner tags' do before do - runner.tag_list = ['bb', 'cc'] + runner.tag_list = %w(bb cc) build.tag_list = ['bb'] end diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index 2e3702f7520..6151d53cd91 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' describe CacheMarkdownField do - CacheMarkdownField::CACHING_CLASSES << "ThingWithMarkdownFields" + caching_classes = CacheMarkdownField::CACHING_CLASSES + CacheMarkdownField::CACHING_CLASSES = ["ThingWithMarkdownFields"].freeze # The minimum necessary ActiveModel to test this concern class ThingWithMarkdownFields @@ -54,7 +55,7 @@ describe CacheMarkdownField do end end - CacheMarkdownField::CACHING_CLASSES.delete("ThingWithMarkdownFields") + CacheMarkdownField::CACHING_CLASSES = caching_classes def thing_subclass(new_attr) Class.new(ThingWithMarkdownFields) { add_attr(new_attr) } diff --git a/spec/models/concerns/spammable_spec.rb b/spec/models/concerns/spammable_spec.rb index b6e5c95d18a..fd3b8307571 100644 --- a/spec/models/concerns/spammable_spec.rb +++ b/spec/models/concerns/spammable_spec.rb @@ -23,6 +23,7 @@ describe Issue, 'Spammable' do describe '#check_for_spam?' do it 'returns true for public project' do issue.project.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC) + expect(issue.check_for_spam?).to eq(true) end diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index 9a024d533a1..78ec518ac86 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -18,7 +18,7 @@ describe 'CycleAnalytics#staging', feature: true do start_time_conditions: [["merge request that closes issue is merged", -> (context, data) do context.merge_merge_requests_closing_issue(data[:issue]) - end ]], + end]], end_time_conditions: [["merge request that closes issue is deployed to production", -> (context, data) do context.deploy_master diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index f0ed0c679d5..dce18f008f8 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -76,7 +76,8 @@ describe Environment, models: true do end describe '#update_merge_request_metrics?' do - { 'production' => true, + { + 'production' => true, 'production/eu' => true, 'production/www.gitlab.com' => true, 'productioneu' => false, @@ -311,7 +312,7 @@ describe Environment, models: true do end describe '#generate_slug' do - SUFFIX = "-[a-z0-9]{6}" + SUFFIX = "-[a-z0-9]{6}".freeze { "staging-12345678901234567" => "staging-123456789" + SUFFIX, "9-staging-123456789012345" => "env-9-staging-123" + SUFFIX, diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index e4be0aba7a6..87ea2e70680 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -89,8 +89,8 @@ describe ProjectMember, models: true do @user_1 = create :user @user_2 = create :user - @project_1.team << [ @user_1, :developer ] - @project_2.team << [ @user_2, :reporter ] + @project_1.team << [@user_1, :developer] + @project_2.team << [@user_2, :reporter] @status = @project_2.team.import(@project_1) end @@ -137,8 +137,8 @@ describe ProjectMember, models: true do @user_1 = create :user @user_2 = create :user - @project_1.team << [ @user_1, :developer] - @project_2.team << [ @user_2, :reporter] + @project_1.team << [@user_1, :developer] + @project_2.team << [@user_2, :reporter] ProjectMember.truncate_teams([@project_1.id, @project_2.id]) end diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 6d599e148a2..0a10ee01506 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -109,7 +109,7 @@ describe MergeRequestDiff, models: true do { id: 'sha2' } ] - expect(subject.commits_sha).to eq(['sha1', 'sha2']) + expect(subject.commits_sha).to eq(%w(sha1 sha2)) end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index a01741a9971..fa1b0396bcf 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -209,6 +209,50 @@ describe MergeRequest, models: true do end end + describe '#diff_size' do + let(:merge_request) do + build(:merge_request, source_branch: 'expand-collapse-files', target_branch: 'master') + end + + context 'when there are MR diffs' do + before do + merge_request.save + end + + it 'returns the correct count' do + expect(merge_request.diff_size).to eq(105) + end + + it 'does not perform highlighting' do + expect(Gitlab::Diff::Highlight).not_to receive(:new) + + merge_request.diff_size + end + end + + context 'when there are no MR diffs' do + before do + merge_request.compare = CompareService.new( + merge_request.source_project, + merge_request.source_branch + ).execute( + merge_request.target_project, + merge_request.target_branch + ) + end + + it 'returns the correct count' do + expect(merge_request.diff_size).to eq(105) + end + + it 'does not perform highlighting' do + expect(Gitlab::Diff::Highlight).not_to receive(:new) + + merge_request.diff_size + end + end + end + describe "#related_notes" do let!(:merge_request) { create(:merge_request) } diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 497a626a418..4014d6129ee 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -181,7 +181,7 @@ describe BambooService, models: true, caching: true do end it 'sets commit status to "pending" when response has no results' do - stub_request(body: %Q({"results":{"results":{"size":"0"}}})) + stub_request(body: %q({"results":{"results":{"size":"0"}}})) is_expected.to eq('pending') end diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index dbd23ff5491..05b602d8106 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -92,7 +92,7 @@ describe BuildkiteService, models: true, caching: true do end it 'passes through build status untouched when status is 200' do - stub_request(body: %Q({"status":"Great Success"})) + stub_request(body: %q({"status":"Great Success"})) is_expected.to eq('Great Success') end @@ -101,7 +101,7 @@ describe BuildkiteService, models: true, caching: true do end def stub_request(status: 200, body: nil) - body ||= %Q({"status":"success"}) + body ||= %q({"status":"success"}) buildkite_full_url = 'https://gitlab.buildkite.com/status/secret-sauce-status-token.json?commit=123' WebMock.stub_request(:get, buildkite_full_url).to_return( diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index f9307d6de7b..47ca802ebc2 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -50,7 +50,7 @@ describe DroneCiService, models: true, caching: true do end def stub_request(status: 200, body: nil) - body ||= %Q({"status":"success"}) + body ||= %q({"status":"success"}) WebMock.stub_request(:get, commit_status_path).to_return( status: status, @@ -95,12 +95,12 @@ describe DroneCiService, models: true, caching: true do is_expected.to eq(:error) end - { "killed" => :canceled, + { + "killed" => :canceled, "failure" => :failed, "error" => :failed, - "success" => "success", + "success" => "success" }.each do |drone_status, our_status| - it "sets commit status to #{our_status.inspect} when returned status is #{drone_status.inspect}" do stub_request(body: %Q({"status":"#{drone_status}"})) diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index b9fb6f3f6f4..d5a16226d9d 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -59,8 +59,8 @@ describe IrkerService, models: true do conn = @irker_server.accept conn.readlines.each do |line| - msg = JSON.load(line.chomp("\n")) - expect(msg.keys).to match_array(['to', 'privmsg']) + msg = JSON.parse(line.chomp("\n")) + expect(msg.keys).to match_array(%w(to privmsg)) expect(msg['to']).to match_array(["irc://chat.freenode.net/#commits", "irc://test.net/#test"]) end diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 9052479d35e..24356447fed 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -171,7 +171,7 @@ describe KubernetesService, models: true, caching: true do context 'with invalid pods' do it 'returns no terminals' do - stub_reactive_cache(service, pods: [ { "bad" => "pod" } ]) + stub_reactive_cache(service, pods: [{ "bad" => "pod" }]) is_expected.to be_empty end @@ -184,7 +184,7 @@ describe KubernetesService, models: true, caching: true do before do stub_reactive_cache( service, - pods: [ pod, pod, kube_pod(app: "should-be-filtered-out") ] + pods: [pod, pod, kube_pod(app: "should-be-filtered-out")] ) end diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index 98f3d420c8a..598ff232c82 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -36,7 +36,8 @@ describe MattermostSlashCommandsService, :models do description: "Perform common operations on: #{project.name_with_namespace}", display_name: "GitLab / #{project.name_with_namespace}", method: 'P', - username: 'GitLab' }.to_json). + username: 'GitLab' + }.to_json). to_return( status: 200, headers: { 'Content-Type' => 'application/json' }, diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index a1edd083aa1..77b18e1c7d0 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -143,7 +143,7 @@ describe TeamcityService, models: true, caching: true do end it 'returns a build URL when teamcity_url has no trailing slash' do - stub_request(body: %Q({"build":{"id":"666"}})) + stub_request(body: %q({"build":{"id":"666"}})) is_expected.to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo') end @@ -152,7 +152,7 @@ describe TeamcityService, models: true, caching: true do let(:teamcity_url) { 'http://gitlab.com/teamcity/' } it 'returns a build URL' do - stub_request(body: %Q({"build":{"id":"666"}})) + stub_request(body: %q({"build":{"id":"666"}})) is_expected.to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo') end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 838fd3754b2..43aac31d8b7 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1382,13 +1382,13 @@ describe Repository, models: true do describe '#branch_count' do it 'returns the number of branches' do - expect(repository.branch_count).to be_an_instance_of(Fixnum) + expect(repository.branch_count).to be_an(Integer) end end describe '#tag_count' do it 'returns the number of tags' do - expect(repository.tag_count).to be_an_instance_of(Fixnum) + expect(repository.tag_count).to be_an(Integer) end end @@ -1738,7 +1738,7 @@ describe Repository, models: true do context 'with an existing repository' do it 'returns the commit count' do - expect(repository.commit_count).to be_an_instance_of(Fixnum) + expect(repository.commit_count).to be_an(Integer) end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 584a4facd94..b69fd24c586 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -693,9 +693,7 @@ describe User, models: true do end describe '.search_with_secondary_emails' do - def search_with_secondary_emails(query) - described_class.search_with_secondary_emails(query) - end + delegate :search_with_secondary_emails, to: :described_class let!(:user) { create(:user) } let!(:email) { create(:email) } diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 2e6db0f43c6..5571f6cc107 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -272,7 +272,7 @@ describe API::Branches, api: true do describe "POST /projects/:id/repository/branches" do it "creates a new branch" do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'feature1', + branch: 'feature1', ref: branch_sha expect(response).to have_http_status(201) @@ -283,14 +283,14 @@ describe API::Branches, api: true do it "denies for user without push access" do post api("/projects/#{project.id}/repository/branches", user2), - branch_name: branch_name, + branch: branch_name, ref: branch_sha expect(response).to have_http_status(403) end it 'returns 400 if branch name is invalid' do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new design', + branch: 'new design', ref: branch_sha expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch name is invalid') @@ -298,12 +298,12 @@ describe API::Branches, api: true do it 'returns 400 if branch already exists' do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new_design1', + branch: 'new_design1', ref: branch_sha expect(response).to have_http_status(201) post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new_design1', + branch: 'new_design1', ref: branch_sha expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch already exists') @@ -311,7 +311,7 @@ describe API::Branches, api: true do it 'returns 400 if ref name is invalid' do post api("/projects/#{project.id}/repository/branches", user), - branch_name: 'new_design3', + branch: 'new_design3', ref: 'foo' expect(response).to have_http_status(400) expect(json_response['message']).to eq('Invalid reference name') @@ -326,14 +326,14 @@ describe API::Branches, api: true do it "removes branch" do delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) expect(response).to have_http_status(200) - expect(json_response['branch_name']).to eq(branch_name) + expect(json_response['branch']).to eq(branch_name) end it "removes a branch with dots in the branch name" do delete api("/projects/#{project.id}/repository/branches/with.1.2.3", user) expect(response).to have_http_status(200) - expect(json_response['branch_name']).to eq("with.1.2.3") + expect(json_response['branch']).to eq("with.1.2.3") end it 'returns 404 if branch not exists' do diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 38aef7f2767..76a10a2374c 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -16,6 +16,8 @@ describe API::Builds, api: true do let(:query) { '' } before do + create(:ci_build, :skipped, pipeline: pipeline) + get api("/projects/#{project.id}/builds?#{query}", api_user) end @@ -49,6 +51,18 @@ describe API::Builds, api: true do end end + context 'filter project with scope skipped' do + let(:query) { 'scope=skipped' } + let(:json_build) { json_response.first } + + it 'return builds with status skipped' do + expect(response).to have_http_status 200 + expect(json_response).to be_an Array + expect(json_response.length).to eq 1 + expect(json_build['status']).to eq 'skipped' + end + end + context 'filter project with array of scope elements' do let(:query) { 'scope[0]=pending&scope[1]=running' } diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index ecc6a597869..8b3dfedc5a9 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -107,7 +107,7 @@ describe API::Commits, api: true do let(:message) { 'Created file' } let!(:invalid_c_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -120,7 +120,7 @@ describe API::Commits, api: true do end let!(:valid_c_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -162,7 +162,7 @@ describe API::Commits, api: true do let(:message) { 'Deleted file' } let!(:invalid_d_params) do { - branch_name: 'markdown', + branch: 'markdown', commit_message: message, actions: [ { @@ -174,7 +174,7 @@ describe API::Commits, api: true do end let!(:valid_d_params) do { - branch_name: 'markdown', + branch: 'markdown', commit_message: message, actions: [ { @@ -203,7 +203,7 @@ describe API::Commits, api: true do let(:message) { 'Moved file' } let!(:invalid_m_params) do { - branch_name: 'feature', + branch: 'feature', commit_message: message, actions: [ { @@ -217,7 +217,7 @@ describe API::Commits, api: true do end let!(:valid_m_params) do { - branch_name: 'feature', + branch: 'feature', commit_message: message, actions: [ { @@ -248,7 +248,7 @@ describe API::Commits, api: true do let(:message) { 'Updated file' } let!(:invalid_u_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -261,7 +261,7 @@ describe API::Commits, api: true do end let!(:valid_u_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -291,7 +291,7 @@ describe API::Commits, api: true do let(:message) { 'Multiple actions' } let!(:invalid_mo_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { @@ -319,7 +319,7 @@ describe API::Commits, api: true do end let!(:valid_mo_params) do { - branch_name: 'master', + branch: 'master', commit_message: message, actions: [ { diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 5e26e779366..a8ce0430401 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -104,7 +104,7 @@ describe API::Files, api: true do let(:valid_params) do { file_path: 'newfile.rb', - branch_name: 'master', + branch: 'master', content: 'puts 8', commit_message: 'Added newfile' } @@ -153,7 +153,7 @@ describe API::Files, api: true do let(:valid_params) do { file_path: file_path, - branch_name: 'master', + branch: 'master', content: 'puts 8', commit_message: 'Changed file' } @@ -193,7 +193,7 @@ describe API::Files, api: true do let(:valid_params) do { file_path: file_path, - branch_name: 'master', + branch: 'master', commit_message: 'Changed file' } end @@ -241,7 +241,7 @@ describe API::Files, api: true do let(:put_params) do { file_path: file_path, - branch_name: 'master', + branch: 'master', content: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=', commit_message: 'Binary file with a \n should not be touched', encoding: 'base64' diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index a59112579e5..9c3a92bedbd 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -303,7 +303,7 @@ describe API::Groups, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response.length).to eq(2) - project_names = json_response.map { |proj| proj['name' ] } + project_names = json_response.map { |proj| proj['name'] } expect(project_names).to match_array([project1.name, project3.name]) expect(json_response.first['visibility_level']).to be_present end @@ -314,7 +314,7 @@ describe API::Groups, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response.length).to eq(2) - project_names = json_response.map { |proj| proj['name' ] } + project_names = json_response.map { |proj| proj['name'] } expect(project_names).to match_array([project1.name, project3.name]) expect(json_response.first['visibility_level']).not_to be_present end @@ -398,7 +398,7 @@ describe API::Groups, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers - project_names = json_response.map { |proj| proj['name' ] } + project_names = json_response.map { |proj| proj['name'] } expect(project_names).to match_array([project1.name, project3.name]) end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index ece1b43567d..7cb75310204 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -775,7 +775,7 @@ describe API::Issues, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('new issue') expect(json_response['description']).to be_nil - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['confidential']).to be_falsy end @@ -1028,6 +1028,33 @@ describe API::Issues, api: true do end end + describe 'PUT /projects/:id/issues/:issue_id with spam filtering' do + let(:params) do + { + title: 'updated title', + description: 'content here', + labels: 'label, label2' + } + end + + it "does not create a new project issue" do + allow_any_instance_of(SpamService).to receive_messages(check_for_spam?: true) + allow_any_instance_of(AkismetService).to receive_messages(is_spam?: true) + + put api("/projects/#{project.id}/issues/#{issue.id}", user), params + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) + + spam_logs = SpamLog.all + expect(spam_logs.count).to eq(1) + expect(spam_logs[0].title).to eq('updated title') + expect(spam_logs[0].description).to eq('content here') + expect(spam_logs[0].user).to eq(user) + expect(spam_logs[0].noteable_type).to eq('Issue') + end + end + describe 'PUT /projects/:id/issues/:issue_id to update labels' do let!(:label) { create(:label, title: 'dummy', project: project) } let!(:label_link) { create(:label_link, label: label, target: issue) } @@ -1232,55 +1259,55 @@ describe API::Issues, api: true do end end - describe 'POST :id/issues/:issue_id/subscription' do + describe 'POST :id/issues/:issue_id/subscribe' do it 'subscribes to an issue' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) + post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user2) expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do - post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) + post api("/projects/#{project.id}/issues/#{issue.id}/subscribe", user) expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do - post api("/projects/#{project.id}/issues/123/subscription", user) + post api("/projects/#{project.id}/issues/123/subscribe", user) expect(response).to have_http_status(404) end it 'returns 404 if the issue is confidential' do - post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member) + post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscribe", non_member) expect(response).to have_http_status(404) end end - describe 'DELETE :id/issues/:issue_id/subscription' do + describe 'POST :id/issues/:issue_id/unsubscribe' do it 'unsubscribes from an issue' do - delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) + post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do - delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) + post api("/projects/#{project.id}/issues/#{issue.id}/unsubscribe", user2) expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do - delete api("/projects/#{project.id}/issues/123/subscription", user) + post api("/projects/#{project.id}/issues/123/unsubscribe", user) expect(response).to have_http_status(404) end it 'returns 404 if the issue is confidential' do - delete api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member) + post api("/projects/#{project.id}/issues/#{confidential_issue.id}/unsubscribe", non_member) expect(response).to have_http_status(404) end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 5d7a76cf3be..af271dbd4f5 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -21,11 +21,11 @@ describe API::Labels, api: true do create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed) create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project ) - expected_keys = [ - 'id', 'name', 'color', 'description', - 'open_issues_count', 'closed_issues_count', 'open_merge_requests_count', - 'subscribed', 'priority' - ] + expected_keys = %w( + id name color description + open_issues_count closed_issues_count open_merge_requests_count + subscribed priority + ) get api("/projects/#{project.id}/labels", user) @@ -318,10 +318,10 @@ describe API::Labels, api: true do end end - describe "POST /projects/:id/labels/:label_id/subscription" do + describe "POST /projects/:id/labels/:label_id/subscribe" do context "when label_id is a label title" do it "subscribes to the label" do - post api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.title}/subscribe", user) expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) @@ -331,7 +331,7 @@ describe API::Labels, api: true do context "when label_id is a label ID" do it "subscribes to the label" do - post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/subscribe", user) expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) @@ -343,7 +343,7 @@ describe API::Labels, api: true do before { label1.subscribe(user, project) } it "returns 304" do - post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/subscribe", user) expect(response).to have_http_status(304) end @@ -351,21 +351,21 @@ describe API::Labels, api: true do context "when label ID is not found" do it "returns 404 error" do - post api("/projects/#{project.id}/labels/1234/subscription", user) + post api("/projects/#{project.id}/labels/1234/subscribe", user) expect(response).to have_http_status(404) end end end - describe "DELETE /projects/:id/labels/:label_id/subscription" do + describe "POST /projects/:id/labels/:label_id/unsubscribe" do before { label1.subscribe(user, project) } context "when label_id is a label title" do it "unsubscribes from the label" do - delete api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.title}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -373,9 +373,9 @@ describe API::Labels, api: true do context "when label_id is a label ID" do it "unsubscribes from the label" do - delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -385,7 +385,7 @@ describe API::Labels, api: true do before { label1.unsubscribe(user, project) } it "returns 304" do - delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + post api("/projects/#{project.id}/labels/#{label1.id}/unsubscribe", user) expect(response).to have_http_status(304) end @@ -393,7 +393,7 @@ describe API::Labels, api: true do context "when label ID is not found" do it "returns 404 error" do - delete api("/projects/#{project.id}/labels/1234/subscription", user) + post api("/projects/#{project.id}/labels/1234/unsubscribe", user) expect(response).to have_http_status(404) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index f4dee4a4ca1..b87d0cd7de9 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -250,7 +250,7 @@ describe API::MergeRequests, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['milestone']['id']).to eq(milestone.id) expect(json_response['force_remove_source_branch']).to be_truthy end @@ -662,22 +662,22 @@ describe API::MergeRequests, api: true do end end - describe 'POST :id/merge_requests/:merge_request_id/subscription' do + describe 'POST :id/merge_requests/:merge_request_id/subscribe' do it 'subscribes to a merge request' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", admin) expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", user) expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do - post api("/projects/#{project.id}/merge_requests/123/subscription", user) + post api("/projects/#{project.id}/merge_requests/123/subscribe", user) expect(response).to have_http_status(404) end @@ -686,28 +686,28 @@ describe API::MergeRequests, api: true do guest = create(:user) project.team << [guest, :guest] - post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", guest) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscribe", guest) expect(response).to have_http_status(403) end end - describe 'DELETE :id/merge_requests/:merge_request_id/subscription' do + describe 'POST :id/merge_requests/:merge_request_id/unsubscribe' do it 'unsubscribes from a merge request' do - delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", user) - expect(response).to have_http_status(200) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do - delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", admin) expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do - post api("/projects/#{project.id}/merge_requests/123/subscription", user) + post api("/projects/#{project.id}/merge_requests/123/unsubscribe", user) expect(response).to have_http_status(404) end @@ -716,7 +716,7 @@ describe API::MergeRequests, api: true do guest = create(:user) project.team << [guest, :guest] - delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", guest) + post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/unsubscribe", guest) expect(response).to have_http_status(403) end diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index f56876bcf54..da9df56401b 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -78,43 +78,33 @@ describe API::ProjectSnippets, api: true do allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) end - context 'when the project is private' do - let(:private_project) { create(:project_empty_repo, :private) } - - context 'when the snippet is public' do - it 'creates the snippet' do - expect { create_snippet(private_project, visibility_level: Snippet::PUBLIC) }. - to change { Snippet.count }.by(1) - end + context 'when the snippet is private' do + it 'creates the snippet' do + expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }. + to change { Snippet.count }.by(1) end end - context 'when the project is public' do - context 'when the snippet is private' do - it 'creates the snippet' do - expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }. - to change { Snippet.count }.by(1) - end + context 'when the snippet is public' do + it 'rejects the shippet' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + not_to change { Snippet.count } + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) end - context 'when the snippet is public' do - it 'rejects the shippet' do - expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. - not_to change { Snippet.count } - expect(response).to have_http_status(400) - end - - it 'creates a spam log' do - expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. - to change { SpamLog.count }.by(1) - end + it 'creates a spam log' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) end end end end describe 'PUT /projects/:project_id/snippets/:id/' do - let(:snippet) { create(:project_snippet, author: admin) } + let(:visibility_level) { Snippet::PUBLIC } + let(:snippet) { create(:project_snippet, author: admin, visibility_level: visibility_level) } it 'updates snippet' do new_content = 'New content' @@ -138,6 +128,56 @@ describe API::ProjectSnippets, api: true do expect(response).to have_http_status(400) end + + context 'when the snippet is spam' do + def update_snippet(snippet_params = {}) + put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}", admin), snippet_params + end + + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end + + context 'when the snippet is private' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'creates the snippet' do + expect { update_snippet(title: 'Foo') }. + to change { snippet.reload.title }.to('Foo') + end + end + + context 'when the snippet is public' do + let(:visibility_level) { Snippet::PUBLIC } + + it 'rejects the snippet' do + expect { update_snippet(title: 'Foo') }. + not_to change { snippet.reload.title } + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo') }. + to change { SpamLog.count }.by(1) + end + end + + context 'when the private snippet is made public' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'rejects the snippet' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + not_to change { snippet.reload.title } + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) + end + end + end end describe 'DELETE /projects/:project_id/snippets/:id/' do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 4e90aae9279..8d139782fdf 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -121,7 +121,7 @@ describe API::Projects, api: true do context 'and with simple=true' do it 'returns a simplified version of all the projects' do - expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"] + expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace) get api('/projects?simple=true', user) @@ -1422,4 +1422,53 @@ describe API::Projects, api: true do end end end + + describe 'POST /projects/:id/housekeeping' do + let(:housekeeping) { Projects::HousekeepingService.new(project) } + + before do + allow(Projects::HousekeepingService).to receive(:new).with(project).and_return(housekeeping) + end + + context 'when authenticated as owner' do + it 'starts the housekeeping process' do + expect(housekeeping).to receive(:execute).once + + post api("/projects/#{project.id}/housekeeping", user) + + expect(response).to have_http_status(201) + end + + context 'when housekeeping lease is taken' do + it 'returns conflict' do + expect(housekeeping).to receive(:execute).once.and_raise(Projects::HousekeepingService::LeaseTaken) + + post api("/projects/#{project.id}/housekeeping", user) + + expect(response).to have_http_status(409) + expect(json_response['message']).to match(/Somebody already triggered housekeeping for this project/) + end + end + end + + context 'when authenticated as developer' do + before do + project_member2 + end + + it 'returns forbidden error' do + post api("/projects/#{project.id}/housekeeping", user3) + + expect(response).to have_http_status(403) + end + end + + context 'when unauthenticated' do + it 'returns authentication error' do + post api("/projects/#{project.id}/housekeeping") + + expect(response).to have_http_status(401) + end + end + end end diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb index 1ef92930b3c..41def7cd1d4 100644 --- a/spec/requests/api/snippets_spec.rb +++ b/spec/requests/api/snippets_spec.rb @@ -129,7 +129,9 @@ describe API::Snippets, api: true do it 'rejects the shippet' do expect { create_snippet(visibility_level: Snippet::PUBLIC) }. not_to change { Snippet.count } + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) end it 'creates a spam log' do @@ -141,16 +143,20 @@ describe API::Snippets, api: true do end describe 'PUT /snippets/:id' do + let(:visibility_level) { Snippet::PUBLIC } let(:other_user) { create(:user) } - let(:public_snippet) { create(:personal_snippet, :public, author: user) } + let(:snippet) do + create(:personal_snippet, author: user, visibility_level: visibility_level) + end + it 'updates snippet' do new_content = 'New content' - put api("/snippets/#{public_snippet.id}", user), content: new_content + put api("/snippets/#{snippet.id}", user), content: new_content expect(response).to have_http_status(200) - public_snippet.reload - expect(public_snippet.content).to eq(new_content) + snippet.reload + expect(snippet.content).to eq(new_content) end it 'returns 404 for invalid snippet id' do @@ -161,7 +167,7 @@ describe API::Snippets, api: true do end it "returns 404 for another user's snippet" do - put api("/snippets/#{public_snippet.id}", other_user), title: 'fubar' + put api("/snippets/#{snippet.id}", other_user), title: 'fubar' expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Snippet Not Found') @@ -172,6 +178,56 @@ describe API::Snippets, api: true do expect(response).to have_http_status(400) end + + context 'when the snippet is spam' do + def update_snippet(snippet_params = {}) + put api("/snippets/#{snippet.id}", user), snippet_params + end + + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end + + context 'when the snippet is private' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'updates the snippet' do + expect { update_snippet(title: 'Foo') }. + to change { snippet.reload.title }.to('Foo') + end + end + + context 'when the snippet is public' do + let(:visibility_level) { Snippet::PUBLIC } + + it 'rejects the shippet' do + expect { update_snippet(title: 'Foo') }. + not_to change { snippet.reload.title } + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo') }. + to change { SpamLog.count }.by(1) + end + end + + context 'when a private snippet is made public' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'rejects the snippet' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + not_to change { snippet.reload.title } + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) + end + end + end end describe 'DELETE /snippets/:id' do diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index 8506e8fccde..2c83e119065 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -58,11 +58,11 @@ describe API::Templates, api: true do expect(json_response['popular']).to be true expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') - expect(json_response['description']).to include('A permissive license that is short and to the point.') + expect(json_response['description']).to include('A short and simple permissive license with conditions') expect(json_response['conditions']).to eq(%w[include-copyright]) expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) expect(json_response['limitations']).to eq(%w[no-liability]) - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end end @@ -73,7 +73,7 @@ describe API::Templates, api: true do expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(15) + expect(json_response.size).to eq(12) expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') end @@ -102,7 +102,7 @@ describe API::Templates, api: true do let(:license_type) { 'mit' } it 'returns the license text' do - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end it 'replaces placeholder values' do diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index 2069d2a7c75..f35e963a14b 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -107,46 +107,47 @@ describe API::Todos, api: true do end end - describe 'DELETE /todos/:id' do + describe 'POST /todos/:id/mark_as_done' do context 'when unauthenticated' do it 'returns authentication error' do - delete api("/todos/#{pending_1.id}") + post api("/todos/#{pending_1.id}/mark_as_done") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'marks a todo as done' do - delete api("/todos/#{pending_1.id}", john_doe) + post api("/todos/#{pending_1.id}/mark_as_done", john_doe) - expect(response.status).to eq(200) + expect(response).to have_http_status(201) + expect(json_response['id']).to eq(pending_1.id) + expect(json_response['state']).to eq('done') expect(pending_1.reload).to be_done end it 'updates todos cache' do expect_any_instance_of(User).to receive(:update_todos_count_cache).and_call_original - delete api("/todos/#{pending_1.id}", john_doe) + post api("/todos/#{pending_1.id}/mark_as_done", john_doe) end end end - describe 'DELETE /todos' do + describe 'POST /mark_as_done' do context 'when unauthenticated' do it 'returns authentication error' do - delete api('/todos') + post api('/todos/mark_as_done') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'marks all todos as done' do - delete api('/todos', john_doe) + post api('/todos/mark_as_done', john_doe) - expect(response.status).to eq(200) - expect(response.body).to eq('3') + expect(response).to have_http_status(204) expect(pending_1.reload).to be_done expect(pending_2.reload).to be_done expect(pending_3.reload).to be_done @@ -155,7 +156,7 @@ describe API::Todos, api: true do it 'updates todos cache' do expect_any_instance_of(User).to receive(:update_todos_count_cache).and_call_original - delete api("/todos", john_doe) + post api("/todos/mark_as_done", john_doe) end end end diff --git a/spec/requests/api/v3/commits_spec.rb b/spec/requests/api/v3/commits_spec.rb new file mode 100644 index 00000000000..2d7584c3e59 --- /dev/null +++ b/spec/requests/api/v3/commits_spec.rb @@ -0,0 +1,578 @@ +require 'spec_helper' +require 'mime/types' + +describe API::V3::Commits, api: true do + include ApiHelpers + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:guest) { create(:project_member, :guest, user: user2, project: project) } + let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } + let!(:another_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'another comment on a commit') } + + before { project.team << [user, :reporter] } + + describe "List repository commits" do + context "authorized user" do + before { project.team << [user2, :reporter] } + + it "returns project commits" do + commit = project.repository.commit + get v3_api("/projects/#{project.id}/repository/commits", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first['id']).to eq(commit.id) + expect(json_response.first['committer_name']).to eq(commit.committer_name) + expect(json_response.first['committer_email']).to eq(commit.committer_email) + end + end + + context "unauthorized user" do + it "does not return project commits" do + get v3_api("/projects/#{project.id}/repository/commits") + expect(response).to have_http_status(401) + end + end + + context "since optional parameter" do + it "returns project commits since provided parameter" do + commits = project.repository.commits("master") + since = commits.second.created_at + + get v3_api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user) + + expect(json_response.size).to eq 2 + expect(json_response.first["id"]).to eq(commits.first.id) + expect(json_response.second["id"]).to eq(commits.second.id) + end + end + + context "until optional parameter" do + it "returns project commits until provided parameter" do + commits = project.repository.commits("master") + before = commits.second.created_at + + get v3_api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user) + + if commits.size >= 20 + expect(json_response.size).to eq(20) + else + expect(json_response.size).to eq(commits.size - 1) + end + + expect(json_response.first["id"]).to eq(commits.second.id) + expect(json_response.second["id"]).to eq(commits.third.id) + end + end + + context "invalid xmlschema date parameters" do + it "returns an invalid parameter error message" do + get v3_api("/projects/#{project.id}/repository/commits?since=invalid-date", user) + + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('since is invalid') + end + end + + context "path optional parameter" do + it "returns project commits matching provided path parameter" do + path = 'files/ruby/popen.rb' + + get v3_api("/projects/#{project.id}/repository/commits?path=#{path}", user) + + expect(json_response.size).to eq(3) + expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d") + end + end + end + + describe "Create a commit with multiple files and actions" do + let!(:url) { "/projects/#{project.id}/repository/commits" } + + it 'returns a 403 unauthorized for user without permissions' do + post v3_api(url, user2) + + expect(response).to have_http_status(403) + end + + it 'returns a 400 bad request if no params are given' do + post v3_api(url, user) + + expect(response).to have_http_status(400) + end + + context :create do + let(:message) { 'Created file' } + let!(:invalid_c_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + } + ] + } + end + let!(:valid_c_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'foo/bar/baz.txt', + content: 'puts 8' + } + ] + } + end + + it 'a new file in project repo' do + post v3_api(url, user), valid_c_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + expect(json_response['committer_name']).to eq(user.name) + expect(json_response['committer_email']).to eq(user.email) + end + + it 'returns a 400 bad request if file exists' do + post v3_api(url, user), invalid_c_params + + expect(response).to have_http_status(400) + end + + context 'with project path in URL' do + let(:url) { "/projects/#{project.namespace.path}%2F#{project.path}/repository/commits" } + + it 'a new file in project repo' do + post v3_api(url, user), valid_c_params + + expect(response).to have_http_status(201) + end + end + end + + context :delete do + let(:message) { 'Deleted file' } + let!(:invalid_d_params) do + { + branch_name: 'markdown', + commit_message: message, + actions: [ + { + action: 'delete', + file_path: 'doc/api/projects.md' + } + ] + } + end + let!(:valid_d_params) do + { + branch_name: 'markdown', + commit_message: message, + actions: [ + { + action: 'delete', + file_path: 'doc/api/users.md' + } + ] + } + end + + it 'an existing file in project repo' do + post v3_api(url, user), valid_d_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'returns a 400 bad request if file does not exist' do + post v3_api(url, user), invalid_d_params + + expect(response).to have_http_status(400) + end + end + + context :move do + let(:message) { 'Moved file' } + let!(:invalid_m_params) do + { + branch_name: 'feature', + commit_message: message, + actions: [ + { + action: 'move', + file_path: 'CHANGELOG', + previous_path: 'VERSION', + content: '6.7.0.pre' + } + ] + } + end + let!(:valid_m_params) do + { + branch_name: 'feature', + commit_message: message, + actions: [ + { + action: 'move', + file_path: 'VERSION.txt', + previous_path: 'VERSION', + content: '6.7.0.pre' + } + ] + } + end + + it 'an existing file in project repo' do + post v3_api(url, user), valid_m_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'returns a 400 bad request if file does not exist' do + post v3_api(url, user), invalid_m_params + + expect(response).to have_http_status(400) + end + end + + context :update do + let(:message) { 'Updated file' } + let!(:invalid_u_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'update', + file_path: 'foo/bar.baz', + content: 'puts 8' + } + ] + } + end + let!(:valid_u_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'update', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + } + ] + } + end + + it 'an existing file in project repo' do + post v3_api(url, user), valid_u_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'returns a 400 bad request if file does not exist' do + post v3_api(url, user), invalid_u_params + + expect(response).to have_http_status(400) + end + end + + context "multiple operations" do + let(:message) { 'Multiple actions' } + let!(:invalid_mo_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + }, + { + action: 'delete', + file_path: 'doc/v3_api/projects.md' + }, + { + action: 'move', + file_path: 'CHANGELOG', + previous_path: 'VERSION', + content: '6.7.0.pre' + }, + { + action: 'update', + file_path: 'foo/bar.baz', + content: 'puts 8' + } + ] + } + end + let!(:valid_mo_params) do + { + branch_name: 'master', + commit_message: message, + actions: [ + { + action: 'create', + file_path: 'foo/bar/baz.txt', + content: 'puts 8' + }, + { + action: 'delete', + file_path: 'Gemfile.zip' + }, + { + action: 'move', + file_path: 'VERSION.txt', + previous_path: 'VERSION', + content: '6.7.0.pre' + }, + { + action: 'update', + file_path: 'files/ruby/popen.rb', + content: 'puts 8' + } + ] + } + end + + it 'are commited as one in project repo' do + post v3_api(url, user), valid_mo_params + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(message) + end + + it 'return a 400 bad request if there are any issues' do + post v3_api(url, user), invalid_mo_params + + expect(response).to have_http_status(400) + end + end + end + + describe "Get a single commit" do + context "authorized user" do + it "returns a commit by sha" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['id']).to eq(project.repository.commit.id) + expect(json_response['title']).to eq(project.repository.commit.title) + expect(json_response['stats']['additions']).to eq(project.repository.commit.stats.additions) + expect(json_response['stats']['deletions']).to eq(project.repository.commit.stats.deletions) + expect(json_response['stats']['total']).to eq(project.repository.commit.stats.total) + end + + it "returns a 404 error if not found" do + get v3_api("/projects/#{project.id}/repository/commits/invalid_sha", user) + expect(response).to have_http_status(404) + end + + it "returns nil for commit without CI" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['status']).to be_nil + end + + it "returns status for CI" do + pipeline = project.ensure_pipeline('master', project.repository.commit.sha) + pipeline.update(status: 'success') + + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['status']).to eq(pipeline.status) + end + + it "returns status for CI when pipeline is created" do + project.ensure_pipeline('master', project.repository.commit.sha) + + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['status']).to eq("created") + end + end + + context "unauthorized user" do + it "does not return the selected commit" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}") + expect(response).to have_http_status(401) + end + end + end + + describe "Get the diff of a commit" do + context "authorized user" do + before { project.team << [user2, :reporter] } + + it "returns the diff of the selected commit" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user) + expect(response).to have_http_status(200) + + expect(json_response).to be_an Array + expect(json_response.length).to be >= 1 + expect(json_response.first.keys).to include "diff" + end + + it "returns a 404 error if invalid commit" do + get v3_api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user) + expect(response).to have_http_status(404) + end + end + + context "unauthorized user" do + it "does not return the diff of the selected commit" do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff") + expect(response).to have_http_status(401) + end + end + end + + describe 'Get the comments of a commit' do + context 'authorized user' do + it 'returns merge_request comments' do + get v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + expect(json_response.first['note']).to eq('a comment on a commit') + expect(json_response.first['author']['id']).to eq(user.id) + end + + it 'returns a 404 error if merge_request_id not found' do + get v3_api("/projects/#{project.id}/repository/commits/1234ab/comments", user) + expect(response).to have_http_status(404) + end + end + + context 'unauthorized user' do + it 'does not return the diff of the selected commit' do + get v3_api("/projects/#{project.id}/repository/commits/1234ab/comments") + expect(response).to have_http_status(401) + end + end + end + + describe 'POST :id/repository/commits/:sha/cherry_pick' do + let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } + + context 'authorized user' do + it 'cherry picks a commit' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'master' + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(master_pickable_commit.title) + expect(json_response['message']).to eq(master_pickable_commit.message) + expect(json_response['author_name']).to eq(master_pickable_commit.author_name) + expect(json_response['committer_name']).to eq(user.name) + end + + it 'returns 400 if commit is already included in the target branch' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'markdown' + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq('Sorry, we cannot cherry-pick this commit automatically. + A cherry-pick may have already been performed with this commit, or a more recent commit may have updated some of its content.') + end + + it 'returns 400 if you are not allowed to push to the target branch' do + project.team << [user2, :developer] + protected_branch = create(:protected_branch, project: project, name: 'feature') + + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user2), branch: protected_branch.name + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq('You are not allowed to push into this branch') + end + + it 'returns 400 for missing parameters' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user) + + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('branch is missing') + end + + it 'returns 404 if commit is not found' do + post v3_api("/projects/#{project.id}/repository/commits/abcd0123/cherry_pick", user), branch: 'master' + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Commit Not Found') + end + + it 'returns 404 if branch is not found' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user), branch: 'foo' + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Branch Not Found') + end + + it 'returns 400 for missing parameters' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick", user) + + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('branch is missing') + end + end + + context 'unauthorized user' do + it 'does not cherry pick the commit' do + post v3_api("/projects/#{project.id}/repository/commits/#{master_pickable_commit.id}/cherry_pick"), branch: 'master' + + expect(response).to have_http_status(401) + end + end + end + + describe 'Post comment to commit' do + context 'authorized user' do + it 'returns comment' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment' + expect(response).to have_http_status(201) + expect(json_response['note']).to eq('My comment') + expect(json_response['path']).to be_nil + expect(json_response['line']).to be_nil + expect(json_response['line_type']).to be_nil + end + + it 'returns the inline comment' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.raw_diffs.first.new_path, line: 1, line_type: 'new' + + expect(response).to have_http_status(201) + expect(json_response['note']).to eq('My comment') + expect(json_response['path']).to eq(project.repository.commit.raw_diffs.first.new_path) + expect(json_response['line']).to eq(1) + expect(json_response['line_type']).to eq('new') + end + + it 'returns 400 if note is missing' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) + expect(response).to have_http_status(400) + end + + it 'returns 404 if note is attached to non existent commit' do + post v3_api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment' + expect(response).to have_http_status(404) + end + end + + context 'unauthorized user' do + it 'does not return the diff of the selected commit' do + post v3_api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments") + expect(response).to have_http_status(401) + end + end + end +end diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb new file mode 100644 index 00000000000..4af05605ec6 --- /dev/null +++ b/spec/requests/api/v3/files_spec.rb @@ -0,0 +1,270 @@ +require 'spec_helper' + +describe API::V3::Files, api: true do + include ApiHelpers + let(:user) { create(:user) } + let!(:project) { create(:project, :repository, namespace: user.namespace ) } + let(:guest) { create(:user) { |u| project.add_guest(u) } } + let(:file_path) { 'files/ruby/popen.rb' } + let(:params) do + { + file_path: file_path, + ref: 'master' + } + end + let(:author_email) { FFaker::Internet.email } + + # I have to remove periods from the end of the name + # This happened when the user's name had a suffix (i.e. "Sr.") + # This seems to be what git does under the hood. For example, this commit: + # + # $ git commit --author='Foo Sr. <foo@example.com>' -m 'Where's my trailing period?' + # + # results in this: + # + # $ git show --pretty + # ... + # Author: Foo Sr <foo@example.com> + # ... + let(:author_name) { FFaker::Name.name.chomp("\.") } + + before { project.team << [user, :developer] } + + describe "GET /projects/:id/repository/files" do + let(:route) { "/projects/#{project.id}/repository/files" } + + shared_examples_for 'repository files' do + it "returns file info" do + get v3_api(route, current_user), params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + expect(json_response['file_name']).to eq('popen.rb') + expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") + end + + context 'when no params are given' do + it_behaves_like '400 response' do + let(:request) { get v3_api(route, current_user) } + end + end + + context 'when file_path does not exist' do + let(:params) do + { + file_path: 'app/models/application.rb', + ref: 'master', + } + end + + it_behaves_like '404 response' do + let(:request) { get v3_api(route, current_user), params } + let(:message) { '404 File Not Found' } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get v3_api(route, current_user), params } + end + end + end + + context 'when unauthenticated', 'and project is public' do + it_behaves_like 'repository files' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get v3_api(route), params } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do + it_behaves_like 'repository files' do + let(:current_user) { user } + end + end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get v3_api(route, guest), params } + end + end + end + + describe "POST /projects/:id/repository/files" do + let(:valid_params) do + { + file_path: 'newfile.rb', + branch_name: 'master', + content: 'puts 8', + commit_message: 'Added newfile' + } + end + + it "creates a new file in project repo" do + post v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(201) + expect(json_response['file_path']).to eq('newfile.rb') + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(user.email) + expect(last_commit.author_name).to eq(user.name) + end + + it "returns a 400 bad request if no params given" do + post v3_api("/projects/#{project.id}/repository/files", user) + + expect(response).to have_http_status(400) + end + + it "returns a 400 if editor fails to create file" do + allow_any_instance_of(Repository).to receive(:commit_file). + and_return(false) + + post v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(400) + end + + context "when specifying an author" do + it "creates a new file with the specified author" do + valid_params.merge!(author_email: author_email, author_name: author_name) + + post v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(201) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(author_email) + expect(last_commit.author_name).to eq(author_name) + end + end + end + + describe "PUT /projects/:id/repository/files" do + let(:valid_params) do + { + file_path: file_path, + branch_name: 'master', + content: 'puts 8', + commit_message: 'Changed file' + } + end + + it "updates existing file in project repo" do + put v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(user.email) + expect(last_commit.author_name).to eq(user.name) + end + + it "returns a 400 bad request if no params given" do + put v3_api("/projects/#{project.id}/repository/files", user) + + expect(response).to have_http_status(400) + end + + context "when specifying an author" do + it "updates a file with the specified author" do + valid_params.merge!(author_email: author_email, author_name: author_name, content: "New content") + + put v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(author_email) + expect(last_commit.author_name).to eq(author_name) + end + end + end + + describe "DELETE /projects/:id/repository/files" do + let(:valid_params) do + { + file_path: file_path, + branch_name: 'master', + commit_message: 'Changed file' + } + end + + it "deletes existing file in project repo" do + delete v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(user.email) + expect(last_commit.author_name).to eq(user.name) + end + + it "returns a 400 bad request if no params given" do + delete v3_api("/projects/#{project.id}/repository/files", user) + + expect(response).to have_http_status(400) + end + + it "returns a 400 if fails to create file" do + allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) + + delete v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(400) + end + + context "when specifying an author" do + it "removes a file with the specified author" do + valid_params.merge!(author_email: author_email, author_name: author_name) + + delete v3_api("/projects/#{project.id}/repository/files", user), valid_params + + expect(response).to have_http_status(200) + last_commit = project.repository.commit.raw + expect(last_commit.author_email).to eq(author_email) + expect(last_commit.author_name).to eq(author_name) + end + end + end + + describe "POST /projects/:id/repository/files with binary file" do + let(:file_path) { 'test.bin' } + let(:put_params) do + { + file_path: file_path, + branch_name: 'master', + content: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=', + commit_message: 'Binary file with a \n should not be touched', + encoding: 'base64' + } + end + let(:get_params) do + { + file_path: file_path, + ref: 'master', + } + end + + before do + post v3_api("/projects/#{project.id}/repository/files", user), put_params + end + + it "remains unchanged" do + get v3_api("/projects/#{project.id}/repository/files", user), get_params + + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + expect(json_response['file_name']).to eq(file_path) + expect(json_response['content']).to eq(put_params[:content]) + end + end +end diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb index 33a127de98a..803acd55470 100644 --- a/spec/requests/api/v3/issues_spec.rb +++ b/spec/requests/api/v3/issues_spec.rb @@ -722,7 +722,7 @@ describe API::V3::Issues, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('new issue') expect(json_response['description']).to be_nil - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['confidential']).to be_falsy end @@ -986,6 +986,33 @@ describe API::V3::Issues, api: true do end end + describe 'PUT /projects/:id/issues/:issue_id with spam filtering' do + let(:params) do + { + title: 'updated title', + description: 'content here', + labels: 'label, label2' + } + end + + it "does not create a new project issue" do + allow_any_instance_of(SpamService).to receive_messages(check_for_spam?: true) + allow_any_instance_of(AkismetService).to receive_messages(is_spam?: true) + + put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), params + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) + + spam_logs = SpamLog.all + expect(spam_logs.count).to eq(1) + expect(spam_logs[0].title).to eq('updated title') + expect(spam_logs[0].description).to eq('content here') + expect(spam_logs[0].user).to eq(user) + expect(spam_logs[0].noteable_type).to eq('Issue') + end + end + describe 'PUT /projects/:id/issues/:issue_id to update labels' do let!(:label) { create(:label, title: 'dummy', project: project) } let!(:label_link) { create(:label_link, label: label, target: issue) } diff --git a/spec/requests/api/v3/labels_spec.rb b/spec/requests/api/v3/labels_spec.rb index 18e2c0d40c8..f44403374e9 100644 --- a/spec/requests/api/v3/labels_spec.rb +++ b/spec/requests/api/v3/labels_spec.rb @@ -21,11 +21,11 @@ describe API::V3::Labels, api: true do create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed) create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project ) - expected_keys = [ - 'id', 'name', 'color', 'description', - 'open_issues_count', 'closed_issues_count', 'open_merge_requests_count', - 'subscribed', 'priority' - ] + expected_keys = %w( + id name color description + open_issues_count closed_issues_count open_merge_requests_count + subscribed priority + ) get v3_api("/projects/#{project.id}/labels", user) @@ -67,4 +67,86 @@ describe API::V3::Labels, api: true do expect(priority_label_response['subscribed']).to be_falsey end end + + describe "POST /projects/:id/labels/:label_id/subscription" do + context "when label_id is a label title" do + it "subscribes to the label" do + post v3_api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + + expect(response).to have_http_status(201) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_truthy + end + end + + context "when label_id is a label ID" do + it "subscribes to the label" do + post v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(201) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_truthy + end + end + + context "when user is already subscribed to label" do + before { label1.subscribe(user, project) } + + it "returns 304" do + post v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(304) + end + end + + context "when label ID is not found" do + it "returns 404 error" do + post v3_api("/projects/#{project.id}/labels/1234/subscription", user) + + expect(response).to have_http_status(404) + end + end + end + + describe "DELETE /projects/:id/labels/:label_id/subscription" do + before { label1.subscribe(user, project) } + + context "when label_id is a label title" do + it "unsubscribes from the label" do + delete v3_api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) + + expect(response).to have_http_status(200) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_falsey + end + end + + context "when label_id is a label ID" do + it "unsubscribes from the label" do + delete v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(200) + expect(json_response["name"]).to eq(label1.title) + expect(json_response["subscribed"]).to be_falsey + end + end + + context "when user is already unsubscribed from label" do + before { label1.unsubscribe(user, project) } + + it "returns 304" do + delete v3_api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) + + expect(response).to have_http_status(304) + end + end + + context "when label ID is not found" do + it "returns 404 error" do + delete v3_api("/projects/#{project.id}/labels/1234/subscription", user) + + expect(response).to have_http_status(404) + end + end + end end diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb index b94e1ef4ced..51764d1000e 100644 --- a/spec/requests/api/v3/merge_requests_spec.rb +++ b/spec/requests/api/v3/merge_requests_spec.rb @@ -237,7 +237,7 @@ describe API::MergeRequests, api: true do expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') - expect(json_response['labels']).to eq(['label', 'label2']) + expect(json_response['labels']).to eq(%w(label label2)) expect(json_response['milestone']['id']).to eq(milestone.id) expect(json_response['force_remove_source_branch']).to be_truthy end diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb new file mode 100644 index 00000000000..b51cb3055d5 --- /dev/null +++ b/spec/requests/api/v3/notes_spec.rb @@ -0,0 +1,432 @@ +require 'spec_helper' + +describe API::V3::Notes, api: true do + include ApiHelpers + let(:user) { create(:user) } + let!(:project) { create(:empty_project, :public, namespace: user.namespace) } + let!(:issue) { create(:issue, project: project, author: user) } + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) } + let!(:snippet) { create(:project_snippet, project: project, author: user) } + let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) } + let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) } + let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) } + + # For testing the cross-reference of a private issue in a public issue + let(:private_user) { create(:user) } + let(:private_project) do + create(:empty_project, namespace: private_user.namespace). + tap { |p| p.team << [private_user, :master] } + end + let(:private_issue) { create(:issue, project: private_project) } + + let(:ext_proj) { create(:empty_project, :public) } + let(:ext_issue) { create(:issue, project: ext_proj) } + + let!(:cross_reference_note) do + create :note, + noteable: ext_issue, project: ext_proj, + note: "mentioned in issue #{private_issue.to_reference(ext_proj)}", + system: true + end + + before { project.team << [user, :reporter] } + + describe "GET /projects/:id/noteable/:noteable_id/notes" do + context "when noteable is an Issue" do + it "returns an array of issue notes" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(issue_note.note) + expect(json_response.first['upvote']).to be_falsey + expect(json_response.first['downvote']).to be_falsey + end + + it "returns a 404 error when issue id not found" do + get v3_api("/projects/#{project.id}/issues/12345/notes", user) + + expect(response).to have_http_status(404) + end + + context "and current user cannot view the notes" do + it "returns an empty array" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response).to be_empty + end + + context "and issue is confidential" do + before { ext_issue.update_attributes(confidential: true) } + + it "returns 404" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) + + expect(response).to have_http_status(404) + end + end + + context "and current user can view the note" do + it "returns an empty array" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(cross_reference_note.note) + end + end + end + end + + context "when noteable is a Snippet" do + it "returns an array of snippet notes" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(snippet_note.note) + end + + it "returns a 404 error when snippet id not found" do + get v3_api("/projects/#{project.id}/snippets/42/notes", user) + + expect(response).to have_http_status(404) + end + + it "returns 404 when not authorized" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user) + + expect(response).to have_http_status(404) + end + end + + context "when noteable is a Merge Request" do + it "returns an array of merge_requests notes" do + get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first['body']).to eq(merge_request_note.note) + end + + it "returns a 404 error if merge request id not found" do + get v3_api("/projects/#{project.id}/merge_requests/4444/notes", user) + + expect(response).to have_http_status(404) + end + + it "returns 404 when not authorized" do + get v3_api("/projects/#{project.id}/merge_requests/4444/notes", private_user) + + expect(response).to have_http_status(404) + end + end + end + + describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do + context "when noteable is an Issue" do + it "returns an issue note by id" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq(issue_note.note) + end + + it "returns a 404 error if issue note not found" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + + context "and current user cannot view the note" do + it "returns a 404 error" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user) + + expect(response).to have_http_status(404) + end + + context "when issue is confidential" do + before { issue.update_attributes(confidential: true) } + + it "returns 404" do + get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user) + + expect(response).to have_http_status(404) + end + end + + context "and current user can view the note" do + it "returns an issue note by id" do + get v3_api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user) + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq(cross_reference_note.note) + end + end + end + end + + context "when noteable is a Snippet" do + it "returns a snippet note by id" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq(snippet_note.note) + end + + it "returns a 404 error if snippet note not found" do + get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + end + end + + describe "POST /projects/:id/noteable/:noteable_id/notes" do + context "when noteable is an Issue" do + it "creates a new issue note" do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + end + + it "returns a 400 bad request error if body not given" do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user) + + expect(response).to have_http_status(400) + end + + it "returns a 401 unauthorized error if user not authenticated" do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' + + expect(response).to have_http_status(401) + end + + context 'when an admin or owner makes the request' do + it 'accepts the creation date to be set' do + creation_time = 2.weeks.ago + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), + body: 'hi!', created_at: creation_time + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + end + end + + context 'when the user is posting an award emoji on an issue created by someone else' do + let(:issue2) { create(:issue, project: project) } + + it 'returns an award emoji' do + post v3_api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:' + + expect(response).to have_http_status(201) + expect(json_response['awardable_id']).to eq issue2.id + end + end + + context 'when the user is posting an award emoji on his/her own issue' do + it 'creates a new issue note' do + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:' + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq(':+1:') + end + end + end + + context "when noteable is a Snippet" do + it "creates a new snippet note" do + post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!' + + expect(response).to have_http_status(201) + expect(json_response['body']).to eq('hi!') + expect(json_response['author']['username']).to eq(user.username) + end + + it "returns a 400 bad request error if body not given" do + post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) + + expect(response).to have_http_status(400) + end + + it "returns a 401 unauthorized error if user not authenticated" do + post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' + + expect(response).to have_http_status(401) + end + end + + context 'when user does not have access to read the noteable' do + it 'responds with 404' do + project = create(:empty_project, :private) { |p| p.add_guest(user) } + issue = create(:issue, :confidential, project: project) + + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), + body: 'Foo' + + expect(response).to have_http_status(404) + end + end + + context 'when user does not have access to create noteable' do + let(:private_issue) { create(:issue, project: create(:empty_project, :private)) } + + ## + # We are posting to project user has access to, but we use issue id + # from a different project, see #15577 + # + before do + post v3_api("/projects/#{project.id}/issues/#{private_issue.id}/notes", user), + body: 'Hi!' + end + + it 'responds with resource not found error' do + expect(response.status).to eq 404 + end + + it 'does not create new note' do + expect(private_issue.notes.reload).to be_empty + end + end + end + + describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do + it "creates an activity event when an issue note is created" do + expect(Event).to receive(:create) + + post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' + end + end + + describe 'PUT /projects/:id/noteable/:noteable_id/notes/:note_id' do + context 'when noteable is an Issue' do + it 'returns modified note' do + put v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user), body: 'Hello!' + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq('Hello!') + end + + it 'returns a 404 error when note id not found' do + put v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user), + body: 'Hello!' + + expect(response).to have_http_status(404) + end + + it 'returns a 400 bad request error if body not given' do + put v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) + + expect(response).to have_http_status(400) + end + end + + context 'when noteable is a Snippet' do + it 'returns modified note' do + put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user), body: 'Hello!' + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq('Hello!') + end + + it 'returns a 404 error when note id not found' do + put v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/12345", user), body: "Hello!" + + expect(response).to have_http_status(404) + end + end + + context 'when noteable is a Merge Request' do + it 'returns modified note' do + put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ + "notes/#{merge_request_note.id}", user), body: 'Hello!' + + expect(response).to have_http_status(200) + expect(json_response['body']).to eq('Hello!') + end + + it 'returns a 404 error when note id not found' do + put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ + "notes/12345", user), body: "Hello!" + + expect(response).to have_http_status(404) + end + end + end + + describe 'DELETE /projects/:id/noteable/:noteable_id/notes/:note_id' do + context 'when noteable is an Issue' do + it 'deletes a note' do + delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete v3_api("/projects/#{project.id}/issues/#{issue.id}/"\ + "notes/#{issue_note.id}", user) + expect(response).to have_http_status(404) + end + + it 'returns a 404 error when note id not found' do + delete v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + end + + context 'when noteable is a Snippet' do + it 'deletes a note' do + delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/#{snippet_note.id}", user) + expect(response).to have_http_status(404) + end + + it 'returns a 404 error when note id not found' do + delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}/"\ + "notes/12345", user) + + expect(response).to have_http_status(404) + end + end + + context 'when noteable is a Merge Request' do + it 'deletes a note' do + delete v3_api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/#{merge_request_note.id}", user) + + expect(response).to have_http_status(200) + # Check if note is really deleted + delete v3_api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/#{merge_request_note.id}", user) + expect(response).to have_http_status(404) + end + + it 'returns a 404 error when note id not found' do + delete v3_api("/projects/#{project.id}/merge_requests/"\ + "#{merge_request.id}/notes/12345", user) + + expect(response).to have_http_status(404) + end + end + end +end diff --git a/spec/requests/api/v3/project_snippets_spec.rb b/spec/requests/api/v3/project_snippets_spec.rb index 3700477f0db..957a3bf97ef 100644 --- a/spec/requests/api/v3/project_snippets_spec.rb +++ b/spec/requests/api/v3/project_snippets_spec.rb @@ -85,43 +85,33 @@ describe API::ProjectSnippets, api: true do allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) end - context 'when the project is private' do - let(:private_project) { create(:project_empty_repo, :private) } - - context 'when the snippet is public' do - it 'creates the snippet' do - expect { create_snippet(private_project, visibility_level: Snippet::PUBLIC) }. - to change { Snippet.count }.by(1) - end + context 'when the snippet is private' do + it 'creates the snippet' do + expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }. + to change { Snippet.count }.by(1) end end - context 'when the project is public' do - context 'when the snippet is private' do - it 'creates the snippet' do - expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }. - to change { Snippet.count }.by(1) - end + context 'when the snippet is public' do + it 'rejects the shippet' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + not_to change { Snippet.count } + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) end - context 'when the snippet is public' do - it 'rejects the shippet' do - expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. - not_to change { Snippet.count } - expect(response).to have_http_status(400) - end - - it 'creates a spam log' do - expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. - to change { SpamLog.count }.by(1) - end + it 'creates a spam log' do + expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) end end end end describe 'PUT /projects/:project_id/snippets/:id/' do - let(:snippet) { create(:project_snippet, author: admin) } + let(:visibility_level) { Snippet::PUBLIC } + let(:snippet) { create(:project_snippet, author: admin, visibility_level: visibility_level) } it 'updates snippet' do new_content = 'New content' @@ -145,6 +135,56 @@ describe API::ProjectSnippets, api: true do expect(response).to have_http_status(400) end + + context 'when the snippet is spam' do + def update_snippet(snippet_params = {}) + put v3_api("/projects/#{snippet.project.id}/snippets/#{snippet.id}", admin), snippet_params + end + + before do + allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true) + end + + context 'when the snippet is private' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'creates the snippet' do + expect { update_snippet(title: 'Foo') }. + to change { snippet.reload.title }.to('Foo') + end + end + + context 'when the snippet is public' do + let(:visibility_level) { Snippet::PUBLIC } + + it 'rejects the snippet' do + expect { update_snippet(title: 'Foo') }. + not_to change { snippet.reload.title } + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo') }. + to change { SpamLog.count }.by(1) + end + end + + context 'when the private snippet is made public' do + let(:visibility_level) { Snippet::PRIVATE } + + it 'rejects the snippet' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + not_to change { snippet.reload.title } + + expect(response).to have_http_status(400) + expect(json_response['message']).to eq({ "error" => "Spam detected" }) + end + + it 'creates a spam log' do + expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }. + to change { SpamLog.count }.by(1) + end + end + end end describe 'DELETE /projects/:project_id/snippets/:id/' do diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index 36d99d80e79..662be3f3531 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -84,7 +84,7 @@ describe API::V3::Projects, api: true do context 'GET /projects?simple=true' do it 'returns a simplified version of all the projects' do - expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"] + expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace) get v3_api('/projects?simple=true', user) diff --git a/spec/requests/api/v3/templates_spec.rb b/spec/requests/api/v3/templates_spec.rb index 4fd4e70bedd..f1e554b98cc 100644 --- a/spec/requests/api/v3/templates_spec.rb +++ b/spec/requests/api/v3/templates_spec.rb @@ -56,11 +56,11 @@ describe API::V3::Templates, api: true do expect(json_response['popular']).to be true expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') - expect(json_response['description']).to include('A permissive license that is short and to the point.') + expect(json_response['description']).to include('A short and simple permissive license with conditions') expect(json_response['conditions']).to eq(%w[include-copyright]) expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) expect(json_response['limitations']).to eq(%w[no-liability]) - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end end @@ -70,7 +70,7 @@ describe API::V3::Templates, api: true do expect(response).to have_http_status(200) expect(json_response).to be_an Array - expect(json_response.size).to eq(15) + expect(json_response.size).to eq(12) expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') end @@ -98,7 +98,7 @@ describe API::V3::Templates, api: true do let(:license_type) { 'mit' } it 'returns the license text' do - expect(json_response['content']).to include('The MIT License (MIT)') + expect(json_response['content']).to include('MIT License') end it 'replaces placeholder values' do diff --git a/spec/requests/api/v3/todos_spec.rb b/spec/requests/api/v3/todos_spec.rb new file mode 100644 index 00000000000..80fa697e949 --- /dev/null +++ b/spec/requests/api/v3/todos_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe API::V3::Todos, api: true do + include ApiHelpers + + let(:project_1) { create(:empty_project) } + let(:project_2) { create(:empty_project) } + let(:author_1) { create(:user) } + let(:author_2) { create(:user) } + let(:john_doe) { create(:user, username: 'john_doe') } + let!(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) } + let!(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe) } + let!(:pending_3) { create(:todo, project: project_1, author: author_2, user: john_doe) } + let!(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) } + + before do + project_1.team << [john_doe, :developer] + project_2.team << [john_doe, :developer] + end + + describe 'DELETE /todos/:id' do + context 'when unauthenticated' do + it 'returns authentication error' do + delete v3_api("/todos/#{pending_1.id}") + + expect(response.status).to eq(401) + end + end + + context 'when authenticated' do + it 'marks a todo as done' do + delete v3_api("/todos/#{pending_1.id}", john_doe) + + expect(response.status).to eq(200) + expect(pending_1.reload).to be_done + end + + it 'updates todos cache' do + expect_any_instance_of(User).to receive(:update_todos_count_cache).and_call_original + + delete v3_api("/todos/#{pending_1.id}", john_doe) + end + end + end + + describe 'DELETE /todos' do + context 'when unauthenticated' do + it 'returns authentication error' do + delete v3_api('/todos') + + expect(response.status).to eq(401) + end + end + + context 'when authenticated' do + it 'marks all todos as done' do + delete v3_api('/todos', john_doe) + + expect(response.status).to eq(200) + expect(response.body).to eq('3') + expect(pending_1.reload).to be_done + expect(pending_2.reload).to be_done + expect(pending_3.reload).to be_done + end + + it 'updates todos cache' do + expect_any_instance_of(User).to receive(:update_todos_count_cache).and_call_original + + delete v3_api("/todos", john_doe) + end + end + end +end diff --git a/spec/requests/api/v3/users_spec.rb b/spec/requests/api/v3/users_spec.rb index 5020ef18a3a..17bbb0b53c1 100644 --- a/spec/requests/api/v3/users_spec.rb +++ b/spec/requests/api/v3/users_spec.rb @@ -186,4 +186,81 @@ describe API::V3::Users, api: true do expect(response).to have_http_status(404) end end + + describe 'GET /users/:id/events' do + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) } + + before do + project.add_user(user, :developer) + EventCreateService.new.leave_note(note, user) + end + + context "as a user than cannot see the event's project" do + it 'returns no events' do + other_user = create(:user) + + get api("/users/#{user.id}/events", other_user) + + expect(response).to have_http_status(200) + expect(json_response).to be_empty + end + end + + context "as a user than can see the event's project" do + context 'joined event' do + it 'returns the "joined" event' do + get v3_api("/users/#{user.id}/events", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + + comment_event = json_response.find { |e| e['action_name'] == 'commented on' } + + expect(comment_event['project_id'].to_i).to eq(project.id) + expect(comment_event['author_username']).to eq(user.username) + expect(comment_event['note']['id']).to eq(note.id) + expect(comment_event['note']['body']).to eq('What an awesome day!') + + joined_event = json_response.find { |e| e['action_name'] == 'joined' } + + expect(joined_event['project_id'].to_i).to eq(project.id) + expect(joined_event['author_username']).to eq(user.username) + expect(joined_event['author']['name']).to eq(user.name) + end + end + + context 'when there are multiple events from different projects' do + let(:second_note) { create(:note_on_issue, project: create(:empty_project)) } + let(:third_note) { create(:note_on_issue, project: project) } + + before do + second_note.project.add_user(user, :developer) + + [second_note, third_note].each do |note| + EventCreateService.new.leave_note(note, user) + end + end + + it 'returns events in the correct order (from newest to oldest)' do + get v3_api("/users/#{user.id}/events", user) + + comment_events = json_response.select { |e| e['action_name'] == 'commented on' } + + expect(comment_events[0]['target_id']).to eq(third_note.id) + expect(comment_events[1]['target_id']).to eq(second_note.id) + expect(comment_events[2]['target_id']).to eq(note.id) + end + end + end + + it 'returns a 404 error if not found' do + get v3_api('/users/42/events', user) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 User Not Found') + end + end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index f2385da8c13..c7284be09b7 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Ci::API::Builds do include ApiHelpers - let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) } + let(:runner) { FactoryGirl.create(:ci_runner, tag_list: %w(mysql ruby)) } let(:project) { FactoryGirl.create(:empty_project, shared_runners_enabled: false) } let(:last_update) { nil } diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index bd55934d0c8..8719313783e 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -41,7 +41,7 @@ describe Ci::API::Runners do it 'creates runner' do expect(response).to have_http_status 201 - expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) + expect(Ci::Runner.first.tag_list.sort).to eq(%w(tag1 tag2)) end end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index a30be767119..5321f8b134f 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -60,7 +60,8 @@ describe Ci::API::Triggers do it 'validates variables to be a hash' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: 'value') expect(response).to have_http_status(400) - expect(json_response['message']).to eq('variables needs to be a hash') + + expect(json_response['error']).to eq('variables is invalid') end it 'validates variables needs to be a map of key-valued strings' do diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index c0e7bab8199..5d495bc9e7d 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -25,11 +25,9 @@ describe 'Git LFS API and storage' do { 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ], 'operation' => 'upload' } @@ -53,11 +51,9 @@ describe 'Git LFS API and storage' do { 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ], 'operation' => 'upload' } @@ -374,11 +370,12 @@ describe 'Git LFS API and storage' do describe 'download' do let(:project) { create(:empty_project) } let(:body) do - { 'operation' => 'download', + { + 'operation' => 'download', 'objects' => [ { 'oid' => sample_oid, - 'size' => sample_size - }] + 'size' => sample_size } + ] } end @@ -393,16 +390,20 @@ describe 'Git LFS API and storage' do end it 'with href to download' do - expect(json_response).to eq('objects' => [ - { 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => { 'Authorization' => authorization } + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => authorization } + } } } - }]) + ] + }) end end @@ -417,24 +418,29 @@ describe 'Git LFS API and storage' do end it 'with href to download' do - expect(json_response).to eq('objects' => [ - { 'oid' => sample_oid, - 'size' => sample_size, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => sample_oid, + 'size' => sample_size, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } } - }]) + ] + }) end end context 'when downloading a lfs object that does not exist' do let(:body) do - { 'operation' => 'download', + { + 'operation' => 'download', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }] + 'size' => 1575078 } + ] } end @@ -443,27 +449,30 @@ describe 'Git LFS API and storage' do end it 'with an 404 for specific object' do - expect(json_response).to eq('objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } } - }]) + ] + }) end end context 'when downloading one new and one existing lfs object' do let(:body) do - { 'operation' => 'download', + { + 'operation' => 'download', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ] } end @@ -477,23 +486,28 @@ describe 'Git LFS API and storage' do end it 'responds with upload hypermedia link for the new object' do - expect(json_response).to eq('objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }, - { 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => { 'Authorization' => authorization } + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }, + { + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => authorization } + } } } - }]) + ] + }) end end end @@ -597,17 +611,21 @@ describe 'Git LFS API and storage' do end it 'responds with status 200 and href to download' do - expect(json_response).to eq('objects' => [ - { 'oid' => sample_oid, - 'size' => sample_size, - 'authenticated' => true, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {} + expect(json_response).to eq({ + 'objects' => [ + { + 'oid' => sample_oid, + 'size' => sample_size, + 'authenticated' => true, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {} + } } } - }]) + ] + }) end end @@ -626,11 +644,12 @@ describe 'Git LFS API and storage' do describe 'upload' do let(:project) { create(:project, :public) } let(:body) do - { 'operation' => 'upload', + { + 'operation' => 'upload', 'objects' => [ { 'oid' => sample_oid, - 'size' => sample_size - }] + 'size' => sample_size } + ] } end @@ -665,11 +684,12 @@ describe 'Git LFS API and storage' do context 'when pushing a lfs object that does not exist' do let(:body) do - { 'operation' => 'upload', + { + 'operation' => 'upload', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }] + 'size' => 1575078 } + ] } end @@ -688,14 +708,13 @@ describe 'Git LFS API and storage' do context 'when pushing one new and one existing lfs object' do let(:body) do - { 'operation' => 'upload', + { + 'operation' => 'upload', 'objects' => [ { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, + 'size' => 1575078 }, { 'oid' => sample_oid, - 'size' => sample_size - } + 'size' => sample_size } ] } end @@ -789,11 +808,12 @@ describe 'Git LFS API and storage' do let(:project) { create(:empty_project) } let(:authorization) { authorize_user } let(:body) do - { 'operation' => 'other', + { + 'operation' => 'other', 'objects' => [ { 'oid' => sample_oid, - 'size' => sample_size - }] + 'size' => sample_size } + ] } end diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index bb26513103d..b91234ddb1e 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -72,7 +72,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do shared_examples 'a pullable and pushable' do it_behaves_like 'a accessible' do - let(:actions) { ['pull', 'push'] } + let(:actions) { %w(pull push) } end end diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index ceaca96e25b..8459a3d8cfb 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -79,66 +79,53 @@ describe Ci::CreatePipelineService, services: true do context 'when commit contains a [ci skip] directive' do let(:message) { "some message[ci skip]" } - let(:messageFlip) { "some message[skip ci]" } - let(:capMessage) { "some message[CI SKIP]" } - let(:capMessageFlip) { "some message[SKIP CI]" } + + ci_messages = [ + "some message[ci skip]", + "some message[skip ci]", + "some message[CI SKIP]", + "some message[SKIP CI]", + "some message[ci_skip]", + "some message[skip_ci]", + "some message[ci-skip]", + "some message[skip-ci]" + ] before do allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message } end - it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{ message: message }] - pipeline = execute(ref: 'refs/heads/master', - before: '00000000', - after: project.commit.id, - commits: commits) + ci_messages.each do |ci_message| + it "skips builds creation if the commit message is #{ci_message}" do + commits = [{ message: ci_message }] + pipeline = execute(ref: 'refs/heads/master', + before: '00000000', + after: project.commit.id, + commits: commits) - expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") - end - - it "skips builds creation if there is [skip ci] tag in commit message" do - commits = [{ message: messageFlip }] - pipeline = execute(ref: 'refs/heads/master', - before: '00000000', - after: project.commit.id, - commits: commits) - - expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end end - it "skips builds creation if there is [CI SKIP] tag in commit message" do - commits = [{ message: capMessage }] - pipeline = execute(ref: 'refs/heads/master', - before: '00000000', - after: project.commit.id, - commits: commits) - - expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") - end + it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do + allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" } - it "skips builds creation if there is [SKIP CI] tag in commit message" do - commits = [{ message: capMessageFlip }] + commits = [{ message: "some message" }] pipeline = execute(ref: 'refs/heads/master', before: '00000000', after: project.commit.id, commits: commits) expect(pipeline).to be_persisted - expect(pipeline.builds.any?).to be false - expect(pipeline.status).to eq("skipped") + expect(pipeline.builds.first.name).to eq("rspec") end - it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do - allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" } + it "does not skip builds creation if the commit message is nil" do + allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { nil } - commits = [{ message: "some message" }] + commits = [{ message: nil }] pipeline = execute(ref: 'refs/heads/master', before: '00000000', after: project.commit.id, diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb index ef2ddc4b1d7..b818dfdd50c 100644 --- a/spec/services/ci/process_pipeline_service_spec.rb +++ b/spec/services/ci/process_pipeline_service_spec.rb @@ -377,9 +377,7 @@ describe Ci::ProcessPipelineService, :services do builds.pending.update_all(status: 'success') end - def manual_actions - pipeline.manual_actions - end + delegate :manual_actions, to: :pipeline def create_build(name, stage_idx, when_value = nil) create(:ci_build, diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index 93147870afe..d03f7505eac 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -12,7 +12,7 @@ describe Ci::RetryBuildService, :services do shared_examples 'build duplication' do let(:build) do - create(:ci_build, :failed, :artifacts, :erased, :trace, + create(:ci_build, :failed, :artifacts_expired, :erased, :trace, :queued, :coverage, pipeline: pipeline) end @@ -38,7 +38,7 @@ describe Ci::RetryBuildService, :services do described_class::IGNORE_ATTRIBUTES + described_class::REJECT_ATTRIBUTES - expect(attributes.size).to eq build.attributes.size + expect(build.attributes.size).to eq(attributes.size) end end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index c0af8b8450a..8b1ed6470e4 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -69,6 +69,25 @@ describe Ci::RetryPipelineService, '#execute', :services do end end + context 'when the last stage was skipepd' do + before do + create_build('build 1', :success, 0) + create_build('test 2', :failed, 1) + create_build('report 3', :skipped, 2) + create_build('report 4', :skipped, 2) + end + + it 'retries builds only in the first stage' do + service.execute(pipeline) + + expect(build('build 1')).to be_success + expect(build('test 2')).to be_pending + expect(build('report 3')).to be_created + expect(build('report 4')).to be_created + expect(pipeline.reload).to be_running + end + end + context 'when pipeline contains manual actions' do context 'when there is a canceled manual action in first stage' do before do @@ -90,14 +109,16 @@ describe Ci::RetryPipelineService, '#execute', :services do context 'when there is a skipped manual action in last stage' do before do create_build('rspec 1', :canceled, 0) + create_build('rspec 2', :skipped, 0, :manual) create_build('staging', :skipped, 1, :manual) end - it 'retries canceled job and skips manual action' do + it 'retries canceled job and reprocesses manual actions' do service.execute(pipeline) expect(build('rspec 1')).to be_pending - expect(build('staging')).to be_skipped + expect(build('rspec 2')).to be_skipped + expect(build('staging')).to be_created expect(pipeline.reload).to be_running end end diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb index 6fb4d517115..18b964e2453 100644 --- a/spec/services/create_deployment_service_spec.rb +++ b/spec/services/create_deployment_service_spec.rb @@ -9,7 +9,8 @@ describe CreateDeploymentService, services: true do describe '#execute' do let(:options) { nil } let(:params) do - { environment: 'production', + { + environment: 'production', ref: 'master', tag: false, sha: '97de212e80737a608d939f648d959671fb0a0142', @@ -83,10 +84,11 @@ describe CreateDeploymentService, services: true do context 'for environment with invalid name' do let(:params) do - { environment: 'name,with,commas', + { + environment: 'name,with,commas', ref: 'master', tag: false, - sha: '97de212e80737a608d939f648d959671fb0a0142', + sha: '97de212e80737a608d939f648d959671fb0a0142' } end @@ -101,7 +103,8 @@ describe CreateDeploymentService, services: true do context 'when variables are used' do let(:params) do - { environment: 'review-apps/$CI_BUILD_REF_NAME', + { + environment: 'review-apps/$CI_BUILD_REF_NAME', ref: 'master', tag: false, sha: '97de212e80737a608d939f648d959671fb0a0142', diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index e1feeed8a67..6045d00ff09 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -230,16 +230,6 @@ describe Issues::CreateService, services: true do expect { issue }.not_to change{SpamLog.last.recaptcha_verified} end end - - context 'when spam log title does not match the issue title' do - before do - opts[:title] = 'Another issue' - end - - it 'does not mark spam_log as recaptcha_verified' do - expect { issue }.not_to change{SpamLog.last.recaptcha_verified} - end - end end context 'when recaptcha was not verified' do diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index dc945ca4868..0768f644036 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -44,15 +44,14 @@ describe MergeRequests::BuildService, services: true do end end - context 'missing target branch' do - let(:target_branch) { '' } + context 'when target branch is missing' do + let(:target_branch) { nil } + let(:commits) { Commit.decorate([commit_1], project) } - it 'forbids the merge request from being created' do + it 'creates compare object with target branch as default branch' do expect(merge_request.can_be_created).to eq(false) - end - - it 'adds an error message to the merge request' do - expect(merge_request.errors).to contain_exactly('You must select source and target branch') + expect(merge_request.compare).to be_present + expect(merge_request.target_branch).to eq(project.default_branch) end end diff --git a/spec/services/merge_requests/resolve_service_spec.rb b/spec/services/merge_requests/resolve_service_spec.rb index a0e51681725..c3b468ac47f 100644 --- a/spec/services/merge_requests/resolve_service_spec.rb +++ b/spec/services/merge_requests/resolve_service_spec.rb @@ -59,8 +59,8 @@ describe MergeRequests::ResolveService do it 'creates a commit with the correct parents' do expect(merge_request.source_branch_head.parents.map(&:id)). - to eq(['1450cd639e0bc6721eb02800169e464f212cde06', - '824be604a34828eb682305f0d963056cfac87b2d']) + to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 + 824be604a34828eb682305f0d963056cfac87b2d)) end end @@ -125,8 +125,8 @@ describe MergeRequests::ResolveService do it 'creates a commit with the correct parents' do expect(merge_request.source_branch_head.parents.map(&:id)). - to eq(['1450cd639e0bc6721eb02800169e464f212cde06', - '824be604a34828eb682305f0d963056cfac87b2d']) + to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 + 824be604a34828eb682305f0d963056cfac87b2d)) end it 'sets the content to the content given' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index af515ad2e0e..62f21049b0b 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -50,7 +50,7 @@ describe Projects::CreateService, '#execute', services: true do context 'error handling' do it 'handles invalid options' do - opts.merge!({ default_branch: 'master' } ) + opts[:default_branch] = 'master' expect(create_project(user, opts)).to eq(nil) end end @@ -67,7 +67,7 @@ describe Projects::CreateService, '#execute', services: true do context 'wiki_enabled false does not create wiki repository directory' do it do - opts.merge!(wiki_enabled: false) + opts[:wiki_enabled] = false project = create_project(user, opts) path = ProjectWiki.new(project, user).send(:path_to_repo) diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb index 7d4eff3b6ef..6ea8f309981 100644 --- a/spec/services/protected_branches/create_service_spec.rb +++ b/spec/services/protected_branches/create_service_spec.rb @@ -6,8 +6,8 @@ describe ProtectedBranches::CreateService, services: true do let(:params) do { name: 'master', - merge_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ], - push_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ] + merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }], + push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }] } end diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb index 271c17dd8c0..4ce3b95aa87 100644 --- a/spec/services/spam_service_spec.rb +++ b/spec/services/spam_service_spec.rb @@ -1,46 +1,61 @@ require 'spec_helper' describe SpamService, services: true do - describe '#check' do - let(:project) { create(:project, :public) } - let(:issue) { create(:issue, project: project) } - let(:request) { double(:request, env: {}) } + describe '#when_recaptcha_verified' do + def check_spam(issue, request, recaptcha_verified) + described_class.new(issue, request).when_recaptcha_verified(recaptcha_verified) do + 'yielded' + end + end + + it 'yields block when recaptcha was already verified' do + issue = build_stubbed(:issue) - def check_spam(issue, request) - described_class.new(issue, request).check + expect(check_spam(issue, nil, true)).to eql('yielded') end - context 'when indicated as spam by akismet' do - before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) } + context 'when recaptcha was not verified' do + let(:project) { create(:project, :public) } + let(:issue) { create(:issue, project: project) } + let(:request) { double(:request, env: {}) } - it 'returns false when request is missing' do - expect(check_spam(issue, nil)).to be_falsey - end + context 'when indicated as spam by akismet' do + before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) } - it 'returns false when issue is not public' do - issue = create(:issue, project: create(:project, :private)) + it 'doesnt check as spam when request is missing' do + check_spam(issue, nil, false) - expect(check_spam(issue, request)).to be_falsey - end + expect(issue.spam).to be_falsey + end - it 'returns true' do - expect(check_spam(issue, request)).to be_truthy - end + it 'checks as spam' do + check_spam(issue, request, false) - it 'creates a spam log' do - expect { check_spam(issue, request) }.to change { SpamLog.count }.from(0).to(1) - end - end + expect(issue.spam).to be_truthy + end - context 'when not indicated as spam by akismet' do - before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) } + it 'creates a spam log' do + expect { check_spam(issue, request, false) } + .to change { SpamLog.count }.from(0).to(1) + end - it 'returns false' do - expect(check_spam(issue, request)).to be_falsey + it 'doesnt yield block' do + expect(check_spam(issue, request, false)) + .to eql(SpamLog.last) + end end - it 'does not create a spam log' do - expect { check_spam(issue, request) }.not_to change { SpamLog.count } + context 'when not indicated as spam by akismet' do + before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) } + + it 'returns false' do + expect(check_spam(issue, request, false)).to be_falsey + end + + it 'does not create a spam log' do + expect { check_spam(issue, request, false) } + .not_to change { SpamLog.count } + end end end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 7f027ae02a2..eca5a418f2a 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -631,7 +631,7 @@ describe SystemNoteService, services: true do jira_service_settings end - noteable_types = ["merge_requests", "commit"] + noteable_types = %w(merge_requests commit) noteable_types.each do |type| context "when noteable is a #{type}" do diff --git a/spec/services/users/destroy_spec.rb b/spec/services/users/destroy_spec.rb index 46e58393218..c0bf27c698c 100644 --- a/spec/services/users/destroy_spec.rb +++ b/spec/services/users/destroy_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' describe Users::DestroyService, services: true do describe "Deletes a user and all their personal projects" do - let!(:user) { create(:user) } - let!(:current_user) { create(:user) } - let!(:namespace) { create(:namespace, owner: user) } - let!(:project) { create(:project, namespace: namespace) } - let(:service) { described_class.new(current_user) } + let!(:user) { create(:user) } + let!(:admin) { create(:admin) } + let!(:namespace) { create(:namespace, owner: user) } + let!(:project) { create(:project, namespace: namespace) } + let(:service) { described_class.new(admin) } context 'no options are given' do it 'deletes the user' do @@ -57,5 +57,26 @@ describe Users::DestroyService, services: true do expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) end end + + context "deletion permission checks" do + it 'does not delete the user when user is not an admin' do + other_user = create(:user) + + expect { described_class.new(other_user).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError) + expect(User.exists?(user.id)).to be(true) + end + + it 'allows admins to delete anyone' do + described_class.new(admin).execute(user) + + expect(User.exists?(user.id)).to be(false) + end + + it 'allows users to delete their own account' do + described_class.new(user).execute(user) + + expect(User.exists?(user.id)).to be(false) + end + end end end diff --git a/spec/support/filtered_search_helpers.rb b/spec/support/filtered_search_helpers.rb new file mode 100644 index 00000000000..58f6636e680 --- /dev/null +++ b/spec/support/filtered_search_helpers.rb @@ -0,0 +1,37 @@ +module FilteredSearchHelpers + def filtered_search + page.find('.filtered-search') + end + + def input_filtered_search(search_term, submit: true) + filtered_search.set(search_term) + + if submit + filtered_search.send_keys(:enter) + end + end + + def input_filtered_search_keys(search_term) + filtered_search.send_keys(search_term) + filtered_search.send_keys(:enter) + end + + def expect_filtered_search_input(input) + expect(find('.filtered-search').value).to eq(input) + end + + def clear_search_field + find('.filtered-search-input-container .clear-search').click + end + + def reset_filters + clear_search_field + filtered_search.send_keys(:enter) + end + + def init_label_search + filtered_search.set('label:') + # This ensures the dropdown is shown + expect(find('#js-dropdown-label')).not_to have_css('.filter-dropdown-loading') + end +end diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb index dac94dfc31e..4644c7a6b86 100644 --- a/spec/support/issuables_list_metadata_shared_examples.rb +++ b/spec/support/issuables_list_metadata_shared_examples.rb @@ -3,11 +3,12 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| @issuable_ids = [] 2.times do - if issuable_type == :issue - issuable = create(issuable_type, project: project) - else - issuable = create(issuable_type, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) - end + issuable = + if issuable_type == :issue + create(issuable_type, project: project) + else + create(issuable_type, title: FFaker::Lorem.sentence, source_project: project, source_branch: FFaker::Name.name) + end @issuable_ids << issuable.id diff --git a/spec/support/javascript_fixtures_helpers.rb b/spec/support/javascript_fixtures_helpers.rb index 0b8729db0f9..a982b159b48 100644 --- a/spec/support/javascript_fixtures_helpers.rb +++ b/spec/support/javascript_fixtures_helpers.rb @@ -5,7 +5,7 @@ require 'gitlab/popen' module JavaScriptFixturesHelpers include Gitlab::Popen - FIXTURE_PATH = 'spec/javascripts/fixtures' + FIXTURE_PATH = 'spec/javascripts/fixtures'.freeze # Public: Removes all fixture files from given directory # diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb index 929fc0c5182..97ae0b6afc5 100644 --- a/spec/support/jira_service_helper.rb +++ b/spec/support/jira_service_helper.rb @@ -1,5 +1,5 @@ module JiraServiceHelper - JIRA_URL = "http://jira.example.net" + JIRA_URL = "http://jira.example.net".freeze JIRA_API = JIRA_URL + "/rest/api/2" def jira_service_settings diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb index 444612cf871..b5ed71ba3be 100644 --- a/spec/support/kubernetes_helpers.rb +++ b/spec/support/kubernetes_helpers.rb @@ -2,23 +2,24 @@ module KubernetesHelpers include Gitlab::Kubernetes def kube_discovery_body - { "kind" => "APIResourceList", + { + "kind" => "APIResourceList", "resources" => [ { "name" => "pods", "namespaced" => true, "kind" => "Pod" }, - ], + ] } end def kube_pods_body(*pods) { "kind" => "PodList", - "items" => [ kube_pod ], - } + "items" => [kube_pod] } end # This is a partial response, it will have many more elements in reality but # these are the ones we care about at the moment def kube_pod(app: "valid-pod-label") - { "metadata" => { + { + "metadata" => { "name" => "kube-pod", "creationTimestamp" => "2016-11-25T19:55:19Z", "labels" => { "app" => app }, @@ -29,7 +30,7 @@ module KubernetesHelpers { "name" => "container-1" }, ], }, - "status" => { "phase" => "Running" }, + "status" => { "phase" => "Running" } } end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index ad1eed5b369..9ffb00be0b8 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -15,11 +15,12 @@ module LoginHelpers # user = create(:user) # login_as(user) def login_as(user_or_role) - if user_or_role.kind_of?(User) - @user = user_or_role - else - @user = create(user_or_role) - end + @user = + if user_or_role.is_a?(User) + user_or_role + else + create(user_or_role) + end login_with(@user) end diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb index ceddb656596..7d238850520 100644 --- a/spec/support/matchers/access_matchers.rb +++ b/spec/support/matchers/access_matchers.rb @@ -38,7 +38,7 @@ module AccessMatchers end def description_for(user, type) - if user.kind_of?(User) + if user.is_a?(User) # User#inspect displays too much information for RSpec's descriptions "be #{type} for the specified user" else diff --git a/spec/support/merge_request_helpers.rb b/spec/support/merge_request_helpers.rb index d5801c8272f..326b85eabd0 100644 --- a/spec/support/merge_request_helpers.rb +++ b/spec/support/merge_request_helpers.rb @@ -10,4 +10,13 @@ module MergeRequestHelpers def last_merge_request page.all('ul.mr-list > li').last.text end + + def expect_mr_list_count(open_count, closed_count = 0) + all_count = open_count + closed_count + + expect(page).to have_issuable_counts(open: open_count, closed: closed_count, all: all_count) + page.within '.mr-list' do + expect(page).to have_selector('.merge-request', count: open_count) + end + end end diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb index 73f375c481b..e9d5c7b12ae 100644 --- a/spec/support/repo_helpers.rb +++ b/spec/support/repo_helpers.rb @@ -42,7 +42,7 @@ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> eos ) end - + def another_sample_commit OpenStruct.new( id: "e56497bb5f03a90a51293fc6d516788730953899", @@ -100,13 +100,13 @@ eos } ] - commits = [ - '5937ac0a7beb003549fc5fd26fc247adbce4a52e', - '570e7b2abdd848b95f2f578043fc23bd6f6fd24d', - '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9', - 'd14d6c0abdd253381df51a723d58691b2ee1ab08', - 'c1acaa58bbcbc3eafe538cb8274ba387047b69f8', - ].reverse # last commit is recent one + commits = %w( + 5937ac0a7beb003549fc5fd26fc247adbce4a52e + 570e7b2abdd848b95f2f578043fc23bd6f6fd24d + 6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9 + d14d6c0abdd253381df51a723d58691b2ee1ab08 + c1acaa58bbcbc3eafe538cb8274ba387047b69f8 + ).reverse # last commit is recent one OpenStruct.new( source_branch: 'master', diff --git a/spec/support/seed_helper.rb b/spec/support/seed_helper.rb index 03fa0a66b9a..07f81e9c4f3 100644 --- a/spec/support/seed_helper.rb +++ b/spec/support/seed_helper.rb @@ -7,7 +7,7 @@ TEST_MUTABLE_REPO_PATH = File.join(SEED_REPOSITORY_PATH, "mutable-repo.git") TEST_BROKEN_REPO_PATH = File.join(SEED_REPOSITORY_PATH, "broken-repo.git") module SeedHelper - GITLAB_URL = "https://gitlab.com/gitlab-org/gitlab-git-test.git" + GITLAB_URL = "https://gitlab.com/gitlab-org/gitlab-git-test.git".freeze def ensure_seeds if File.exist?(SEED_REPOSITORY_PATH) diff --git a/spec/support/seed_repo.rb b/spec/support/seed_repo.rb index 9f2cd7c67c5..99a500bbbb1 100644 --- a/spec/support/seed_repo.rb +++ b/spec/support/seed_repo.rb @@ -25,64 +25,64 @@ module SeedRepo module BigCommit - ID = "913c66a37b4a45b9769037c55c2d238bd0942d2e" - PARENT_ID = "cfe32cf61b73a0d5e9f13e774abde7ff789b1660" - MESSAGE = "Files, encoding and much more" - AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" + ID = "913c66a37b4a45b9769037c55c2d238bd0942d2e".freeze + PARENT_ID = "cfe32cf61b73a0d5e9f13e774abde7ff789b1660".freeze + MESSAGE = "Files, encoding and much more".freeze + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze FILES_COUNT = 2 end module Commit - ID = "570e7b2abdd848b95f2f578043fc23bd6f6fd24d" - PARENT_ID = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9" - MESSAGE = "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n" - AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" - FILES = ["files/ruby/popen.rb", "files/ruby/regex.rb"] + ID = "570e7b2abdd848b95f2f578043fc23bd6f6fd24d".freeze + PARENT_ID = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9".freeze + MESSAGE = "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n".freeze + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze + FILES = ["files/ruby/popen.rb", "files/ruby/regex.rb"].freeze FILES_COUNT = 2 - C_FILE_PATH = "files/ruby" - C_FILES = ["popen.rb", "regex.rb", "version_info.rb"] - BLOB_FILE = %{%h3= @key.title\n%hr\n%pre= @key.key\n.actions\n = link_to 'Remove', @key, :confirm => 'Are you sure?', :method => :delete, :class => \"btn danger delete-key\"\n\n\n} - BLOB_FILE_PATH = "app/views/keys/show.html.haml" + C_FILE_PATH = "files/ruby".freeze + C_FILES = ["popen.rb", "regex.rb", "version_info.rb"].freeze + BLOB_FILE = %{%h3= @key.title\n%hr\n%pre= @key.key\n.actions\n = link_to 'Remove', @key, :confirm => 'Are you sure?', :method => :delete, :class => \"btn danger delete-key\"\n\n\n}.freeze + BLOB_FILE_PATH = "app/views/keys/show.html.haml".freeze end module EmptyCommit - ID = "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9" - PARENT_ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d" - MESSAGE = "Empty commit" - AUTHOR_FULL_NAME = "Rémy Coutable" - FILES = [] + ID = "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9".freeze + PARENT_ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d".freeze + MESSAGE = "Empty commit".freeze + AUTHOR_FULL_NAME = "Rémy Coutable".freeze + FILES = [].freeze FILES_COUNT = FILES.count end module EncodingCommit - ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d" - PARENT_ID = "66028349a123e695b589e09a36634d976edcc5e8" - MESSAGE = "Add ISO-8859-encoded file" - AUTHOR_FULL_NAME = "Stan Hu" - FILES = ["encoding/iso8859.txt"] + ID = "40f4a7a617393735a95a0bb67b08385bc1e7c66d".freeze + PARENT_ID = "66028349a123e695b589e09a36634d976edcc5e8".freeze + MESSAGE = "Add ISO-8859-encoded file".freeze + AUTHOR_FULL_NAME = "Stan Hu".freeze + FILES = ["encoding/iso8859.txt"].freeze FILES_COUNT = FILES.count end module FirstCommit - ID = "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863" + ID = "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863".freeze PARENT_ID = nil - MESSAGE = "Initial commit" - AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" - FILES = ["LICENSE", ".gitignore", "README.md"] + MESSAGE = "Initial commit".freeze + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets".freeze + FILES = ["LICENSE", ".gitignore", "README.md"].freeze FILES_COUNT = 3 end module LastCommit - ID = "4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6" - PARENT_ID = "0e1b353b348f8477bdbec1ef47087171c5032cd9" - MESSAGE = "Merge branch 'master' into 'master'" - AUTHOR_FULL_NAME = "Stan Hu" - FILES = ["bin/executable"] + ID = "4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6".freeze + PARENT_ID = "0e1b353b348f8477bdbec1ef47087171c5032cd9".freeze + MESSAGE = "Merge branch 'master' into 'master'".freeze + AUTHOR_FULL_NAME = "Stan Hu".freeze + FILES = ["bin/executable"].freeze FILES_COUNT = FILES.count end module Repo - HEAD = "master" + HEAD = "master".freeze BRANCHES = %w[ feature fix @@ -93,14 +93,14 @@ module SeedRepo gitattributes-updated master merge-test - ] - TAGS = %w[v1.0.0 v1.1.0 v1.2.0 v1.2.1] + ].freeze + TAGS = %w[v1.0.0 v1.1.0 v1.2.0 v1.2.1].freeze end module RubyBlob - ID = "7e3e39ebb9b2bf433b4ad17313770fbe4051649c" - NAME = "popen.rb" - CONTENT = <<-eos + ID = "7e3e39ebb9b2bf433b4ad17313770fbe4051649c".freeze + NAME = "popen.rb".freeze + CONTENT = <<-eos.freeze require 'fileutils' require 'open3' diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb index d30cc8ff9f2..0d526045012 100644 --- a/spec/support/select2_helper.rb +++ b/spec/support/select2_helper.rb @@ -12,7 +12,7 @@ module Select2Helper def select2(value, options = {}) - raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash) + raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash) selector = options.fetch(:from) diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb index c64574679b6..81d06dc2a3d 100644 --- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb +++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb @@ -11,7 +11,7 @@ shared_examples 'new issuable record that supports slash commands' do let(:params) { base_params.merge(defined?(default_params) ? default_params : {}).merge(example_params) } let(:issuable) { described_class.new(project, user, params).execute } - before { project.team << [assignee, :master ] } + before { project.team << [assignee, :master] } context 'with labels in command only' do let(:example_params) do diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index 93f96cacc00..a01ef576234 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -35,7 +35,7 @@ module StubGitlabCalls { "tags" => tags } ) allow_any_instance_of(ContainerRegistry::Client).to receive(:repository_manifest).and_return( - JSON.load(File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json')) + JSON.parse(File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json')) ) allow_any_instance_of(ContainerRegistry::Client).to receive(:blob).and_return( File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json') diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index b87232a350b..4e63a4cd537 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -38,7 +38,7 @@ module TestEnv 'deleted-image-test' => '6c17798', 'wip' => 'b9238ee', 'csv' => '3dd0896' - } + }.freeze # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily # need to keep all the branches in sync. @@ -48,7 +48,7 @@ module TestEnv 'master' => '5937ac0', 'remove-submodule' => '2a33e0c', 'conflict-resolvable-fork' => '404fa3f' - } + }.freeze # Test environment # diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 5ef8cf1105b..a60af574a08 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -102,8 +102,8 @@ describe GitGarbageCollectWorker do new_commit_sha = Rugged::Commit.create( rugged, message: "hello world #{SecureRandom.hex(6)}", - author: Gitlab::Git::committer_hash(email: 'foo@bar', name: 'baz'), - committer: Gitlab::Git::committer_hash(email: 'foo@bar', name: 'baz'), + author: Gitlab::Git.committer_hash(email: 'foo@bar', name: 'baz'), + committer: Gitlab::Git.committer_hash(email: 'foo@bar', name: 'baz'), tree: old_commit.tree, parents: [old_commit], ) diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb index f1b1574abf4..c42f3147b7a 100644 --- a/spec/workers/repository_import_worker_spec.rb +++ b/spec/workers/repository_import_worker_spec.rb @@ -20,7 +20,7 @@ describe RepositoryImportWorker do context 'when the import has failed' do it 'hide the credentials that were used in the import URL' do - error = %Q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found } + error = %q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found } expect_any_instance_of(Projects::ImportService).to receive(:execute). and_return({ status: :error, message: error }) |