summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorValery Sizov <valery@gitlab.com>2017-03-03 20:24:16 +0200
committerValery Sizov <valery@gitlab.com>2017-03-03 20:24:16 +0200
commit5bf2ab73ba1a812b90ec50be676378eb0ae58fa8 (patch)
tree932cef8cd4d5063df10828cf4a02c0c31c631bb2 /spec
parent32538def144f88a68c5cdfbe7cb7cb2866bce932 (diff)
parentdf63d9db40e568bcca87cf7946cf518684538a31 (diff)
downloadgitlab-ce-5bf2ab73ba1a812b90ec50be676378eb0ae58fa8.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into orderable-issues
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/ci/projects_controller_spec.rb74
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb12
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb20
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb37
-rw-r--r--spec/controllers/root_controller_spec.rb36
-rw-r--r--spec/factories/ci/builds.rb12
-rw-r--r--spec/factories/merge_requests.rb4
-rw-r--r--spec/factories/projects.rb4
-rw-r--r--spec/factories/todos.rb14
-rw-r--r--spec/features/dashboard/activity_spec.rb11
-rw-r--r--spec/features/dashboard/groups_list_spec.rb47
-rw-r--r--spec/features/dashboard/issues_spec.rb3
-rw-r--r--spec/features/dashboard/projects_spec.rb10
-rw-r--r--spec/features/explore/groups_list_spec.rb46
-rw-r--r--spec/features/groups/activity_spec.rb26
-rw-r--r--spec/features/groups/issues_spec.rb18
-rw-r--r--spec/features/groups/show_spec.rb24
-rw-r--r--spec/features/issues/award_emoji_spec.rb25
-rw-r--r--spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb2
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb4
-rw-r--r--spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb8
-rw-r--r--spec/features/merge_requests/widget_spec.rb67
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/projects/activity/rss_spec.rb29
-rw-r--r--spec/features/projects/commit/cherry_pick_spec.rb1
-rw-r--r--spec/features/projects/commit/rss_spec.rb27
-rw-r--r--spec/features/projects/guest_navigation_menu_spec.rb2
-rw-r--r--spec/features/projects/issues/rss_spec.rb31
-rw-r--r--spec/features/projects/main/rss_spec.rb25
-rw-r--r--spec/features/projects/milestones/milestone_spec.rb64
-rw-r--r--spec/features/projects/tree/rss_spec.rb25
-rw-r--r--spec/features/users/rss_spec.rb22
-rw-r--r--spec/helpers/milestones_helper_spec.rb2
-rw-r--r--spec/helpers/rss_helper_spec.rb20
-rw-r--r--spec/javascripts/boards/board_card_spec.js2
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js191
-rw-r--r--spec/javascripts/environments/environment_actions_spec.js.es630
-rw-r--r--spec/javascripts/fixtures/projects.json18
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js.es660
-rw-r--r--spec/javascripts/new_branch_spec.js1
-rw-r--r--spec/javascripts/user_callout_spec.js.es636
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js.es610
-rw-r--r--spec/lib/banzai/filter/user_reference_filter_spec.rb6
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb44
-rw-r--r--spec/lib/gitlab/gitaly_client/notifications_spec.rb20
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/project.json18
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml2
-rw-r--r--spec/models/ci/build_spec.rb14
-rw-r--r--spec/models/merge_request_spec.rb22
-rw-r--r--spec/models/repository_spec.rb16
-rw-r--r--spec/requests/api/commit_statuses_spec.rb72
-rw-r--r--spec/requests/api/environments_spec.rb36
-rw-r--r--spec/requests/api/files_spec.rb14
-rw-r--r--spec/requests/api/groups_spec.rb6
-rw-r--r--spec/requests/api/internal_spec.rb28
-rw-r--r--spec/requests/api/issues_spec.rb57
-rw-r--r--spec/requests/api/merge_requests_spec.rb6
-rw-r--r--spec/requests/api/milestones_spec.rb50
-rw-r--r--spec/requests/api/notes_spec.rb4
-rw-r--r--spec/requests/api/project_snippets_spec.rb16
-rw-r--r--spec/requests/api/projects_spec.rb154
-rw-r--r--spec/requests/api/settings_spec.rb7
-rw-r--r--spec/requests/api/snippets_spec.rb12
-rw-r--r--spec/requests/api/todos_spec.rb4
-rw-r--r--spec/requests/api/v3/environments_spec.rb126
-rw-r--r--spec/requests/api/v3/files_spec.rb14
-rw-r--r--spec/requests/api/v3/groups_spec.rb530
-rw-r--r--spec/requests/api/v3/milestones_spec.rb232
-rw-r--r--spec/requests/api/v3/notes_spec.rb4
-rw-r--r--spec/requests/api/v3/projects_spec.rb6
-rw-r--r--spec/requests/api/v3/settings_spec.rb65
-rw-r--r--spec/requests/api/v3/snippets_spec.rb187
-rw-r--r--spec/rubocop/cop/custom_error_class_spec.rb111
-rw-r--r--spec/services/ci/image_for_build_service_spec.rb50
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb2
-rw-r--r--spec/services/ci/register_build_service_spec.rb45
-rw-r--r--spec/services/ci/retry_build_service_spec.rb47
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb14
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb4
-rw-r--r--spec/services/notes/create_service_spec.rb50
-rw-r--r--spec/services/notification_service_spec.rb12
-rw-r--r--spec/services/slash_commands/interpret_service_spec.rb39
-rw-r--r--spec/services/system_note_service_spec.rb47
-rw-r--r--spec/services/todo_service_spec.rb4
-rw-r--r--spec/support/features/rss_shared_examples.rb23
-rw-r--r--spec/support/matchers/gitaly_matchers.rb3
-rw-r--r--spec/support/project_features_apply_to_issuables_shared_examples.rb2
-rw-r--r--spec/views/ci/status/_badge.html.haml_spec.rb89
89 files changed, 2892 insertions, 525 deletions
diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb
deleted file mode 100644
index 86f01f437a2..00000000000
--- a/spec/controllers/ci/projects_controller_spec.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-require 'spec_helper'
-
-describe Ci::ProjectsController do
- let(:visibility) { :public }
- let!(:project) { create(:empty_project, visibility, ci_id: 1) }
- let(:ci_id) { project.ci_id }
-
- describe '#index' do
- context 'user signed in' do
- before do
- sign_in(create(:user))
- get(:index)
- end
-
- it 'redirects to /' do
- expect(response).to redirect_to(root_path)
- end
- end
-
- context 'user not signed in' do
- before { get(:index) }
-
- it 'redirects to sign in page' do
- expect(response).to redirect_to(new_user_session_path)
- end
- end
- end
-
- ##
- # Specs for *deprecated* CI badge
- #
- describe '#badge' do
- shared_examples 'badge provider' do
- it 'shows badge' do
- expect(response.status).to eq 200
- expect(response.headers)
- .to include('Content-Type' => 'image/svg+xml')
- end
- end
-
- context 'user not signed in' do
- before { get(:badge, id: ci_id) }
-
- context 'project has no ci_id reference' do
- let(:ci_id) { 123 }
-
- it 'returns 404' do
- expect(response.status).to eq 404
- end
- end
-
- context 'project is public' do
- let(:visibility) { :public }
- it_behaves_like 'badge provider'
- end
-
- context 'project is private' do
- let(:visibility) { :private }
- it_behaves_like 'badge provider'
- end
- end
-
- context 'user signed in' do
- let(:user) { create(:user) }
- before { sign_in(user) }
- before { get(:badge, id: ci_id) }
-
- context 'private is internal' do
- let(:visibility) { :internal }
- it_behaves_like 'badge provider'
- end
- end
- end
-end
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 640baa3a01c..b223a22ae60 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -166,7 +166,7 @@ describe Projects::CommitController do
post(:revert,
namespace_id: project.namespace,
project_id: project,
- target_branch: 'master',
+ start_branch: 'master',
id: commit.id)
expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master')
@@ -179,7 +179,7 @@ describe Projects::CommitController do
post(:revert,
namespace_id: project.namespace,
project_id: project,
- target_branch: 'master',
+ start_branch: 'master',
id: commit.id)
end
@@ -188,7 +188,7 @@ describe Projects::CommitController do
post(:revert,
namespace_id: project.namespace,
project_id: project,
- target_branch: 'master',
+ start_branch: 'master',
id: commit.id)
expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, commit.id)
@@ -215,7 +215,7 @@ describe Projects::CommitController do
post(:cherry_pick,
namespace_id: project.namespace,
project_id: project,
- target_branch: 'master',
+ start_branch: 'master',
id: master_pickable_commit.id)
expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master')
@@ -228,7 +228,7 @@ describe Projects::CommitController do
post(:cherry_pick,
namespace_id: project.namespace,
project_id: project,
- target_branch: 'master',
+ start_branch: 'master',
id: master_pickable_commit.id)
end
@@ -237,7 +237,7 @@ describe Projects::CommitController do
post(:cherry_pick,
namespace_id: project.namespace,
project_id: project,
- target_branch: 'master',
+ start_branch: 'master',
id: master_pickable_commit.id)
expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index c4a7aa7d63e..049bae1899d 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -9,7 +9,23 @@ describe Projects::GraphsController do
project.team << [user, :master]
end
- describe 'GET #languages' do
+ describe 'GET languages' do
+ it "redirects_to action charts" do
+ get(:commits, namespace_id: project.namespace.path, project_id: project.path, id: 'master')
+
+ expect(response).to redirect_to action: :charts
+ end
+ end
+
+ describe 'GET commits' do
+ it "redirects_to action charts" do
+ get(:commits, namespace_id: project.namespace.path, project_id: project.path, id: 'master')
+
+ expect(response).to redirect_to action: :charts
+ end
+ end
+
+ describe 'GET charts' do
let(:linguist_repository) do
double(languages: {
'Ruby' => 1000,
@@ -34,7 +50,7 @@ describe Projects::GraphsController do
end
it 'sets the correct colour according to language' do
- get(:languages, namespace_id: project.namespace, project_id: project, id: 'master')
+ get(:charts, namespace_id: project.namespace, project_id: project, id: 'master')
expected_values.each do |val|
expect(assigns(:languages)).to include(a_hash_including(val))
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 1ced666bb36..250d64f7055 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -43,7 +43,8 @@ describe Projects::MergeRequestsController do
submit_new_merge_request(format: :json)
expect(response).to be_ok
- expect(json_response).not_to be_empty
+ expect(json_response).to have_key 'pipelines'
+ expect(json_response['pipelines']).not_to be_empty
end
end
end
@@ -319,41 +320,41 @@ describe Projects::MergeRequestsController do
merge_with_sha
end
- context 'when merge_when_build_succeeds is passed' do
- def merge_when_build_succeeds
- post :merge, base_params.merge(sha: merge_request.diff_head_sha, merge_when_build_succeeds: '1')
+ context 'when the pipeline succeeds is passed' do
+ def merge_when_pipeline_succeeds
+ post :merge, base_params.merge(sha: merge_request.diff_head_sha, merge_when_pipeline_succeeds: '1')
end
before do
create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
end
- it 'returns :merge_when_build_succeeds' do
- merge_when_build_succeeds
+ it 'returns :merge_when_pipeline_succeeds' do
+ merge_when_pipeline_succeeds
- expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+ expect(assigns(:status)).to eq(:merge_when_pipeline_succeeds)
end
- it 'sets the MR to merge when the build succeeds' do
- service = double(:merge_when_build_succeeds_service)
+ it 'sets the MR to merge when the pipeline succeeds' do
+ service = double(:merge_when_pipeline_succeeds_service)
expect(MergeRequests::MergeWhenPipelineSucceedsService)
.to receive(:new).with(project, anything, anything)
.and_return(service)
expect(service).to receive(:execute).with(merge_request)
- merge_when_build_succeeds
+ merge_when_pipeline_succeeds
end
- context 'when project.only_allow_merge_if_build_succeeds? is true' do
+ context 'when project.only_allow_merge_if_pipeline_succeeds? is true' do
before do
- project.update_column(:only_allow_merge_if_build_succeeds, true)
+ project.update_column(:only_allow_merge_if_pipeline_succeeds, true)
end
- it 'returns :merge_when_build_succeeds' do
- merge_when_build_succeeds
+ it 'returns :merge_when_pipeline_succeeds' do
+ merge_when_pipeline_succeeds
- expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+ expect(assigns(:status)).to eq(:merge_when_pipeline_succeeds)
end
end
end
@@ -1134,14 +1135,14 @@ describe Projects::MergeRequestsController do
end
context 'when waiting for build' do
- let(:merge_request) { create(:merge_request, source_project: project, merge_when_build_succeeds: true, merge_user: user) }
+ let(:merge_request) { create(:merge_request, source_project: project, merge_when_pipeline_succeeds: true, merge_user: user) }
it 'returns an OK response' do
expect(response).to have_http_status(:ok)
end
- it 'sets status to :merge_when_build_succeeds' do
- expect(assigns(:status)).to eq(:merge_when_build_succeeds)
+ it 'sets status to :merge_when_pipeline_succeeds' do
+ expect(assigns(:status)).to eq(:merge_when_pipeline_succeeds)
expect(response).to render_template('merge')
end
end
diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb
index b14d275f7fa..b32eb39b1fb 100644
--- a/spec/controllers/root_controller_spec.rb
+++ b/spec/controllers/root_controller_spec.rb
@@ -2,6 +2,26 @@ require 'spec_helper'
describe RootController do
describe 'GET index' do
+ context 'when user is not logged in' do
+ it 'redirects to the sign-in page' do
+ get :index
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+
+ context 'when a custom home page URL is defined' do
+ before do
+ stub_application_setting(home_page_url: 'https://gitlab.com')
+ end
+
+ it 'redirects the user to the custom home page URL' do
+ get :index
+
+ expect(response).to redirect_to('https://gitlab.com')
+ end
+ end
+ end
+
context 'with a user' do
let(:user) { create(:user) }
@@ -12,55 +32,60 @@ describe RootController do
context 'who has customized their dashboard setting for starred projects' do
before do
- user.update_attribute(:dashboard, 'stars')
+ user.dashboard = 'stars'
end
it 'redirects to their specified dashboard' do
get :index
+
expect(response).to redirect_to starred_dashboard_projects_path
end
end
context 'who has customized their dashboard setting for project activities' do
before do
- user.update_attribute(:dashboard, 'project_activity')
+ user.dashboard = 'project_activity'
end
it 'redirects to the activity list' do
get :index
+
expect(response).to redirect_to activity_dashboard_path
end
end
context 'who has customized their dashboard setting for starred project activities' do
before do
- user.update_attribute(:dashboard, 'starred_project_activity')
+ user.dashboard = 'starred_project_activity'
end
it 'redirects to the activity list' do
get :index
+
expect(response).to redirect_to activity_dashboard_path(filter: 'starred')
end
end
context 'who has customized their dashboard setting for groups' do
before do
- user.update_attribute(:dashboard, 'groups')
+ user.dashboard = 'groups'
end
it 'redirects to their group list' do
get :index
+
expect(response).to redirect_to dashboard_groups_path
end
end
context 'who has customized their dashboard setting for todos' do
before do
- user.update_attribute(:dashboard, 'todos')
+ user.dashboard = 'todos'
end
it 'redirects to their todo list' do
get :index
+
expect(response).to redirect_to dashboard_todos_path
end
end
@@ -68,6 +93,7 @@ describe RootController do
context 'who uses the default dashboard setting' do
it 'renders the default dashboard' do
get :index
+
expect(response).to render_template 'dashboard/projects/index'
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index a90534d10ba..cabe128acf7 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -76,6 +76,18 @@ FactoryGirl.define do
manual
end
+ trait :tags do
+ tag_list [:docker, :ruby]
+ end
+
+ trait :on_tag do
+ tag true
+ end
+
+ trait :triggered do
+ trigger_request factory: :ci_trigger_request_with_variables
+ end
+
after(:build) do |build, evaluator|
build.project = build.pipeline.project
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 22f84150bb3..ae0bbbd6aeb 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -59,8 +59,8 @@ FactoryGirl.define do
target_branch "master"
end
- trait :merge_when_build_succeeds do
- merge_when_build_succeeds true
+ trait :merge_when_pipeline_succeeds do
+ merge_when_pipeline_succeeds true
merge_user author
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 586efdefdb3..04de3512125 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -39,6 +39,10 @@ FactoryGirl.define do
trait :empty_repo do
after(:create) do |project|
project.create_repository
+
+ # We delete hooks so that gitlab-shell will not try to authenticate with
+ # an API that isn't running
+ FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'hooks'))
end
end
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index a5265f1b189..c1ac3bb84ad 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -18,11 +18,6 @@ FactoryGirl.define do
action { Todo::DIRECTLY_ADDRESSED }
end
- trait :on_commit do
- commit_id RepoHelpers.sample_commit.id
- target_type "Commit"
- end
-
trait :build_failed do
action { Todo::BUILD_FAILED }
target factory: :merge_request
@@ -48,4 +43,13 @@ FactoryGirl.define do
state :done
end
end
+
+ factory :on_commit_todo, class: Todo do
+ project factory: :empty_project
+ author
+ user
+ action { Todo::ASSIGNED }
+ commit_id RepoHelpers.sample_commit.id
+ target_type "Commit"
+ end
end
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
new file mode 100644
index 00000000000..c977f266296
--- /dev/null
+++ b/spec/features/dashboard/activity_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+RSpec.describe 'Dashboard Activity', feature: true do
+ before do
+ login_as(create :user)
+ visit activity_dashboard_path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+end
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
new file mode 100644
index 00000000000..ca04107d33a
--- /dev/null
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe 'Dashboard Groups page', js: true, feature: true do
+ include WaitForAjax
+
+ let!(:user) { create :user }
+ let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, :nested) }
+ let!(:another_group) { create(:group) }
+
+ before do
+ group.add_owner(user)
+ nested_group.add_owner(user)
+
+ login_as(user)
+
+ visit dashboard_groups_path
+ end
+
+ it 'shows groups user is member of' do
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ end
+
+ it 'filters groups' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).not_to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ end
+
+ it 'resets search when user cleans the input' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ fill_in 'filter_groups', with: ""
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+ end
+end
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 2db1cf71209..f4420814c3a 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -45,4 +45,7 @@ RSpec.describe 'Dashboard Issues', feature: true do
expect(page).to have_content(assigned_issue.title)
expect(page).to have_content(other_issue.title)
end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
new file mode 100644
index 00000000000..63eb5c697c2
--- /dev/null
+++ b/spec/features/dashboard/projects_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+
+RSpec.describe 'Dashboard Projects', feature: true do
+ before do
+ login_as(create :user)
+ visit dashboard_projects_path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+end
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
new file mode 100644
index 00000000000..773ae4b38bc
--- /dev/null
+++ b/spec/features/explore/groups_list_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe 'Explore Groups page', js: true, feature: true do
+ include WaitForAjax
+
+ let!(:user) { create :user }
+ let!(:group) { create(:group) }
+ let!(:public_group) { create(:group, :public) }
+ let!(:private_group) { create(:group, :private) }
+
+ before do
+ group.add_owner(user)
+
+ login_as(user)
+
+ visit explore_groups_path
+ end
+
+ it 'shows groups user is member of' do
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ end
+
+ it 'filters groups' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).not_to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ end
+
+ it 'resets search when user cleans the input' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ fill_in 'filter_groups', with: ""
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+ end
+end
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
new file mode 100644
index 00000000000..3b481cba424
--- /dev/null
+++ b/spec/features/groups/activity_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+feature 'Group activity page', feature: true do
+ let(:group) { create(:group) }
+ let(:path) { activity_group_path(group) }
+
+ context 'when signed in' do
+ before do
+ user = create(:group_member, :developer, user: create(:user), group: group ).user
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 476eca17a9d..1b3747c390b 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -5,4 +5,22 @@ feature 'Group issues page', feature: true do
let(:issuable) { create(:issue, project: project, title: "this is my created issuable")}
include_examples 'project features apply to issuables', Issue
+
+ context 'rss feed' do
+ let(:access_level) { ProjectFeature::ENABLED }
+
+ context 'when signed in' do
+ let(:user) { user_in_group }
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ let(:user) { nil }
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+ end
end
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
new file mode 100644
index 00000000000..fb39693e8ca
--- /dev/null
+++ b/spec/features/groups/show_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+feature 'Group show page', feature: true do
+ let(:group) { create(:group) }
+ let(:path) { group_path(group) }
+
+ context 'when signed in' do
+ before do
+ user = create(:group_member, :developer, user: create(:user), group: group ).user
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 73e43316dc7..3ab3d2d4229 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -67,6 +67,18 @@ describe 'Awards Emoji', feature: true do
expect(page).not_to have_selector(emoji_counter)
end
end
+
+ context 'execute /award slash command' do
+ it 'toggles the emoji award on noteable', js: true do
+ execute_slash_command('/award :100:')
+
+ expect(find(noteable_award_counter)).to have_text("1")
+
+ execute_slash_command('/award :100:')
+
+ expect(page).not_to have_selector(noteable_award_counter)
+ end
+ end
end
end
@@ -80,6 +92,15 @@ describe 'Awards Emoji', feature: true do
end
end
+ def execute_slash_command(cmd)
+ within('.js-main-target-form') do
+ fill_in 'note[note]', with: cmd
+ click_button 'Comment'
+ end
+
+ wait_for_ajax
+ end
+
def thumbsup_emoji
page.all(emoji_counter).first
end
@@ -92,6 +113,10 @@ describe 'Awards Emoji', feature: true do
'span.js-counter'
end
+ def noteable_award_counter
+ ".awards .active"
+ end
+
def toggle_smiley_emoji(status)
within('.note') do
find('.note-emoji-button').click
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
index f2f8f11ab28..0ceaf7bc830 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -34,7 +34,7 @@ feature 'Merge immediately', :feature, :js do
click_link 'Merge Immediately'
- expect(find('.js-merge-button')).to have_content('Merge in progress')
+ expect(find('.js-merge-when-pipeline-succeeds-button')).to have_content('Merge in progress')
end
end
end
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index 2ea9c317bd1..ed7193b9777 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -75,7 +75,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
context 'when it was enabled and then canceled' do
let(:merge_request) do
create(:merge_request_with_diffs,
- :merge_when_build_succeeds,
+ :merge_when_pipeline_succeeds,
source_project: project,
title: 'Bug NS-04',
author: user,
@@ -97,7 +97,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
author: user,
merge_user: user,
title: 'MepMep',
- merge_when_build_succeeds: true)
+ merge_when_pipeline_succeeds: true)
end
let!(:build) do
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
index d2f5c4afc93..447764566e0 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Only allow merge requests to be merged if the build succeeds', feature: true do
+feature 'Only allow merge requests to be merged if the pipeline succeeds', feature: true do
let(:merge_request) { create(:merge_request_with_diffs) }
let(:project) { merge_request.target_project }
@@ -27,9 +27,9 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature:
status: status)
end
- context 'when merge requests can only be merged if the build succeeds' do
+ context 'when merge requests can only be merged if the pipeline succeeds' do
before do
- project.update_attribute(:only_allow_merge_if_build_succeeds, true)
+ project.update_attribute(:only_allow_merge_if_pipeline_succeeds, true)
end
context 'when CI is running' do
@@ -88,7 +88,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature:
context 'when merge requests can be merged when the build failed' do
before do
- project.update_attribute(:only_allow_merge_if_build_succeeds, false)
+ project.update_attribute(:only_allow_merge_if_pipeline_succeeds, false)
end
context 'when CI is running' do
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index 4ad944366c8..b575aeff0d8 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -3,8 +3,8 @@ require 'rails_helper'
describe 'Merge request', :feature, :js do
include WaitForAjax
- let(:project) { create(:project) }
let(:user) { create(:user) }
+ let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
before do
@@ -31,7 +31,7 @@ describe 'Merge request', :feature, :js do
wait_for_ajax
- expect(page).to have_selector('.accept_merge_request')
+ expect(page).to have_selector('.accept-merge-request')
end
end
@@ -51,6 +51,69 @@ describe 'Merge request', :feature, :js do
expect(find('.js-environment-link')[:href]).to include(environment.formatted_external_url)
end
end
+
+ it 'shows green accept merge request button' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_ajax
+ expect(page).to have_selector('.accept-merge-request.btn-create')
+ end
+ end
+
+ context 'view merge request with external CI service' do
+ before do
+ create(:service, project: project,
+ active: true,
+ type: 'CiService',
+ category: 'ci')
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'has danger button while waiting for external CI status' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_ajax
+ expect(page).to have_selector('.accept-merge-request.btn-danger')
+ end
+ end
+
+ context 'view merge request with failed GitLab CI pipelines' do
+ before do
+ commit_status = create(:commit_status, project: project, status: 'failed')
+ pipeline = create(:ci_pipeline, project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ status: 'failed',
+ statuses: [commit_status])
+ create(:ci_build, :pending, pipeline: pipeline)
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'has danger button when not succeeded' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_ajax
+ expect(page).to have_selector('.accept-merge-request.btn-danger')
+ end
+ end
+
+ context 'view merge request with MWBS button' do
+ before do
+ commit_status = create(:commit_status, project: project, status: 'pending')
+ pipeline = create(:ci_pipeline, project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ status: 'pending',
+ statuses: [commit_status])
+ create(:ci_build, :pending, pipeline: pipeline)
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'has info button when MWBS button' do
+ # Wait for the `ci_status` and `merge_check` requests
+ wait_for_ajax
+ expect(page).to have_selector('.merge-when-pipeline-succeeds.btn-info')
+ end
end
context 'merge error' do
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index a2e40546588..c3297de709a 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -24,7 +24,7 @@ feature 'Milestone', feature: true do
find('input[name="commit"]').click
expect(find('.alert-success')).to have_content('Assign some issues to this milestone.')
- expect(page).to have_content('Nov 16, 2016 - Dec 16, 2016')
+ expect(page).to have_content('Nov 16, 2016–Dec 16, 2016')
end
end
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
new file mode 100644
index 00000000000..b47c6d431eb
--- /dev/null
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+feature 'Project Activity RSS' do
+ let(:project) { create(:empty_project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { activity_namespace_project_path(project.namespace, project) }
+
+ before do
+ create(:issue, project: project)
+ end
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ end
+end
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index 7baf7913424..0b972d2a439 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -60,6 +60,7 @@ describe 'Cherry-pick Commits' do
click_button 'Cherry-pick'
end
expect(page).to have_content('The commit has been successfully cherry-picked. You can now submit a merge request to get this change into the original branch.')
+ expect(page).to have_content("From cherry-pick-#{master_pickable_commit.short_id} into master")
end
end
diff --git a/spec/features/projects/commit/rss_spec.rb b/spec/features/projects/commit/rss_spec.rb
new file mode 100644
index 00000000000..6e0e1916f87
--- /dev/null
+++ b/spec/features/projects/commit/rss_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+feature 'Project Commits RSS' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_commits_path(project.namespace, project, :master) }
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
index 8120a51c515..726469daba4 100644
--- a/spec/features/projects/guest_navigation_menu_spec.rb
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -15,13 +15,11 @@ describe "Guest navigation menu" do
within(".nav-links") do
expect(page).to have_content 'Project'
- expect(page).to have_content 'Activity'
expect(page).to have_content 'Issues'
expect(page).to have_content 'Wiki'
expect(page).not_to have_content 'Repository'
expect(page).not_to have_content 'Pipelines'
- expect(page).not_to have_content 'Graphs'
expect(page).not_to have_content 'Merge Requests'
end
end
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
new file mode 100644
index 00000000000..71429f00095
--- /dev/null
+++ b/spec/features/projects/issues/rss_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+feature 'Project Issues RSS' do
+ let(:project) { create(:empty_project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_issues_path(project.namespace, project) }
+
+ before do
+ create(:issue, project: project)
+ end
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/projects/main/rss_spec.rb b/spec/features/projects/main/rss_spec.rb
new file mode 100644
index 00000000000..b1a3af612a1
--- /dev/null
+++ b/spec/features/projects/main/rss_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+feature 'Project RSS' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_path(project.namespace, project) }
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb
new file mode 100644
index 00000000000..df229d0aa78
--- /dev/null
+++ b/spec/features/projects/milestones/milestone_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+feature 'Project milestone', :feature do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) }
+ let(:milestone) { create(:milestone, project: project) }
+
+ before do
+ login_as(user)
+ end
+
+ context 'when project has enabled issues' do
+ before do
+ visit namespace_project_milestone_path(project.namespace, project, milestone)
+ end
+
+ it 'shows issues tab' do
+ within('#content-body') do
+ expect(page).to have_link 'Issues', href: '#tab-issues'
+ expect(page).to have_selector '.nav-links li.active', count: 1
+ expect(find('.nav-links li.active')).to have_content 'Issues'
+ end
+ end
+
+ it 'shows issues stats' do
+ expect(page).to have_content 'issues:'
+ end
+
+ it 'shows Browse Issues button' do
+ within('#content-body') do
+ expect(page).to have_link 'Browse Issues'
+ end
+ end
+ end
+
+ context 'when project has disabled issues' do
+ before do
+ project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
+ visit namespace_project_milestone_path(project.namespace, project, milestone)
+ end
+
+ it 'hides issues tab' do
+ within('#content-body') do
+ expect(page).not_to have_link 'Issues', href: '#tab-issues'
+ expect(page).to have_selector '.nav-links li.active', count: 1
+ expect(find('.nav-links li.active')).to have_content 'Merge Requests'
+ end
+ end
+
+ it 'hides issues stats' do
+ expect(page).to have_no_content 'issues:'
+ end
+
+ it 'hides Browse Issues button' do
+ within('#content-body') do
+ expect(page).not_to have_link 'Browse Issues'
+ end
+ end
+
+ it 'does not show an informative message' do
+ expect(page).not_to have_content('Assign some issues to this milestone.')
+ end
+ end
+end
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
new file mode 100644
index 00000000000..9ac51997d65
--- /dev/null
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+feature 'Project Tree RSS' do
+ let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let(:path) { namespace_project_tree_path(project.namespace, project, :master) }
+
+ context 'when signed in' do
+ before do
+ user = create(:user)
+ project.team << [user, :developer]
+ login_as(user)
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "an autodiscoverable RSS feed without a private token"
+ end
+end
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
new file mode 100644
index 00000000000..14564abb16d
--- /dev/null
+++ b/spec/features/users/rss_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+feature 'User RSS' do
+ let(:path) { user_path(create(:user)) }
+
+ context 'when signed in' do
+ before do
+ login_as(create(:user))
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button with current_user's private token"
+ end
+
+ context 'when signed out' do
+ before do
+ visit path
+ end
+
+ it_behaves_like "it has an RSS button without a private token"
+ end
+end
diff --git a/spec/helpers/milestones_helper_spec.rb b/spec/helpers/milestones_helper_spec.rb
index 14a95479339..68b20a1e4fc 100644
--- a/spec/helpers/milestones_helper_spec.rb
+++ b/spec/helpers/milestones_helper_spec.rb
@@ -17,7 +17,7 @@ describe MilestonesHelper do
it { expect(result_for(due_date: yesterday)).to eq("expired on #{yesterday_formatted}") }
it { expect(result_for(start_date: tomorrow)).to eq("starts on #{tomorrow_formatted}") }
it { expect(result_for(start_date: yesterday)).to eq("started on #{yesterday_formatted}") }
- it { expect(result_for(start_date: yesterday, due_date: tomorrow)).to eq("#{yesterday_formatted} - #{tomorrow_formatted}") }
+ it { expect(result_for(start_date: yesterday, due_date: tomorrow)).to eq("#{yesterday_formatted}–#{tomorrow_formatted}") }
end
describe '#milestone_counts' do
diff --git a/spec/helpers/rss_helper_spec.rb b/spec/helpers/rss_helper_spec.rb
new file mode 100644
index 00000000000..f3f174f3d14
--- /dev/null
+++ b/spec/helpers/rss_helper_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe RssHelper do
+ describe '#rss_url_options' do
+ context 'when signed in' do
+ it "includes the current_user's private_token" do
+ current_user = create(:user)
+ allow(helper).to receive(:current_user).and_return(current_user)
+ expect(helper.rss_url_options).to include private_token: current_user.private_token
+ end
+ end
+
+ context 'when signed out' do
+ it "does not have a private_token" do
+ allow(helper).to receive(:current_user).and_return(nil)
+ expect(helper.rss_url_options[:private_token]).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index 192916fbc6a..be31f644e20 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -8,7 +8,7 @@
require('~/boards/models/list');
require('~/boards/models/label');
require('~/boards/stores/boards_store');
-const boardCard = require('~/boards/components/board_card');
+const boardCard = require('~/boards/components/board_card').default;
require('./mock_data');
describe('Issue card', () => {
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
new file mode 100644
index 00000000000..22c9f12951b
--- /dev/null
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -0,0 +1,191 @@
+/* global boardsMockInterceptor */
+/* global BoardService */
+/* global List */
+/* global listObj */
+
+import Vue from 'vue';
+import boardNewIssue from '~/boards/components/board_new_issue';
+
+require('~/boards/models/list');
+require('./mock_data');
+require('es6-promise').polyfill();
+
+describe('Issue boards new issue form', () => {
+ let vm;
+ let list;
+ const promiseReturn = {
+ json() {
+ return {
+ iid: 100,
+ };
+ },
+ };
+ const submitIssue = () => {
+ vm.$el.querySelector('.btn-success').click();
+ };
+
+ beforeEach((done) => {
+ const BoardNewIssueComp = Vue.extend(boardNewIssue);
+
+ Vue.http.interceptors.push(boardsMockInterceptor);
+ gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
+ gl.issueBoards.BoardsStore.create();
+ gl.IssueBoardsApp = new Vue();
+
+ setTimeout(() => {
+ list = new List(listObj);
+
+ spyOn(gl.boardService, 'newIssue').and.callFake(() => new Promise((resolve, reject) => {
+ if (vm.title === 'error') {
+ reject();
+ } else {
+ resolve(promiseReturn);
+ }
+ }));
+
+ vm = new BoardNewIssueComp({
+ propsData: {
+ list,
+ },
+ }).$mount();
+
+ done();
+ }, 0);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
+ });
+
+ it('disables submit button if title is empty', () => {
+ expect(vm.$el.querySelector('.btn-success').disabled).toBe(true);
+ });
+
+ it('enables submit button if title is not empty', (done) => {
+ vm.title = 'Testing Title';
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.form-control').value).toBe('Testing Title');
+ expect(vm.$el.querySelector('.btn-success').disabled).not.toBe(true);
+
+ done();
+ }, 0);
+ });
+
+ it('clears title after clicking cancel', (done) => {
+ vm.$el.querySelector('.btn-default').click();
+
+ setTimeout(() => {
+ expect(vm.title).toBe('');
+ done();
+ }, 0);
+ });
+
+ it('does not create new issue if title is empty', (done) => {
+ submitIssue();
+
+ setTimeout(() => {
+ expect(gl.boardService.newIssue).not.toHaveBeenCalled();
+ done();
+ }, 0);
+ });
+
+ describe('submit success', () => {
+ it('creates new issue', (done) => {
+ vm.title = 'submit title';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(gl.boardService.newIssue).toHaveBeenCalled();
+ done();
+ }, 0);
+ });
+
+ it('enables button after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(vm.$el.querySelector('.btn-success').disbled).not.toBe(true);
+ done();
+ }, 0);
+ });
+
+ it('clears title after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(vm.title).toBe('');
+ done();
+ }, 0);
+ });
+
+ it('adds new issue to list after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(list.issues.length).toBe(2);
+ expect(list.issues[1].title).toBe('submit issue');
+ expect(list.issues[1].subscribed).toBe(true);
+ done();
+ }, 0);
+ });
+
+ it('sets detail issue after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe('submit issue');
+ done();
+ });
+ });
+
+ it('sets detail list after submit', (done) => {
+ vm.title = 'submit issue';
+
+ setTimeout(() => {
+ submitIssue();
+
+ expect(gl.issueBoards.BoardsStore.detail.list.id).toBe(list.id);
+ done();
+ }, 0);
+ });
+ });
+
+ describe('submit error', () => {
+ it('removes issue', (done) => {
+ vm.title = 'error';
+
+ setTimeout(() => {
+ submitIssue();
+
+ setTimeout(() => {
+ expect(list.issues.length).toBe(1);
+ done();
+ }, 500);
+ }, 0);
+ });
+
+ it('shows error', (done) => {
+ vm.title = 'error';
+ submitIssue();
+
+ setTimeout(() => {
+ submitIssue();
+
+ setTimeout(() => {
+ expect(vm.error).toBe(true);
+ done();
+ }, 500);
+ }, 0);
+ });
+ });
+});
diff --git a/spec/javascripts/environments/environment_actions_spec.js.es6 b/spec/javascripts/environments/environment_actions_spec.js.es6
index 850586f9f3a..d50d45d295e 100644
--- a/spec/javascripts/environments/environment_actions_spec.js.es6
+++ b/spec/javascripts/environments/environment_actions_spec.js.es6
@@ -23,7 +23,6 @@ describe('Actions Component', () => {
el: document.querySelector('.test-dom-element'),
propsData: {
actions: actionsMock,
- playIconSvg: '<svg></svg>',
},
});
@@ -34,33 +33,4 @@ describe('Actions Component', () => {
component.$el.querySelector('.dropdown-menu li a').getAttribute('href'),
).toEqual(actionsMock[0].play_path);
});
-
- it('should render a dropdown with the provided svg', () => {
- const actionsMock = [
- {
- name: 'bar',
- play_path: 'https://gitlab.com/play',
- },
- {
- name: 'foo',
- play_path: '#',
- },
- ];
-
- const component = new ActionsComponent({
- el: document.querySelector('.test-dom-element'),
- propsData: {
- actions: actionsMock,
- playIconSvg: '<svg></svg>',
- },
- });
-
- expect(
- component.$el.querySelector('.js-dropdown-play-icon-container').children,
- ).toContain('svg');
-
- expect(
- component.$el.querySelector('.js-action-play-icon-container').children,
- ).toContain('svg');
- });
});
diff --git a/spec/javascripts/fixtures/projects.json b/spec/javascripts/fixtures/projects.json
index 4ce7f5c601a..1339ee00870 100644
--- a/spec/javascripts/fixtures/projects.json
+++ b/spec/javascripts/fixtures/projects.json
@@ -43,7 +43,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 0,
"permissions": {
"project_access": null,
@@ -88,7 +88,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": {
@@ -139,7 +139,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": true,
+ "only_allow_merge_if_pipeline_succeeds": true,
"open_issues_count": 4,
"permissions": {
"project_access": null,
@@ -187,7 +187,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": true,
+ "only_allow_merge_if_pipeline_succeeds": true,
"open_issues_count": 4,
"permissions": {
"project_access": null,
@@ -235,7 +235,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": null,
@@ -283,7 +283,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": {
@@ -334,7 +334,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 3,
"permissions": {
"project_access": null,
@@ -382,7 +382,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": {
@@ -433,7 +433,7 @@
"avatar_url": null,
"star_count": 0,
"forks_count": 0,
- "only_allow_merge_if_build_succeeds": false,
+ "only_allow_merge_if_pipeline_succeeds": false,
"open_issues_count": 5,
"permissions": {
"project_access": null,
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js.es6 b/spec/javascripts/lib/utils/text_utility_spec.js.es6
index 06b69b8ac17..4200e943121 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js.es6
+++ b/spec/javascripts/lib/utils/text_utility_spec.js.es6
@@ -46,5 +46,65 @@ require('~/lib/utils/text_utility');
expect(gl.text.highCountTrim(45)).toBe(45);
});
});
+
+ describe('gl.text.insertText', () => {
+ let textArea;
+
+ beforeAll(() => {
+ textArea = document.createElement('textarea');
+ document.querySelector('body').appendChild(textArea);
+ });
+
+ afterAll(() => {
+ textArea.parentNode.removeChild(textArea);
+ });
+
+ describe('without selection', () => {
+ it('inserts the tag on an empty line', () => {
+ const initialValue = '';
+
+ textArea.value = initialValue;
+ textArea.selectionStart = 0;
+ textArea.selectionEnd = 0;
+
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+
+ expect(textArea.value).toEqual(`${initialValue}* `);
+ });
+
+ it('inserts the tag on a new line if the current one is not empty', () => {
+ const initialValue = 'some text';
+
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
+
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+
+ expect(textArea.value).toEqual(`${initialValue}\n* `);
+ });
+
+ it('inserts the tag on the same line if the current line only contains spaces', () => {
+ const initialValue = ' ';
+
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
+
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+
+ expect(textArea.value).toEqual(`${initialValue}* `);
+ });
+
+ it('inserts the tag on the same line if the current line only contains tabs', () => {
+ const initialValue = '\t\t\t';
+
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
+
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+
+ expect(textArea.value).toEqual(`${initialValue}* `);
+ });
+ });
+ });
});
})();
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index f132537b943..90a429beeca 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,7 +1,6 @@
/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */
/* global NewBranchForm */
-require('jquery-ui/ui/autocomplete');
require('~/new_branch_form');
(function() {
diff --git a/spec/javascripts/user_callout_spec.js.es6 b/spec/javascripts/user_callout_spec.js.es6
index 6ee63f56a26..205e72af600 100644
--- a/spec/javascripts/user_callout_spec.js.es6
+++ b/spec/javascripts/user_callout_spec.js.es6
@@ -3,35 +3,55 @@ const UserCallout = require('~/user_callout');
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
const Cookie = window.Cookies;
-describe('UserCallout', () => {
+describe('UserCallout', function () {
const fixtureName = 'static/user_callout.html.raw';
preloadFixtures(fixtureName);
- beforeEach(function () {
+ beforeEach(() => {
loadFixtures(fixtureName);
+ Cookie.remove(USER_CALLOUT_COOKIE);
+
this.userCallout = new UserCallout();
this.closeButton = $('.close-user-callout');
this.userCalloutBtn = $('.user-callout-btn');
this.userCalloutContainer = $('.user-callout');
- Cookie.set(USER_CALLOUT_COOKIE, 'false');
});
- afterEach(function () {
- Cookie.set(USER_CALLOUT_COOKIE, 'false');
+ it('does not show when cookie is set not defined', () => {
+ expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeUndefined();
+ expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
- it('shows when cookie is set to false', function () {
+ it('shows when cookie is set to false', () => {
+ Cookie.set(USER_CALLOUT_COOKIE, 'false');
+
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeDefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
- it('hides when user clicks on the dismiss-icon', function () {
+ it('hides when user clicks on the dismiss-icon', () => {
this.closeButton.click();
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
});
- it('hides when user clicks on the "check it out" button', function () {
+ it('hides when user clicks on the "check it out" button', () => {
this.userCalloutBtn.click();
expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
});
});
+
+describe('UserCallout when cookie is present', function () {
+ const fixtureName = 'static/user_callout.html.raw';
+ preloadFixtures(fixtureName);
+
+ beforeEach(() => {
+ loadFixtures(fixtureName);
+ Cookie.set(USER_CALLOUT_COOKIE, 'true');
+ this.userCallout = new UserCallout();
+ this.userCalloutContainer = $('.user-callout');
+ });
+
+ it('removes the DOM element', () => {
+ expect(this.userCalloutContainer.length).toBe(0);
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6
index dd495cb43bc..9cb067921a7 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6
@@ -6,12 +6,10 @@ describe('Pagination component', () => {
const changeChanges = {
one: '',
- two: '',
};
- const change = (one, two) => {
+ const change = (one) => {
changeChanges.one = one;
- changeChanges.two = two;
};
it('should render and start at page 1', () => {
@@ -34,7 +32,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: '1' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the previous page', () => {
@@ -55,7 +52,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: 'Prev' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the next page', () => {
@@ -76,7 +72,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: 'Next' } });
expect(changeChanges.one).toEqual(5);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the last page', () => {
@@ -97,7 +92,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: 'Last >>' } });
expect(changeChanges.one).toEqual(10);
- expect(changeChanges.two).toEqual(null);
});
it('should go to the first page', () => {
@@ -118,7 +112,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: '<< First' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
it('should do nothing', () => {
@@ -139,7 +132,6 @@ describe('Pagination component', () => {
component.changePage({ target: { innerText: '...' } });
expect(changeChanges.one).toEqual(1);
- expect(changeChanges.two).toEqual(null);
});
});
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index d5d128c1907..9873774909e 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -123,6 +123,12 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
end
+
+ it 'has the full group name as a title' do
+ doc = reference_filter("Hey #{reference}")
+
+ expect(doc.css('a').first.attr('title')).to eq group.full_name
+ end
end
it 'links with adjacent text' do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 1919542ca25..3f11f0a4516 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -47,7 +47,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :branch_names do
+ describe '#branch_names' do
subject { repository.branch_names }
it 'has SeedRepo::Repo::BRANCHES.size elements' do
@@ -57,7 +57,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.not_to include("branch-from-space") }
end
- describe :tag_names do
+ describe '#tag_names' do
subject { repository.tag_names }
it { is_expected.to be_kind_of Array }
@@ -78,49 +78,63 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { expect(metadata['ArchivePath']).to end_with extenstion }
end
- describe :archive do
+ describe '#archive_prefix' do
+ let(:project_name) { 'project-name'}
+
+ before do
+ expect(repository).to receive(:name).once.and_return(project_name)
+ end
+
+ it 'returns parameterised string for a ref containing slashes' do
+ prefix = repository.archive_prefix('test/branch', 'SHA')
+
+ expect(prefix).to eq("#{project_name}-test-branch-SHA")
+ end
+ end
+
+ describe '#archive' do
let(:metadata) { repository.archive_metadata('master', '/tmp') }
it_should_behave_like 'archive check', '.tar.gz'
end
- describe :archive_zip do
+ describe '#archive_zip' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'zip') }
it_should_behave_like 'archive check', '.zip'
end
- describe :archive_bz2 do
+ describe '#archive_bz2' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'tbz2') }
it_should_behave_like 'archive check', '.tar.bz2'
end
- describe :archive_fallback do
+ describe '#archive_fallback' do
let(:metadata) { repository.archive_metadata('master', '/tmp', 'madeup') }
it_should_behave_like 'archive check', '.tar.gz'
end
- describe :size do
+ describe '#size' do
subject { repository.size }
it { is_expected.to be < 2 }
end
- describe :has_commits? do
+ describe '#has_commits?' do
it { expect(repository.has_commits?).to be_truthy }
end
- describe :empty? do
+ describe '#empty?' do
it { expect(repository.empty?).to be_falsey }
end
- describe :bare? do
+ describe '#bare?' do
it { expect(repository.bare?).to be_truthy }
end
- describe :heads do
+ describe '#heads' do
let(:heads) { repository.heads }
subject { heads }
@@ -147,7 +161,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :ref_names do
+ describe '#ref_names' do
let(:ref_names) { repository.ref_names }
subject { ref_names }
@@ -164,7 +178,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :search_files do
+ describe '#search_files' do
let(:results) { repository.search_files('rails', 'master') }
subject { results }
@@ -200,7 +214,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context :submodules do
+ context '#submodules' do
let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) }
context 'where repo has submodules' do
@@ -264,7 +278,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe :commit_count do
+ describe '#commit_count' do
it { expect(repository.commit_count("master")).to eq(25) }
it { expect(repository.commit_count("feature")).to eq(9) }
end
diff --git a/spec/lib/gitlab/gitaly_client/notifications_spec.rb b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
new file mode 100644
index 00000000000..a6252c99aa1
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Gitlab::GitalyClient::Notifications do
+ let(:client) { Gitlab::GitalyClient::Notifications.new }
+
+ before do
+ allow(Gitlab.config.gitaly).to receive(:socket_path).and_return('path/to/gitaly.socket')
+ end
+
+ describe '#post_receive' do
+ let(:repo_path) { '/path/to/my_repo.git' }
+
+ it 'sends a post_receive message' do
+ expect_any_instance_of(Gitaly::Notifications::Stub).
+ to receive(:post_receive).with(post_receive_request_with_repo_path(repo_path))
+
+ client.post_receive(repo_path)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 06617f3b007..eef283c2460 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -153,6 +153,7 @@ project:
- gitlab_issue_tracker_service
- external_wiki_service
- kubernetes_service
+- mock_ci_service
- forked_project_link
- forked_from_project
- forked_project_links
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 2e9f60432b4..c3d5c451a3c 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2539,7 +2539,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": true,
+ "merge_when_pipeline_succeeds": true,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -2976,7 +2976,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -3260,7 +3260,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -3544,7 +3544,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -4234,7 +4234,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -4782,7 +4782,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -5281,7 +5281,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -5541,7 +5541,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
@@ -6231,7 +6231,7 @@
"merge_params": {
"force_remove_source_branch": null
},
- "merge_when_build_succeeds": false,
+ "merge_when_pipeline_succeeds": false,
"merge_user_id": null,
"merge_commit_sha": null,
"deleted_at": null,
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 261c1f8447a..59204b8d08b 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -143,7 +143,7 @@ MergeRequest:
- updated_by_id
- merge_error
- merge_params
-- merge_when_build_succeeds
+- merge_when_pipeline_succeeds
- merge_user_id
- merge_commit_sha
- deleted_at
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b963ca4e542..5743c555cbe 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -181,20 +181,6 @@ describe Ci::Build, :models do
end
end
- describe '#create_from' do
- before do
- build.status = 'success'
- build.save
- end
- let(:create_from_build) { Ci::Build.create_from build }
-
- it 'exists a pending task' do
- expect(Ci::Build.pending.count(:all)).to eq 0
- create_from_build
- expect(Ci::Build.pending.count(:all)).to be > 0
- end
- end
-
describe '#depends_on_builds' do
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 9331dc41a5e..e000d0d38b3 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -37,12 +37,12 @@ describe MergeRequest, models: true do
end
it "is invalid without merge user" do
- subject.merge_when_build_succeeds = true
+ subject.merge_when_pipeline_succeeds = true
expect(subject).not_to be_valid
end
it "is valid with merge user" do
- subject.merge_when_build_succeeds = true
+ subject.merge_when_pipeline_succeeds = true
subject.merge_user = build(:user)
expect(subject).to be_valid
@@ -55,7 +55,7 @@ describe MergeRequest, models: true do
it { is_expected.to respond_to(:can_be_merged?) }
it { is_expected.to respond_to(:cannot_be_merged?) }
it { is_expected.to respond_to(:merge_params) }
- it { is_expected.to respond_to(:merge_when_build_succeeds) }
+ it { is_expected.to respond_to(:merge_when_pipeline_succeeds) }
end
describe '.in_projects' do
@@ -508,17 +508,17 @@ describe MergeRequest, models: true do
end
end
- describe "#reset_merge_when_build_succeeds" do
+ describe "#reset_merge_when_pipeline_succeeds" do
let(:merge_if_green) do
- create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user),
+ create :merge_request, merge_when_pipeline_succeeds: true, merge_user: create(:user),
merge_params: { "should_remove_source_branch" => "1", "commit_message" => "msg" }
end
it "sets the item to false" do
- merge_if_green.reset_merge_when_build_succeeds
+ merge_if_green.reset_merge_when_pipeline_succeeds
merge_if_green.reload
- expect(merge_if_green.merge_when_build_succeeds).to be_falsey
+ expect(merge_if_green.merge_when_pipeline_succeeds).to be_falsey
expect(merge_if_green.merge_params["should_remove_source_branch"]).to be_nil
expect(merge_if_green.merge_params["commit_message"]).to be_nil
end
@@ -812,7 +812,7 @@ describe MergeRequest, models: true do
end
describe '#check_if_can_be_merged' do
- let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
+ let(:project) { create(:empty_project, only_allow_merge_if_pipeline_succeeds: true) }
subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
@@ -927,7 +927,7 @@ describe MergeRequest, models: true do
end
describe '#mergeable_ci_state?' do
- let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
+ let(:project) { create(:empty_project, only_allow_merge_if_pipeline_succeeds: true) }
let(:pipeline) { create(:ci_empty_pipeline) }
subject { build(:merge_request, target_project: project) }
@@ -970,7 +970,7 @@ describe MergeRequest, models: true do
end
context 'when merges are not restricted to green builds' do
- subject { build(:merge_request, target_project: build(:empty_project, only_allow_merge_if_build_succeeds: false)) }
+ subject { build(:merge_request, target_project: build(:empty_project, only_allow_merge_if_pipeline_succeeds: false)) }
context 'and a failed pipeline is associated' do
before do
@@ -1575,7 +1575,7 @@ describe MergeRequest, models: true do
status: status)
end
- let(:project) { create(:project, :public, :repository, only_allow_merge_if_build_succeeds: true) }
+ let(:project) { create(:project, :public, :repository, only_allow_merge_if_pipeline_succeeds: true) }
let(:developer) { create(:user) }
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index ae203fada12..eb992e1354e 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1112,16 +1112,16 @@ describe Repository, models: true do
let(:update_image_commit) { repository.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
context 'when there is a conflict' do
- it 'aborts the operation' do
- expect(repository.revert(user, new_image_commit, 'master')).to eq(false)
+ it 'raises an error' do
+ expect { repository.revert(user, new_image_commit, 'master') }.to raise_error(/Failed to/)
end
end
context 'when commit was already reverted' do
- it 'aborts the operation' do
+ it 'raises an error' do
repository.revert(user, update_image_commit, 'master')
- expect(repository.revert(user, update_image_commit, 'master')).to eq(false)
+ expect { repository.revert(user, update_image_commit, 'master') }.to raise_error(/Failed to/)
end
end
@@ -1148,16 +1148,16 @@ describe Repository, models: true do
let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') }
context 'when there is a conflict' do
- it 'aborts the operation' do
- expect(repository.cherry_pick(user, conflict_commit, 'master')).to eq(false)
+ it 'raises an error' do
+ expect { repository.cherry_pick(user, conflict_commit, 'master') }.to raise_error(/Failed to/)
end
end
context 'when commit was already cherry-picked' do
- it 'aborts the operation' do
+ it 'raises an error' do
repository.cherry_pick(user, pickable_commit, 'master')
- expect(repository.cherry_pick(user, pickable_commit, 'master')).to eq(false)
+ expect { repository.cherry_pick(user, pickable_commit, 'master') }.to raise_error(/Failed to/)
end
end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 81a8856b8f1..d8b3cc041a5 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -151,26 +151,62 @@ describe API::CommitStatuses, api: true do
end
context 'with all optional parameters' do
- before do
- optional_params = { state: 'success',
- context: 'coverage',
- ref: 'develop',
- description: 'test',
- coverage: 80.0,
- target_url: 'http://gitlab.com/status' }
-
- post api(post_url, developer), optional_params
+ context 'when creating a commit status' do
+ it 'creates commit status' do
+ post api(post_url, developer), {
+ state: 'success',
+ context: 'coverage',
+ ref: 'develop',
+ description: 'test',
+ coverage: 80.0,
+ target_url: 'http://gitlab.com/status'
+ }
+
+ expect(response).to have_http_status(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('coverage')
+ expect(json_response['ref']).to eq('develop')
+ expect(json_response['coverage']).to eq(80.0)
+ expect(json_response['description']).to eq('test')
+ expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ end
end
- it 'creates commit status' do
- expect(response).to have_http_status(201)
- expect(json_response['sha']).to eq(commit.id)
- expect(json_response['status']).to eq('success')
- expect(json_response['name']).to eq('coverage')
- expect(json_response['ref']).to eq('develop')
- expect(json_response['coverage']).to eq(80.0)
- expect(json_response['description']).to eq('test')
- expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ context 'when updatig a commit status' do
+ before do
+ post api(post_url, developer), {
+ state: 'running',
+ context: 'coverage',
+ ref: 'develop',
+ description: 'coverage test',
+ coverage: 0.0,
+ target_url: 'http://gitlab.com/status'
+ }
+
+ post api(post_url, developer), {
+ state: 'success',
+ name: 'coverage',
+ ref: 'develop',
+ description: 'new description',
+ coverage: 90.0
+ }
+ end
+
+ it 'updates a commit status' do
+ expect(response).to have_http_status(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('coverage')
+ expect(json_response['ref']).to eq('develop')
+ expect(json_response['coverage']).to eq(90.0)
+ expect(json_response['description']).to eq('new description')
+ expect(json_response['target_url']).to eq('http://gitlab.com/status')
+ end
+
+ it 'does not create a new commit status' do
+ expect(CommitStatus.count).to eq 1
+ end
end
end
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index d66eb63fd0a..f2fd1dfc8db 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -24,6 +24,7 @@ describe API::Environments, api: true do
expect(json_response.first['name']).to eq(environment.name)
expect(json_response.first['external_url']).to eq(environment.external_url)
expect(json_response.first['project']['id']).to eq(project.id)
+ expect(json_response.first['project']['visibility']).to be_present
end
end
@@ -141,4 +142,39 @@ describe API::Environments, api: true do
end
end
end
+
+ describe 'POST /projects/:id/environments/:environment_id/stop' do
+ context 'as a master' do
+ context 'with a stoppable environment' do
+ before do
+ environment.update(state: :available)
+
+ post api("/projects/#{project.id}/environments/#{environment.id}/stop", user)
+ end
+
+ it 'returns a 200' do
+ expect(response).to have_http_status(200)
+ end
+
+ it 'actually stops the environment' do
+ expect(environment.reload).to be_stopped
+ end
+ end
+
+ it 'returns a 404 for non existing id' do
+ post api("/projects/#{project.id}/environments/12345/stop", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Not found')
+ end
+ end
+
+ context 'a non member' do
+ it 'rejects the request' do
+ post api("/projects/#{project.id}/environments/#{environment.id}/stop", non_member)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 31b1aca6d73..91f8a35e045 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -147,6 +147,20 @@ describe API::Files, api: true do
expect(last_commit.author_name).to eq(author_name)
end
end
+
+ context 'when the repo is empty' do
+ let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
+
+ it "creates a new file in project repo" do
+ post 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
+ end
end
describe "PUT /projects/:id/repository/files" do
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index b0ba3ea912d..2b8fd7e31a1 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -176,7 +176,7 @@ describe API::Groups, api: true do
expect(json_response['name']).to eq(group1.name)
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
- expect(json_response['visibility_level']).to eq(group1.visibility_level)
+ expect(json_response['visibility']).to eq(Gitlab::VisibilityLevel.string_level(group1.visibility_level))
expect(json_response['avatar_url']).to eq(group1.avatar_url)
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
@@ -295,7 +295,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
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
+ expect(json_response.first['visibility']).to be_present
end
it "returns the group's projects with simple representation" do
@@ -306,7 +306,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
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
+ expect(json_response.first['visibility']).not_to be_present
end
it 'filters the groups projects' do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index ffeacb15f17..f18b8e98707 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -409,6 +409,34 @@ describe API::Internal, api: true do
end
end
+ describe 'POST /notify_post_receive' do
+ let(:valid_params) do
+ { repo_path: project.repository.path, secret_token: secret_token }
+ end
+
+ before do
+ allow(Gitlab.config.gitaly).to receive(:socket_path).and_return('path/to/gitaly.socket')
+ end
+
+ it "calls the Gitaly client if it's enabled" do
+ expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ to receive(:post_receive).with(project.repository.path)
+
+ post api("/internal/notify_post_receive"), valid_params
+
+ expect(response).to have_http_status(200)
+ end
+
+ it "returns 500 if the gitaly call fails" do
+ expect_any_instance_of(Gitlab::GitalyClient::Notifications).
+ to receive(:post_receive).with(project.repository.path).and_raise(GRPC::Unavailable)
+
+ post api("/internal/notify_post_receive"), valid_params
+
+ expect(response).to have_http_status(500)
+ end
+ end
+
def project_with_repo_path(path)
double().tap do |fake_project|
allow(fake_project).to receive_message_chain('repository.path_to_repo' => path)
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index ddc2e51821e..710e4320fd1 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -212,6 +212,25 @@ describe API::Issues, api: true do
expect(json_response.first['id']).to eq(confidential_issue.id)
end
+ it 'returns an array of issues found by iids' do
+ get api('/issues', user), iids: [closed_issue.iid]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(closed_issue.id)
+ end
+
+ it 'returns an empty array if iid does not exist' do
+ get api("/issues", user), iids: [99999]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
it 'sorts by created_at descending by default' do
get api('/issues', user)
@@ -377,6 +396,25 @@ describe API::Issues, api: true do
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, group_label.title])
end
+ it 'returns an array of issues found by iids' do
+ get api(base_url, user), iids: [group_issue.iid]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(group_issue.id)
+ end
+
+ it 'returns an empty array if iid does not exist' do
+ get api(base_url, user), iids: [99999]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
it 'returns an empty array if no group issue matches labels' do
get api("#{base_url}?labels=foo,bar", user)
@@ -586,6 +624,25 @@ describe API::Issues, api: true do
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title])
end
+ it 'returns an array of issues found by iids' do
+ get api("#{base_url}/issues", user), iids: [issue.iid]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(issue.id)
+ end
+
+ it 'returns an empty array if iid does not exist' do
+ get api("#{base_url}/issues", user), iids: [99999]
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
it 'returns an empty array if not all labels matches' do
get api("#{base_url}/issues?labels=#{label.title},foo", user)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 5522154899c..b3f0876c822 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -170,7 +170,7 @@ describe API::MergeRequests, api: true do
expect(json_response['source_project_id']).to eq(merge_request.source_project.id)
expect(json_response['target_project_id']).to eq(merge_request.target_project.id)
expect(json_response['work_in_progress']).to be_falsy
- expect(json_response['merge_when_build_succeeds']).to be_falsy
+ expect(json_response['merge_when_pipeline_succeeds']).to be_falsy
expect(json_response['merge_status']).to eq('can_be_merged')
expect(json_response['should_close_merge_request']).to be_falsy
expect(json_response['force_close_merge_request']).to be_falsy
@@ -483,11 +483,11 @@ describe API::MergeRequests, api: true do
allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(pipeline)
allow(pipeline).to receive(:active?).and_return(true)
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_pipeline_succeeds: true
expect(response).to have_http_status(200)
expect(json_response['title']).to eq('Test')
- expect(json_response['merge_when_build_succeeds']).to eq(true)
+ expect(json_response['merge_when_pipeline_succeeds']).to eq(true)
end
end
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index 418bf5a507c..78c230117b8 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -4,8 +4,8 @@ describe API::Milestones, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:empty_project, namespace: user.namespace ) }
- let!(:closed_milestone) { create(:closed_milestone, project: project) }
- let!(:milestone) { create(:milestone, project: project) }
+ let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
+ let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
before { project.team << [user, :developer] }
@@ -45,8 +45,37 @@ describe API::Milestones, api: true do
expect(json_response.first['id']).to eq(closed_milestone.id)
end
- it 'returns a project milestone by iid' do
- get api("/projects/#{project.id}/milestones?iid=#{closed_milestone.iid}", user)
+ it 'returns an array of milestones specified by iids' do
+ other_milestone = create(:milestone, project: project)
+
+ get api("/projects/#{project.id}/milestones", user), iids: [closed_milestone.iid, other_milestone.iid]
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ expect(json_response.map{ |m| m['id'] }).to match_array([closed_milestone.id, other_milestone.id])
+ end
+
+ it 'does not return any milestone if none found' do
+ get api("/projects/#{project.id}/milestones", user), iids: [Milestone.maximum(:iid).succ]
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+ end
+
+ describe 'GET /projects/:id/milestones/:milestone_id' do
+ it 'returns a project milestone by id' do
+ get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['title']).to eq(milestone.title)
+ expect(json_response['iid']).to eq(milestone.iid)
+ end
+
+ it 'returns a project milestone by iids array' do
+ get api("/projects/#{project.id}/milestones?iids=#{closed_milestone.iid}", user)
expect(response.status).to eq 200
expect(response).to include_pagination_headers
@@ -56,21 +85,22 @@ describe API::Milestones, api: true do
expect(json_response.first['id']).to eq closed_milestone.id
end
- it 'returns a project milestone by iid array' do
- get api("/projects/#{project.id}/milestones", user), iid: [milestone.iid, closed_milestone.iid]
+ it 'returns a project milestone by searching for title' do
+ get api("/projects/#{project.id}/milestones", user), search: 'version2'
expect(response).to have_http_status(200)
- expect(json_response.size).to eq(2)
+ expect(response).to include_pagination_headers
+ expect(json_response.size).to eq(1)
expect(json_response.first['title']).to eq milestone.title
expect(json_response.first['id']).to eq milestone.id
end
- it 'returns a project milestone by iid array' do
- get api("/projects/#{project.id}/milestones", user), iid: [milestone.iid, closed_milestone.iid]
+ it 'returns a project milestones by searching for description' do
+ get api("/projects/#{project.id}/milestones", user), search: 'open'
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
- expect(json_response.size).to eq(2)
+ expect(json_response.size).to eq(1)
expect(json_response.first['title']).to eq milestone.title
expect(json_response.first['id']).to eq milestone.id
end
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 9d3c821b692..347f8f6fa3b 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -225,11 +225,11 @@ describe API::Notes, api: true do
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
+ it 'creates a new issue note' do
post 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
+ expect(json_response['body']).to eq(':+1:')
end
end
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 2c4602faf2c..9e88c19b0bc 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -44,7 +44,7 @@ describe API::ProjectSnippets, api: true do
title: 'Test Title',
file_name: 'test.rb',
code: 'puts "hello world"',
- visibility_level: Snippet::PUBLIC
+ visibility: 'public'
}
end
@@ -56,7 +56,7 @@ describe API::ProjectSnippets, api: true do
expect(snippet.content).to eq(params[:code])
expect(snippet.title).to eq(params[:title])
expect(snippet.file_name).to eq(params[:file_name])
- expect(snippet.visibility_level).to eq(params[:visibility_level])
+ expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
end
it 'returns 400 for missing parameters' do
@@ -80,14 +80,14 @@ describe API::ProjectSnippets, api: true do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
+ expect { create_snippet(project, visibility: '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) }.
+ it 'rejects the snippet' do
+ expect { create_snippet(project, visibility: 'public') }.
not_to change { Snippet.count }
expect(response).to have_http_status(400)
@@ -95,7 +95,7 @@ describe API::ProjectSnippets, api: true do
end
it 'creates a spam log' do
- expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
+ expect { create_snippet(project, visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
@@ -165,7 +165,7 @@ describe API::ProjectSnippets, api: true do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
@@ -173,7 +173,7 @@ describe API::ProjectSnippets, api: true do
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 3a00d974633..03cae074803 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -43,9 +43,10 @@ describe API::Projects, api: true do
describe 'GET /projects' do
shared_examples_for 'projects response' do
it 'returns an array of projects' do
- get api('/projects', current_user)
+ get api('/projects', current_user), filter
expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |p| p['id'] }).to contain_exactly(*projects.map(&:id))
end
@@ -61,6 +62,7 @@ describe API::Projects, api: true do
context 'when unauthenticated' do
it_behaves_like 'projects response' do
+ let(:filter) { {} }
let(:current_user) { nil }
let(:projects) { [public_project] }
end
@@ -68,6 +70,7 @@ describe API::Projects, api: true do
context 'when authenticated as regular user' do
it_behaves_like 'projects response' do
+ let(:filter) { {} }
let(:current_user) { user }
let(:projects) { [public_project, project, project2, project3] }
end
@@ -133,13 +136,18 @@ describe API::Projects, api: true do
end
context 'and using search' do
- it 'returns searched project' do
- get api('/projects', user), { search: project.name }
+ it_behaves_like 'projects response' do
+ let(:filter) { { search: project.name } }
+ let(:current_user) { user }
+ let(:projects) { [project] }
+ end
+ end
- expect(response).to have_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(1)
+ context 'and membership=true' do
+ it_behaves_like 'projects response' do
+ let(:filter) { { membership: true } }
+ let(:current_user) { user }
+ let(:projects) { [project, project2, project3] }
end
end
@@ -216,36 +224,52 @@ describe API::Projects, api: true do
end
context 'and with all query parameters' do
- # | | project5 | project6 | project7 | project8 | project9 |
- # |---------+----------+----------+----------+----------+----------|
- # | search | x | | x | x | x |
- # | starred | x | x | | x | x |
- # | public | x | x | x | | x |
- # | owned | x | x | x | x | |
- let!(:project5) { create(:empty_project, :public, path: 'gitlab5', namespace: user.namespace) }
+ let!(:project5) { create(:empty_project, :public, path: 'gitlab5', namespace: create(:namespace)) }
let!(:project6) { create(:empty_project, :public, path: 'project6', namespace: user.namespace) }
let!(:project7) { create(:empty_project, :public, path: 'gitlab7', namespace: user.namespace) }
let!(:project8) { create(:empty_project, path: 'gitlab8', namespace: user.namespace) }
let!(:project9) { create(:empty_project, :public, path: 'gitlab9') }
before do
- user.update_attributes(starred_projects: [project5, project6, project8, project9])
+ user.update_attributes(starred_projects: [project5, project7, project8, project9])
end
- it 'returns only projects that satify all query parameters' do
- get api('/projects', user), { visibility: 'public', owned: true, starred: true, search: 'gitlab' }
+ context 'including owned filter' do
+ it 'returns only projects that satisfy all query parameters' do
+ get api('/projects', user), { visibility: 'public', owned: true, starred: true, search: 'gitlab' }
- 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(1)
- expect(json_response.first['id']).to eq(project5.id)
+ 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(1)
+ expect(json_response.first['id']).to eq(project7.id)
+ end
+ end
+
+ context 'including membership filter' do
+ before do
+ create(:project_member,
+ user: user,
+ project: project5,
+ access_level: ProjectMember::MASTER)
+ end
+
+ it 'returns only projects that satisfy all query parameters' do
+ get api('/projects', user), { visibility: 'public', membership: true, starred: true, search: 'gitlab' }
+
+ 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(2)
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(project5.id, project7.id)
+ end
end
end
end
context 'when authenticated as a different user' do
it_behaves_like 'projects response' do
+ let(:filter) { {} }
let(:current_user) { user2 }
let(:projects) { [public_project] }
end
@@ -253,6 +277,7 @@ describe API::Projects, api: true do
context 'when authenticated as admin' do
it_behaves_like 'projects response' do
+ let(:filter) { {} }
let(:current_user) { admin }
let(:projects) { Project.all }
end
@@ -320,7 +345,7 @@ describe API::Projects, api: true do
issues_enabled: false,
merge_requests_enabled: false,
wiki_enabled: false,
- only_allow_merge_if_build_succeeds: false,
+ only_allow_merge_if_pipeline_succeeds: false,
request_access_enabled: true,
only_allow_merge_if_all_discussions_are_resolved: false
})
@@ -340,36 +365,39 @@ describe API::Projects, api: true do
end
it 'sets a project as public' do
- project = attributes_for(:project, :public)
+ project = attributes_for(:project, visibility: 'public')
+
post api('/projects', user), project
- expect(json_response['public']).to be_truthy
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+
+ expect(json_response['visibility']).to eq('public')
end
it 'sets a project as internal' do
- project = attributes_for(:project, :internal)
+ project = attributes_for(:project, visibility: 'internal')
+
post api('/projects', user), project
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+
+ expect(json_response['visibility']).to eq('internal')
end
it 'sets a project as private' do
- project = attributes_for(:project, :private)
+ project = attributes_for(:project, visibility: 'private')
+
post api('/projects', user), project
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+
+ expect(json_response['visibility']).to eq('private')
end
it 'sets a project as allowing merge even if build fails' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
post api('/projects', user), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: true })
post api('/projects', user), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
end
it 'sets a project as allowing merge even if discussions are unresolved' do
@@ -397,7 +425,7 @@ describe API::Projects, api: true do
end
context 'when a visibility level is restricted' do
- let(:project_param) { attributes_for(:project, :public) }
+ let(:project_param) { attributes_for(:project, visibility: 'public') }
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
@@ -415,10 +443,7 @@ describe API::Projects, api: true do
it 'allows an admin to override restricted visibility settings' do
post api('/projects', admin), project_param
- expect(json_response['public']).to be_truthy
- expect(json_response['visibility_level']).to(
- eq(Gitlab::VisibilityLevel::PUBLIC)
- )
+ expect(json_response['visibility']).to eq('public')
end
end
end
@@ -459,40 +484,41 @@ describe API::Projects, api: true do
end
it 'sets a project as public' do
- project = attributes_for(:project, :public)
+ project = attributes_for(:project, visibility: 'public')
+
post api("/projects/user/#{user.id}", admin), project
expect(response).to have_http_status(201)
- expect(json_response['public']).to be_truthy
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ expect(json_response['visibility']).to eq('public')
end
it 'sets a project as internal' do
- project = attributes_for(:project, :internal)
+ project = attributes_for(:project, visibility: 'internal')
+
post api("/projects/user/#{user.id}", admin), project
expect(response).to have_http_status(201)
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ expect(json_response['visibility']).to eq('internal')
end
it 'sets a project as private' do
- project = attributes_for(:project, :private)
+ project = attributes_for(:project, visibility: 'private')
+
post api("/projects/user/#{user.id}", admin), project
- expect(json_response['public']).to be_falsey
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+
+ expect(json_response['visibility']).to eq('private')
end
it 'sets a project as allowing merge even if build fails' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: false })
post api("/projects/user/#{user.id}", admin), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
- project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
+ project = attributes_for(:project, { only_allow_merge_if_pipeline_succeeds: true })
post api("/projects/user/#{user.id}", admin), project
- expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to be_truthy
end
it 'sets a project as allowing merge even if discussions are unresolved' do
@@ -556,9 +582,8 @@ describe API::Projects, api: true do
expect(json_response['description']).to eq(project.description)
expect(json_response['default_branch']).to eq(project.default_branch)
expect(json_response['tag_list']).to be_an Array
- expect(json_response['public']).to be_falsey
expect(json_response['archived']).to be_falsey
- expect(json_response['visibility_level']).to be_present
+ expect(json_response['visibility']).to be_present
expect(json_response['ssh_url_to_repo']).to be_present
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
@@ -586,7 +611,7 @@ describe API::Projects, api: true do
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
- expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
+ expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
end
@@ -812,8 +837,7 @@ describe API::Projects, api: true do
describe 'POST /projects/:id/snippets' do
it 'creates a new project snippet' do
post api("/projects/#{project.id}/snippets", user),
- title: 'api test', file_name: 'sample.rb', code: 'test',
- visibility_level: Gitlab::VisibilityLevel::PRIVATE
+ title: 'api test', file_name: 'sample.rb', code: 'test', visibility: 'private'
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('api test')
end
@@ -1065,7 +1089,7 @@ describe API::Projects, api: true do
end
it 'updates visibility_level' do
- project_param = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
+ project_param = { visibility: 'public' }
put api("/projects/#{project3.id}", user), project_param
expect(response).to have_http_status(200)
project_param.each_pair do |k, v|
@@ -1075,13 +1099,13 @@ describe API::Projects, api: true do
it 'updates visibility_level from public to private' do
project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
- project_param = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
+ project_param = { visibility: 'private' }
put api("/projects/#{project3.id}", user), project_param
expect(response).to have_http_status(200)
project_param.each_pair do |k, v|
expect(json_response[k.to_s]).to eq(v)
end
- expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ expect(json_response['visibility']).to eq('private')
end
it 'does not update name to existing name' do
@@ -1148,7 +1172,7 @@ describe API::Projects, api: true do
end
it 'does not update visibility_level' do
- project_param = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
+ project_param = { visibility: 'public' }
put api("/projects/#{project3.id}", user4), project_param
expect(response).to have_http_status(403)
end
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 411905edb49..11b4b718e2c 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -18,6 +18,9 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['koding_url']).to be_nil
expect(json_response['plantuml_enabled']).to be_falsey
expect(json_response['plantuml_url']).to be_nil
+ expect(json_response['default_project_visibility']).to be_a String
+ expect(json_response['default_snippet_visibility']).to be_a String
+ expect(json_response['default_group_visibility']).to be_a String
end
end
@@ -37,6 +40,8 @@ describe API::Settings, 'Settings', api: true do
koding_url: 'http://koding.example.com',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
+ default_snippet_visibility: 'internal',
+ restricted_visibility_levels: ['public'],
default_artifacts_expire_in: '2 days'
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
@@ -47,6 +52,8 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['koding_url']).to eq('http://koding.example.com')
expect(json_response['plantuml_enabled']).to be_truthy
expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
+ expect(json_response['default_snippet_visibility']).to eq('internal')
+ expect(json_response['restricted_visibility_levels']).to eq(['public'])
expect(json_response['default_artifacts_expire_in']).to eq('2 days')
end
end
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 5219f6eed42..5d75b47b3cd 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -87,7 +87,7 @@ describe API::Snippets, api: true do
title: 'Test Title',
file_name: 'test.rb',
content: 'puts "hello world"',
- visibility_level: Snippet::PUBLIC
+ visibility: 'public'
}
end
@@ -120,14 +120,14 @@ describe API::Snippets, api: true do
context 'when the snippet is private' do
it 'creates the snippet' do
- expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
+ expect { create_snippet(visibility: 'private') }.
to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
+ expect { create_snippet(visibility: 'public') }.
not_to change { Snippet.count }
expect(response).to have_http_status(400)
@@ -135,7 +135,7 @@ describe API::Snippets, api: true do
end
it 'creates a spam log' do
- expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
+ expect { create_snippet(visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
@@ -218,12 +218,12 @@ describe API::Snippets, api: true do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
not_to change { snippet.reload.title }
end
it 'creates a spam log' do
- expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
+ expect { update_snippet(title: 'Foo', visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index f35e963a14b..1e401935662 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Todos, api: true do
include ApiHelpers
- let(:project_1) { create(:empty_project) }
+ let(:project_1) { create(:empty_project, :test_repo) }
let(:project_2) { create(:empty_project) }
let(:author_1) { create(:user) }
let(:author_2) { create(:user) }
@@ -11,7 +11,7 @@ describe API::Todos, api: true do
let(:merge_request) { create(:merge_request, source_project: project_1) }
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!(:pending_3) { create(:on_commit_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
diff --git a/spec/requests/api/v3/environments_spec.rb b/spec/requests/api/v3/environments_spec.rb
index 1ac666ab240..216192c9d34 100644
--- a/spec/requests/api/v3/environments_spec.rb
+++ b/spec/requests/api/v3/environments_spec.rb
@@ -12,6 +12,132 @@ describe API::V3::Environments, api: true do
project.team << [user, :master]
end
+ shared_examples 'a paginated resources' do
+ before do
+ # Fires the request
+ request
+ end
+
+ it 'has pagination headers' do
+ expect(response.headers).to include('X-Total')
+ expect(response.headers).to include('X-Total-Pages')
+ expect(response.headers).to include('X-Per-Page')
+ expect(response.headers).to include('X-Page')
+ expect(response.headers).to include('X-Next-Page')
+ expect(response.headers).to include('X-Prev-Page')
+ expect(response.headers).to include('Link')
+ end
+ end
+
+ describe 'GET /projects/:id/environments' do
+ context 'as member of the project' do
+ it_behaves_like 'a paginated resources' do
+ let(:request) { get v3_api("/projects/#{project.id}/environments", user) }
+ end
+
+ it 'returns project environments' do
+ get v3_api("/projects/#{project.id}/environments", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['name']).to eq(environment.name)
+ expect(json_response.first['external_url']).to eq(environment.external_url)
+ expect(json_response.first['project']['id']).to eq(project.id)
+ expect(json_response.first['project']['visibility_level']).to be_present
+ end
+ end
+
+ context 'as non member' do
+ it 'returns a 404 status code' do
+ get v3_api("/projects/#{project.id}/environments", non_member)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/environments' do
+ context 'as a member' do
+ it 'creates a environment with valid params' do
+ post v3_api("/projects/#{project.id}/environments", user), name: "mepmep"
+
+ expect(response).to have_http_status(201)
+ expect(json_response['name']).to eq('mepmep')
+ expect(json_response['slug']).to eq('mepmep')
+ expect(json_response['external']).to be nil
+ end
+
+ it 'requires name to be passed' do
+ post v3_api("/projects/#{project.id}/environments", user), external_url: 'test.gitlab.com'
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 400 if environment already exists' do
+ post v3_api("/projects/#{project.id}/environments", user), name: environment.name
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 400 if slug is specified' do
+ post v3_api("/projects/#{project.id}/environments", user), name: "foo", slug: "foo"
+
+ expect(response).to have_http_status(400)
+ expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
+ end
+ end
+
+ context 'a non member' do
+ it 'rejects the request' do
+ post v3_api("/projects/#{project.id}/environments", non_member), name: 'gitlab.com'
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'returns a 400 when the required params are missing' do
+ post v3_api("/projects/12345/environments", non_member), external_url: 'http://env.git.com'
+ end
+ end
+ end
+
+ describe 'PUT /projects/:id/environments/:environment_id' do
+ it 'returns a 200 if name and external_url are changed' do
+ url = 'https://mepmep.whatever.ninja'
+ put v3_api("/projects/#{project.id}/environments/#{environment.id}", user),
+ name: 'Mepmep', external_url: url
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq('Mepmep')
+ expect(json_response['external_url']).to eq(url)
+ end
+
+ it "won't allow slug to be changed" do
+ slug = environment.slug
+ api_url = v3_api("/projects/#{project.id}/environments/#{environment.id}", user)
+ put api_url, slug: slug + "-foo"
+
+ expect(response).to have_http_status(400)
+ expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
+ end
+
+ it "won't update the external_url if only the name is passed" do
+ url = environment.external_url
+ put v3_api("/projects/#{project.id}/environments/#{environment.id}", user),
+ name: 'Mepmep'
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq('Mepmep')
+ expect(json_response['external_url']).to eq(url)
+ end
+
+ it 'returns a 404 if the environment does not exist' do
+ put v3_api("/projects/#{project.id}/environments/12345", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
describe 'DELETE /projects/:id/environments/:environment_id' do
context 'as a master' do
it 'returns a 200 for an existing environment' do
diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb
index 93637053626..3b61139a2cd 100644
--- a/spec/requests/api/v3/files_spec.rb
+++ b/spec/requests/api/v3/files_spec.rb
@@ -148,6 +148,20 @@ describe API::V3::Files, api: true do
expect(last_commit.author_name).to eq(author_name)
end
end
+
+ context 'when the repo is empty' do
+ let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
+
+ 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
+ end
end
describe "PUT /projects/:id/repository/files" do
diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb
index 8b29ad03737..a71b7d4b008 100644
--- a/spec/requests/api/v3/groups_spec.rb
+++ b/spec/requests/api/v3/groups_spec.rb
@@ -4,14 +4,144 @@ describe API::V3::Groups, api: true do
include ApiHelpers
include UploadHelpers
+ let(:user1) { create(:user, can_create_group: false) }
let(:user2) { create(:user) }
+ let(:user3) { create(:user) }
+ let(:admin) { create(:admin) }
+ let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
+ let!(:project1) { create(:empty_project, namespace: group1) }
let!(:project2) { create(:empty_project, namespace: group2) }
+ let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
+ group1.add_owner(user1)
group2.add_owner(user2)
end
+ describe "GET /groups" do
+ context "when unauthenticated" do
+ it "returns authentication error" do
+ get v3_api("/groups")
+
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context "when authenticated as user" do
+ it "normal user: returns an array of groups of user1" do
+ get v3_api("/groups", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response)
+ .to satisfy_one { |group| group['name'] == group1.name }
+ end
+
+ it "does not include statistics" do
+ get v3_api("/groups", user1), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include 'statistics'
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "admin: returns an array of all groups" do
+ get v3_api("/groups", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ end
+
+ it "does not include statistics by default" do
+ get v3_api("/groups", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it "includes statistics if requested" do
+ attributes = {
+ storage_size: 702,
+ repository_size: 123,
+ lfs_objects_size: 234,
+ build_artifacts_size: 345,
+ }.stringify_keys
+
+ project1.statistics.update!(attributes)
+
+ get v3_api("/groups", admin), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response)
+ .to satisfy_one { |group| group['statistics'] == attributes }
+ end
+ end
+
+ context "when using skip_groups in request" do
+ it "returns all groups excluding skipped groups" do
+ get v3_api("/groups", admin), skip_groups: [group2.id]
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ end
+ end
+
+ context "when using all_available in request" do
+ let(:response_groups) { json_response.map { |group| group['name'] } }
+
+ it "returns all groups you have access to" do
+ public_group = create :group, :public
+
+ get v3_api("/groups", user1), all_available: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to contain_exactly(public_group.name, group1.name)
+ end
+ end
+
+ context "when using sorting" do
+ let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") }
+ let(:response_groups) { json_response.map { |group| group['name'] } }
+
+ before do
+ group3.add_owner(user1)
+ end
+
+ it "sorts by name ascending by default" do
+ get v3_api("/groups", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group3.name, group1.name])
+ end
+
+ it "sorts in descending order when passed" do
+ get v3_api("/groups", user1), sort: "desc"
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group1.name, group3.name])
+ end
+
+ it "sorts by the order_by param" do
+ get v3_api("/groups", user1), order_by: "path"
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(response_groups).to eq([group1.name, group3.name])
+ end
+ end
+ end
+
describe 'GET /groups/owned' do
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -32,4 +162,404 @@ describe API::V3::Groups, api: true do
end
end
end
+
+ describe "GET /groups/:id" do
+ context "when authenticated as user" do
+ it "returns one of user1's groups" do
+ project = create(:empty_project, namespace: group2, path: 'Foo')
+ create(:project_group_link, project: project, group: group1)
+
+ get v3_api("/groups/#{group1.id}", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['id']).to eq(group1.id)
+ expect(json_response['name']).to eq(group1.name)
+ expect(json_response['path']).to eq(group1.path)
+ expect(json_response['description']).to eq(group1.description)
+ expect(json_response['visibility_level']).to eq(group1.visibility_level)
+ expect(json_response['avatar_url']).to eq(group1.avatar_url)
+ expect(json_response['web_url']).to eq(group1.web_url)
+ expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
+ expect(json_response['full_name']).to eq(group1.full_name)
+ expect(json_response['full_path']).to eq(group1.full_path)
+ expect(json_response['parent_id']).to eq(group1.parent_id)
+ expect(json_response['projects']).to be_an Array
+ expect(json_response['projects'].length).to eq(2)
+ expect(json_response['shared_projects']).to be_an Array
+ expect(json_response['shared_projects'].length).to eq(1)
+ expect(json_response['shared_projects'][0]['id']).to eq(project.id)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "does not return a group not attached to user1" do
+ get v3_api("/groups/#{group2.id}", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "returns any existing group" do
+ get v3_api("/groups/#{group2.id}", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(group2.name)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when using group path in URL' do
+ it 'returns any existing group' do
+ get v3_api("/groups/#{group1.path}", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(group1.name)
+ end
+
+ it 'does not return a non existing group' do
+ get v3_api('/groups/unknown', admin)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'does not return a group not attached to user1' do
+ get v3_api("/groups/#{group2.path}", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe 'PUT /groups/:id' do
+ let(:new_group_name) { 'New Group'}
+
+ context 'when authenticated as the group owner' do
+ it 'updates the group' do
+ put v3_api("/groups/#{group1.id}", user1), name: new_group_name, request_access_enabled: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(new_group_name)
+ expect(json_response['request_access_enabled']).to eq(true)
+ end
+
+ it 'returns 404 for a non existing group' do
+ put v3_api('/groups/1328', user1), name: new_group_name
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when authenticated as the admin' do
+ it 'updates the group' do
+ put v3_api("/groups/#{group1.id}", admin), name: new_group_name
+
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(new_group_name)
+ end
+ end
+
+ context 'when authenticated as an user that can see the group' do
+ it 'does not updates the group' do
+ put v3_api("/groups/#{group1.id}", user2), name: new_group_name
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'when authenticated as an user that cannot see the group' do
+ it 'returns 404 when trying to update the group' do
+ put v3_api("/groups/#{group2.id}", user1), name: new_group_name
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe "GET /groups/:id/projects" do
+ context "when authenticated as user" do
+ it "returns the group's projects" do
+ get v3_api("/groups/#{group1.id}/projects", user1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(2)
+ 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
+
+ it "returns the group's projects with simple representation" do
+ get v3_api("/groups/#{group1.id}/projects", user1), simple: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(2)
+ 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
+
+ it 'filters the groups projects' do
+ public_project = create(:empty_project, :public, path: 'test1', group: group1)
+
+ get v3_api("/groups/#{group1.id}/projects", user1), visibility: 'public'
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an(Array)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(public_project.name)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328/projects", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "does not return a group not attached to user1" do
+ get v3_api("/groups/#{group2.id}/projects", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "only returns projects to which user has access" do
+ project3.team << [user3, :developer]
+
+ get v3_api("/groups/#{group1.id}/projects", user3)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project3.name)
+ end
+
+ it 'only returns the projects owned by user' do
+ project2.group.add_owner(user3)
+
+ get v3_api("/groups/#{project2.group.id}/projects", user3), owned: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project2.name)
+ end
+
+ it 'only returns the projects starred by user' do
+ user1.starred_projects = [project1]
+
+ get v3_api("/groups/#{group1.id}/projects", user1), starred: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project1.name)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "returns any existing group" do
+ get v3_api("/groups/#{group2.id}/projects", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['name']).to eq(project2.name)
+ end
+
+ it "does not return a non existing group" do
+ get v3_api("/groups/1328/projects", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when using group path in URL' do
+ it 'returns any existing group' do
+ get v3_api("/groups/#{group1.path}/projects", admin)
+
+ expect(response).to have_http_status(200)
+ project_names = json_response.map { |proj| proj['name'] }
+ expect(project_names).to match_array([project1.name, project3.name])
+ end
+
+ it 'does not return a non existing group' do
+ get v3_api('/groups/unknown/projects', admin)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'does not return a group not attached to user1' do
+ get v3_api("/groups/#{group2.path}/projects", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe "POST /groups" do
+ context "when authenticated as user without group permissions" do
+ it "does not create group" do
+ post v3_api("/groups", user1), attributes_for(:group)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context "when authenticated as user with group permissions" do
+ it "creates group" do
+ group = attributes_for(:group, { request_access_enabled: false })
+
+ post v3_api("/groups", user3), group
+
+ expect(response).to have_http_status(201)
+
+ expect(json_response["name"]).to eq(group[:name])
+ expect(json_response["path"]).to eq(group[:path])
+ expect(json_response["request_access_enabled"]).to eq(group[:request_access_enabled])
+ end
+
+ it "creates a nested group" do
+ parent = create(:group)
+ parent.add_owner(user3)
+ group = attributes_for(:group, { parent_id: parent.id })
+
+ post v3_api("/groups", user3), group
+
+ expect(response).to have_http_status(201)
+
+ expect(json_response["full_path"]).to eq("#{parent.path}/#{group[:path]}")
+ expect(json_response["parent_id"]).to eq(parent.id)
+ end
+
+ it "does not create group, duplicate" do
+ post v3_api("/groups", user3), { name: 'Duplicate Test', path: group2.path }
+
+ expect(response).to have_http_status(400)
+ expect(response.message).to eq("Bad Request")
+ end
+
+ it "returns 400 bad request error if name not given" do
+ post v3_api("/groups", user3), { path: group2.path }
+
+ expect(response).to have_http_status(400)
+ end
+
+ it "returns 400 bad request error if path not given" do
+ post v3_api("/groups", user3), { name: 'test' }
+
+ expect(response).to have_http_status(400)
+ end
+ end
+ end
+
+ describe "DELETE /groups/:id" do
+ context "when authenticated as user" do
+ it "removes group" do
+ delete v3_api("/groups/#{group1.id}", user1)
+
+ expect(response).to have_http_status(200)
+ end
+
+ it "does not remove a group if not an owner" do
+ user4 = create(:user)
+ group1.add_master(user4)
+
+ delete v3_api("/groups/#{group1.id}", user3)
+
+ expect(response).to have_http_status(403)
+ end
+
+ it "does not remove a non existing group" do
+ delete v3_api("/groups/1328", user1)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "does not remove a group not attached to user1" do
+ delete v3_api("/groups/#{group2.id}", user1)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "removes any existing group" do
+ delete v3_api("/groups/#{group2.id}", admin)
+
+ expect(response).to have_http_status(200)
+ end
+
+ it "does not remove a non existing group" do
+ delete v3_api("/groups/1328", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe "POST /groups/:id/projects/:project_id" do
+ let(:project) { create(:empty_project) }
+ let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
+
+ before(:each) do
+ allow_any_instance_of(Projects::TransferService).
+ to receive(:execute).and_return(true)
+ end
+
+ context "when authenticated as user" do
+ it "does not transfer project to group" do
+ post v3_api("/groups/#{group1.id}/projects/#{project.id}", user2)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "transfers project to group" do
+ post v3_api("/groups/#{group1.id}/projects/#{project.id}", admin)
+
+ expect(response).to have_http_status(201)
+ end
+
+ context 'when using project path in URL' do
+ context 'with a valid project path' do
+ it "transfers project to group" do
+ post v3_api("/groups/#{group1.id}/projects/#{project_path}", admin)
+
+ expect(response).to have_http_status(201)
+ end
+ end
+
+ context 'with a non-existent project path' do
+ it "does not transfer project to group" do
+ post v3_api("/groups/#{group1.id}/projects/nogroup%2Fnoproject", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ context 'when using a group path in URL' do
+ context 'with a valid group path' do
+ it "transfers project to group" do
+ post v3_api("/groups/#{group1.path}/projects/#{project_path}", admin)
+
+ expect(response).to have_http_status(201)
+ end
+ end
+
+ context 'with a non-existent group path' do
+ it "does not transfer project to group" do
+ post v3_api("/groups/noexist/projects/#{project_path}", admin)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/v3/milestones_spec.rb b/spec/requests/api/v3/milestones_spec.rb
new file mode 100644
index 00000000000..77705d8c839
--- /dev/null
+++ b/spec/requests/api/v3/milestones_spec.rb
@@ -0,0 +1,232 @@
+require 'spec_helper'
+
+describe API::V3::Milestones, api: true do
+ include ApiHelpers
+ let(:user) { create(:user) }
+ let!(:project) { create(:empty_project, namespace: user.namespace ) }
+ let!(:closed_milestone) { create(:closed_milestone, project: project) }
+ let!(:milestone) { create(:milestone, project: project) }
+
+ before { project.team << [user, :developer] }
+
+ describe 'GET /projects/:id/milestones' do
+ it 'returns project milestones' do
+ get v3_api("/projects/#{project.id}/milestones", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(milestone.title)
+ end
+
+ it 'returns a 401 error if user not authenticated' do
+ get v3_api("/projects/#{project.id}/milestones")
+
+ expect(response).to have_http_status(401)
+ end
+
+ it 'returns an array of active milestones' do
+ get v3_api("/projects/#{project.id}/milestones?state=active", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(milestone.id)
+ end
+
+ it 'returns an array of closed milestones' do
+ get v3_api("/projects/#{project.id}/milestones?state=closed", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(closed_milestone.id)
+ end
+ end
+
+ describe 'GET /projects/:id/milestones/:milestone_id' do
+ it 'returns a project milestone by id' do
+ get v3_api("/projects/#{project.id}/milestones/#{milestone.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['title']).to eq(milestone.title)
+ expect(json_response['iid']).to eq(milestone.iid)
+ end
+
+ it 'returns a project milestone by iid' do
+ get v3_api("/projects/#{project.id}/milestones?iid=#{closed_milestone.iid}", user)
+
+ expect(response.status).to eq 200
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['title']).to eq closed_milestone.title
+ expect(json_response.first['id']).to eq closed_milestone.id
+ end
+
+ it 'returns a project milestone by iid array' do
+ get v3_api("/projects/#{project.id}/milestones", user), iid: [milestone.iid, closed_milestone.iid]
+
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(2)
+ expect(json_response.first['title']).to eq milestone.title
+ expect(json_response.first['id']).to eq milestone.id
+ end
+
+ it 'returns 401 error if user not authenticated' do
+ get v3_api("/projects/#{project.id}/milestones/#{milestone.id}")
+
+ expect(response).to have_http_status(401)
+ end
+
+ it 'returns a 404 error if milestone id not found' do
+ get v3_api("/projects/#{project.id}/milestones/1234", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe 'POST /projects/:id/milestones' do
+ it 'creates a new project milestone' do
+ post v3_api("/projects/#{project.id}/milestones", user), title: 'new milestone'
+
+ expect(response).to have_http_status(201)
+ expect(json_response['title']).to eq('new milestone')
+ expect(json_response['description']).to be_nil
+ end
+
+ it 'creates a new project milestone with description and dates' do
+ post v3_api("/projects/#{project.id}/milestones", user),
+ title: 'new milestone', description: 'release', due_date: '2013-03-02', start_date: '2013-02-02'
+
+ expect(response).to have_http_status(201)
+ expect(json_response['description']).to eq('release')
+ expect(json_response['due_date']).to eq('2013-03-02')
+ expect(json_response['start_date']).to eq('2013-02-02')
+ end
+
+ it 'returns a 400 error if title is missing' do
+ post v3_api("/projects/#{project.id}/milestones", user)
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 400 error if params are invalid (duplicate title)' do
+ post v3_api("/projects/#{project.id}/milestones", user),
+ title: milestone.title, description: 'release', due_date: '2013-03-02'
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'creates a new project with reserved html characters' do
+ post v3_api("/projects/#{project.id}/milestones", user), title: 'foo & bar 1.1 -> 2.2'
+
+ expect(response).to have_http_status(201)
+ expect(json_response['title']).to eq('foo & bar 1.1 -> 2.2')
+ expect(json_response['description']).to be_nil
+ end
+ end
+
+ describe 'PUT /projects/:id/milestones/:milestone_id' do
+ it 'updates a project milestone' do
+ put v3_api("/projects/#{project.id}/milestones/#{milestone.id}", user),
+ title: 'updated title'
+
+ expect(response).to have_http_status(200)
+ expect(json_response['title']).to eq('updated title')
+ end
+
+ it 'removes a due date if nil is passed' do
+ milestone.update!(due_date: "2016-08-05")
+
+ put v3_api("/projects/#{project.id}/milestones/#{milestone.id}", user), due_date: nil
+
+ expect(response).to have_http_status(200)
+ expect(json_response['due_date']).to be_nil
+ end
+
+ it 'returns a 404 error if milestone id not found' do
+ put v3_api("/projects/#{project.id}/milestones/1234", user),
+ title: 'updated title'
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe 'PUT /projects/:id/milestones/:milestone_id to close milestone' do
+ it 'updates a project milestone' do
+ put v3_api("/projects/#{project.id}/milestones/#{milestone.id}", user),
+ state_event: 'close'
+ expect(response).to have_http_status(200)
+
+ expect(json_response['state']).to eq('closed')
+ end
+ end
+
+ describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
+ it 'creates an activity event when an milestone is closed' do
+ expect(Event).to receive(:create)
+
+ put v3_api("/projects/#{project.id}/milestones/#{milestone.id}", user),
+ state_event: 'close'
+ end
+ end
+
+ describe 'GET /projects/:id/milestones/:milestone_id/issues' do
+ before do
+ milestone.issues << create(:issue, project: project)
+ end
+ it 'returns project issues for a particular milestone' do
+ get v3_api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['milestone']['title']).to eq(milestone.title)
+ end
+
+ it 'returns a 401 error if user not authenticated' do
+ get v3_api("/projects/#{project.id}/milestones/#{milestone.id}/issues")
+
+ expect(response).to have_http_status(401)
+ end
+
+ describe 'confidential issues' do
+ let(:public_project) { create(:empty_project, :public) }
+ let(:milestone) { create(:milestone, project: public_project) }
+ let(:issue) { create(:issue, project: public_project) }
+ let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
+
+ before do
+ public_project.team << [user, :developer]
+ milestone.issues << issue << confidential_issue
+ end
+
+ it 'returns confidential issues to team members' do
+ get v3_api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(2)
+ expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id)
+ end
+
+ it 'does not return confidential issues to team members with guest role' do
+ member = create(:user)
+ project.team << [member, :guest]
+
+ get v3_api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
+ end
+
+ it 'does not return confidential issues to regular users' do
+ get v3_api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user))
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+ expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb
index b8f0260c6a2..ddef2d5eb04 100644
--- a/spec/requests/api/v3/notes_spec.rb
+++ b/spec/requests/api/v3/notes_spec.rb
@@ -228,11 +228,11 @@ describe API::V3::Notes, api: true do
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
+ it 'creates a new issue note' 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
+ expect(json_response['body']).to eq(':+1:')
end
end
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index 34940b2f1c7..d8bb562587d 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -427,7 +427,7 @@ describe API::V3::Projects, api: true do
expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
post v3_api('/projects', user), project
expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
@@ -572,7 +572,7 @@ describe API::V3::Projects, api: true do
expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
end
- it 'sets a project as allowing merge only if build succeeds' do
+ it 'sets a project as allowing merge only if merge_when_pipeline_succeeds' do
project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
post v3_api("/projects/user/#{user.id}", admin), project
expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
@@ -669,7 +669,7 @@ describe API::V3::Projects, api: true do
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
- expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
+ expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds)
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
end
diff --git a/spec/requests/api/v3/settings_spec.rb b/spec/requests/api/v3/settings_spec.rb
new file mode 100644
index 00000000000..a9fa5adac17
--- /dev/null
+++ b/spec/requests/api/v3/settings_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe API::V3::Settings, 'Settings', api: true do
+ include ApiHelpers
+
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
+
+ describe "GET /application/settings" do
+ it "returns application settings" do
+ get v3_api("/application/settings", admin)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Hash
+ expect(json_response['default_projects_limit']).to eq(42)
+ expect(json_response['signin_enabled']).to be_truthy
+ expect(json_response['repository_storage']).to eq('default')
+ expect(json_response['koding_enabled']).to be_falsey
+ expect(json_response['koding_url']).to be_nil
+ expect(json_response['plantuml_enabled']).to be_falsey
+ expect(json_response['plantuml_url']).to be_nil
+ end
+ end
+
+ describe "PUT /application/settings" do
+ context "custom repository storage type set in the config" do
+ before do
+ storages = { 'custom' => 'tmp/tests/custom_repositories' }
+ allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
+ end
+
+ it "updates application settings" do
+ put v3_api("/application/settings", admin),
+ default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com',
+ plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com'
+ expect(response).to have_http_status(200)
+ expect(json_response['default_projects_limit']).to eq(3)
+ expect(json_response['signin_enabled']).to be_falsey
+ expect(json_response['repository_storage']).to eq('custom')
+ expect(json_response['repository_storages']).to eq(['custom'])
+ expect(json_response['koding_enabled']).to be_truthy
+ expect(json_response['koding_url']).to eq('http://koding.example.com')
+ expect(json_response['plantuml_enabled']).to be_truthy
+ expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
+ end
+ end
+
+ context "missing koding_url value when koding_enabled is true" do
+ it "returns a blank parameter error message" do
+ put v3_api("/application/settings", admin), koding_enabled: true
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('koding_url is missing')
+ end
+ end
+
+ context "missing plantuml_url value when plantuml_enabled is true" do
+ it "returns a blank parameter error message" do
+ put v3_api("/application/settings", admin), plantuml_enabled: true
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('plantuml_url is missing')
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/v3/snippets_spec.rb b/spec/requests/api/v3/snippets_spec.rb
new file mode 100644
index 00000000000..05653bd0d51
--- /dev/null
+++ b/spec/requests/api/v3/snippets_spec.rb
@@ -0,0 +1,187 @@
+require 'rails_helper'
+
+describe API::V3::Snippets, api: true do
+ include ApiHelpers
+ let!(:user) { create(:user) }
+
+ describe 'GET /snippets/' do
+ it 'returns snippets available' do
+ public_snippet = create(:personal_snippet, :public, author: user)
+ private_snippet = create(:personal_snippet, :private, author: user)
+ internal_snippet = create(:personal_snippet, :internal, author: user)
+
+ get v3_api("/snippets/", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
+ public_snippet.id,
+ internal_snippet.id,
+ private_snippet.id)
+ expect(json_response.last).to have_key('web_url')
+ expect(json_response.last).to have_key('raw_url')
+ end
+
+ it 'hides private snippets from regular user' do
+ create(:personal_snippet, :private)
+
+ get v3_api("/snippets/", user)
+ expect(response).to have_http_status(200)
+ expect(json_response.size).to eq(0)
+ end
+ end
+
+ describe 'GET /snippets/public' do
+ let!(:other_user) { create(:user) }
+ let!(:public_snippet) { create(:personal_snippet, :public, author: user) }
+ let!(:private_snippet) { create(:personal_snippet, :private, author: user) }
+ let!(:internal_snippet) { create(:personal_snippet, :internal, author: user) }
+ let!(:public_snippet_other) { create(:personal_snippet, :public, author: other_user) }
+ let!(:private_snippet_other) { create(:personal_snippet, :private, author: other_user) }
+ let!(:internal_snippet_other) { create(:personal_snippet, :internal, author: other_user) }
+
+ it 'returns all snippets with public visibility from all users' do
+ get v3_api("/snippets/public", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
+ public_snippet.id,
+ public_snippet_other.id)
+ expect(json_response.map{ |snippet| snippet['web_url']} ).to include(
+ "http://localhost/snippets/#{public_snippet.id}",
+ "http://localhost/snippets/#{public_snippet_other.id}")
+ expect(json_response.map{ |snippet| snippet['raw_url']} ).to include(
+ "http://localhost/snippets/#{public_snippet.id}/raw",
+ "http://localhost/snippets/#{public_snippet_other.id}/raw")
+ end
+ end
+
+ describe 'GET /snippets/:id/raw' do
+ let(:snippet) { create(:personal_snippet, author: user) }
+
+ it 'returns raw text' do
+ get v3_api("/snippets/#{snippet.id}/raw", user)
+
+ expect(response).to have_http_status(200)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to eq(snippet.content)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ delete v3_api("/snippets/1234", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+ end
+
+ describe 'POST /snippets/' do
+ let(:params) do
+ {
+ title: 'Test Title',
+ file_name: 'test.rb',
+ content: 'puts "hello world"',
+ visibility_level: Snippet::PUBLIC
+ }
+ end
+
+ it 'creates a new snippet' do
+ expect do
+ post v3_api("/snippets/", user), params
+ end.to change { PersonalSnippet.count }.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['title']).to eq(params[:title])
+ expect(json_response['file_name']).to eq(params[:file_name])
+ end
+
+ it 'returns 400 for missing parameters' do
+ params.delete(:title)
+
+ post v3_api("/snippets/", user), params
+
+ expect(response).to have_http_status(400)
+ end
+
+ context 'when the snippet is spam' do
+ def create_snippet(snippet_params = {})
+ post v3_api('/snippets', user), params.merge(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
+ it 'creates the snippet' do
+ expect { create_snippet(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(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(visibility_level: Snippet::PUBLIC) }.
+ to change { SpamLog.count }.by(1)
+ end
+ end
+ end
+ end
+
+ describe 'PUT /snippets/:id' do
+ let(:other_user) { create(:user) }
+ let(:public_snippet) { create(:personal_snippet, :public, author: user) }
+ it 'updates snippet' do
+ new_content = 'New content'
+
+ put v3_api("/snippets/#{public_snippet.id}", user), content: new_content
+
+ expect(response).to have_http_status(200)
+ public_snippet.reload
+ expect(public_snippet.content).to eq(new_content)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ put v3_api("/snippets/1234", user), title: 'foo'
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+
+ it "returns 404 for another user's snippet" do
+ put v3_api("/snippets/#{public_snippet.id}", other_user), title: 'fubar'
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+
+ it 'returns 400 for missing parameters' do
+ put v3_api("/snippets/1234", user)
+
+ expect(response).to have_http_status(400)
+ end
+ end
+
+ describe 'DELETE /snippets/:id' do
+ let!(:public_snippet) { create(:personal_snippet, :public, author: user) }
+ it 'deletes snippet' do
+ expect do
+ delete v3_api("/snippets/#{public_snippet.id}", user)
+
+ expect(response).to have_http_status(204)
+ end.to change { PersonalSnippet.count }.by(-1)
+ end
+
+ it 'returns 404 for invalid snippet id' do
+ delete v3_api("/snippets/1234", user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
+ end
+end
diff --git a/spec/rubocop/cop/custom_error_class_spec.rb b/spec/rubocop/cop/custom_error_class_spec.rb
new file mode 100644
index 00000000000..381d7871a40
--- /dev/null
+++ b/spec/rubocop/cop/custom_error_class_spec.rb
@@ -0,0 +1,111 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../rubocop/cop/custom_error_class'
+
+describe RuboCop::Cop::CustomErrorClass do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'when a class has a body' do
+ it 'does nothing' do
+ inspect_source(cop, 'class CustomError < StandardError; def foo; end; end')
+
+ expect(cop.offenses).to be_empty
+ end
+ end
+
+ context 'when a class has no explicit superclass' do
+ it 'does nothing' do
+ inspect_source(cop, 'class CustomError; end')
+
+ expect(cop.offenses).to be_empty
+ end
+ end
+
+ context 'when a class has a superclass that does not end in Error' do
+ it 'does nothing' do
+ inspect_source(cop, 'class CustomError < BasicObject; end')
+
+ expect(cop.offenses).to be_empty
+ end
+ end
+
+ context 'when a class is empty and inherits from a class ending in Error' do
+ context 'when the class is on a single line' do
+ let(:source) do
+ <<-SOURCE
+ module Foo
+ class CustomError < Bar::Baz::BaseError; end
+ end
+ SOURCE
+ end
+
+ let(:expected) do
+ <<-EXPECTED
+ module Foo
+ CustomError = Class.new(Bar::Baz::BaseError)
+ end
+ EXPECTED
+ end
+
+ it 'registers an offense' do
+ expected_highlights = source.split("\n")[1].strip
+
+ inspect_source(cop, source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([2])
+ expect(cop.highlights).to contain_exactly(expected_highlights)
+ end
+ end
+
+ it 'autocorrects to the right version' do
+ autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb')
+
+ expect(autocorrected).to eq(expected)
+ end
+ end
+
+ context 'when the class is on multiple lines' do
+ let(:source) do
+ <<-SOURCE
+ module Foo
+ class CustomError < Bar::Baz::BaseError
+ end
+ end
+ SOURCE
+ end
+
+ let(:expected) do
+ <<-EXPECTED
+ module Foo
+ CustomError = Class.new(Bar::Baz::BaseError)
+ end
+ EXPECTED
+ end
+
+ it 'registers an offense' do
+ expected_highlights = source.split("\n")[1..2].join("\n").strip
+
+ inspect_source(cop, source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([2])
+ expect(cop.highlights).to contain_exactly(expected_highlights)
+ end
+ end
+
+ it 'autocorrects to the right version' do
+ autocorrected = autocorrect_source(cop, source, 'foo/custom_error.rb')
+
+ expect(autocorrected).to eq(expected)
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
deleted file mode 100644
index b3e0a7b9b58..00000000000
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require 'spec_helper'
-
-module Ci
- describe ImageForBuildService, services: true do
- let(:service) { ImageForBuildService.new }
- let(:project) { FactoryGirl.create(:empty_project) }
- let(:commit_sha) { '01234567890123456789' }
- let(:pipeline) { project.ensure_pipeline('master', commit_sha) }
- let(:build) { FactoryGirl.create(:ci_build, pipeline: pipeline) }
-
- describe '#execute' do
- before { build }
-
- context 'branch name' do
- before { allow(project).to receive(:commit).and_return(OpenStruct.new(sha: commit_sha)) }
- before { build.run! }
- let(:image) { service.execute(project, ref: 'master') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-running.svg') }
- it { expect(image.name).to eq('build-running.svg') }
- end
-
- context 'unknown branch name' do
- let(:image) { service.execute(project, ref: 'feature') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') }
- it { expect(image.name).to eq('build-unknown.svg') }
- end
-
- context 'commit sha' do
- before { build.run! }
- let(:image) { service.execute(project, sha: build.sha) }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-running.svg') }
- it { expect(image.name).to eq('build-running.svg') }
- end
-
- context 'unknown commit sha' do
- let(:image) { service.execute(project, sha: '0000000') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') }
- it { expect(image.name).to eq('build-unknown.svg') }
- end
- end
- end
-end
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index b818dfdd50c..de68fb64726 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -341,7 +341,7 @@ describe Ci::ProcessPipelineService, :services do
expect(builds.pending.count).to eq(1)
expect(all_builds.count).to eq(4)
- # When pending build succeeds in stage test, we enqueue deploy stage.
+ # When pending merge_when_pipeline_succeeds in stage test, we enqueue deploy stage.
#
succeed_pending
process_pipeline
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index d9f774a1095..cd7dd53025c 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -170,6 +170,51 @@ module Ci
end
end
+ context 'when first build is stalled' do
+ before do
+ pending_build.lock_version = 10
+ end
+
+ subject { described_class.new(specific_runner).execute }
+
+ context 'with multiple builds are in queue' do
+ let!(:other_build) { create :ci_build, pipeline: pipeline }
+
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([pending_build, other_build])
+ end
+
+ it "receives second build from the queue" do
+ expect(subject).to be_valid
+ expect(subject.build).to eq(other_build)
+ end
+ end
+
+ context 'when single build is in queue' do
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([pending_build])
+ end
+
+ it "does not receive any valid result" do
+ expect(subject).not_to be_valid
+ end
+ end
+
+ context 'when there is no build in queue' do
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([])
+ end
+
+ it "does not receive builds but result is valid" do
+ expect(subject).to be_valid
+ expect(subject.build).to be_nil
+ end
+ end
+ end
+
def execute(runner)
described_class.new(runner).execute.build
end
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index d03f7505eac..65af4e13118 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -10,22 +10,39 @@ describe Ci::RetryBuildService, :services do
described_class.new(project, user)
end
+ CLONE_ACCESSORS = described_class::CLONE_ACCESSORS
+
+ REJECT_ACCESSORS =
+ %i[id status user token coverage trace runner artifacts_expire_at
+ artifacts_file artifacts_metadata artifacts_size created_at
+ updated_at started_at finished_at queued_at erased_by
+ erased_at].freeze
+
+ IGNORE_ACCESSORS =
+ %i[type lock_version target_url gl_project_id deploy job_id base_tags
+ commit_id deployments erased_by_id last_deployment project_id
+ runner_id tag_taggings taggings tags trigger_request_id
+ user_id].freeze
+
shared_examples 'build duplication' do
let(:build) do
- create(:ci_build, :failed, :artifacts_expired, :erased, :trace,
- :queued, :coverage, pipeline: pipeline)
+ create(:ci_build, :failed, :artifacts_expired, :erased,
+ :queued, :coverage, :tags, :allowed_to_fail, :on_tag,
+ :teardown_environment, :triggered, :trace,
+ description: 'some build', pipeline: pipeline)
end
- describe 'clone attributes' do
- described_class::CLONE_ATTRIBUTES.each do |attribute|
+ describe 'clone accessors' do
+ CLONE_ACCESSORS.each do |attribute|
it "clones #{attribute} build attribute" do
+ expect(new_build.send(attribute)).to be_present
expect(new_build.send(attribute)).to eq build.send(attribute)
end
end
end
- describe 'reject attributes' do
- described_class::REJECT_ATTRIBUTES.each do |attribute|
+ describe 'reject acessors' do
+ REJECT_ACCESSORS.each do |attribute|
it "does not clone #{attribute} build attribute" do
expect(new_build.send(attribute)).not_to eq build.send(attribute)
end
@@ -33,12 +50,20 @@ describe Ci::RetryBuildService, :services do
end
it 'has correct number of known attributes' do
- attributes =
- described_class::CLONE_ATTRIBUTES +
- described_class::IGNORE_ATTRIBUTES +
- described_class::REJECT_ATTRIBUTES
+ known_accessors = CLONE_ACCESSORS + REJECT_ACCESSORS + IGNORE_ACCESSORS
+
+ # :tag_list is a special case, this accessor does not exist
+ # in reflected associations, comes from `act_as_taggable` and
+ # we use it to copy tags, instead of reusing tags.
+ #
+ current_accessors =
+ Ci::Build.attribute_names.map(&:to_sym) +
+ Ci::Build.reflect_on_all_associations.map(&:name) +
+ [:tag_list]
+
+ current_accessors.uniq!
- expect(build.attributes.size).to eq(attributes.size)
+ expect(known_accessors).to contain_exactly(*current_accessors)
end
end
diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index 0ff6e8fda16..c2f205c389d 100644
--- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
let(:project) { create(:project) }
let(:mr_merge_if_green_enabled) do
- create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
+ create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user,
source_branch: "master", target_branch: 'feature',
source_project: project, target_project: project, state: "opened")
end
@@ -36,7 +36,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
it 'sets the params, merge_user, and flag' do
expect(merge_request).to be_valid
- expect(merge_request.merge_when_build_succeeds).to be_truthy
+ expect(merge_request.merge_when_pipeline_succeeds).to be_truthy
expect(merge_request.merge_params).to eq commit_message: 'Awesome message'
expect(merge_request.merge_user).to be user
end
@@ -62,7 +62,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
end
it 'updates the merge params' do
- expect(SystemNoteService).not_to receive(:merge_when_build_succeeds)
+ expect(SystemNoteService).not_to receive(:merge_when_pipeline_succeeds)
service.execute(mr_merge_if_green_enabled)
expect(mr_merge_if_green_enabled.merge_params).to have_key(:new_key)
@@ -82,7 +82,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
sha: merge_request_head, status: 'success')
end
- it "merges all merge requests with merge when build succeeds enabled" do
+ it "merges all merge requests with merge when the pipeline succeeds enabled" do
expect(MergeWorker).to receive(:perform_async)
service.trigger(triggering_pipeline)
end
@@ -114,7 +114,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
context 'when the merge request is not mergeable' do
let(:mr_conflict) do
- create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
+ create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user,
source_branch: 'master', target_branch: 'feature-conflict',
source_project: project, target_project: project)
end
@@ -143,8 +143,8 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
service.cancel(mr_merge_if_green_enabled)
end
- it "resets all the merge_when_build_succeeds params" do
- expect(mr_merge_if_green_enabled.merge_when_build_succeeds).to be_falsey
+ it "resets all the pipeline succeeds params" do
+ expect(mr_merge_if_green_enabled.merge_when_pipeline_succeeds).to be_falsey
expect(mr_merge_if_green_enabled.merge_params).to eq({})
expect(mr_merge_if_green_enabled.merge_user).to be nil
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 983dac6efdb..ff367f54d2a 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -18,7 +18,7 @@ describe MergeRequests::RefreshService, services: true do
source_branch: 'master',
target_branch: 'feature',
target_project: @project,
- merge_when_build_succeeds: true,
+ merge_when_pipeline_succeeds: true,
merge_user: @user)
@fork_merge_request = create(:merge_request,
@@ -62,7 +62,7 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.notes).not_to be_empty }
it { expect(@merge_request).to be_open }
- it { expect(@merge_request.merge_when_build_succeeds).to be_falsey }
+ it { expect(@merge_request.merge_when_pipeline_succeeds).to be_falsey }
it { expect(@merge_request.diff_head_sha).to eq(@newrev) }
it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 9c92a5080c6..152c6d20daa 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -102,47 +102,19 @@ describe Notes::CreateService, services: true do
expect(subject.note).to eq(params[:note])
end
end
- end
-
- describe "award emoji" do
- before do
- project.team << [user, :master]
- end
-
- it "creates an award emoji" do
- opts = {
- note: ':smile: ',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
- note = described_class.new(project, user, opts).execute
-
- expect(note).to be_valid
- expect(note.name).to eq('smile')
- end
- it "creates regular note if emoji name is invalid" do
- opts = {
- note: ':smile: moretext:',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
- note = described_class.new(project, user, opts).execute
-
- expect(note).to be_valid
- expect(note.note).to eq(opts[:note])
- end
-
- it "normalizes the emoji name" do
- opts = {
- note: ':+1:',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
-
- expect_any_instance_of(TodoService).to receive(:new_award_emoji).with(issue, user)
+ describe 'note with emoji only' do
+ it 'creates regular note' do
+ opts = {
+ note: ':smile: ',
+ noteable_type: 'Issue',
+ noteable_id: issue.id
+ }
+ note = described_class.new(project, user, opts).execute
- described_class.new(project, user, opts).execute
+ expect(note).to be_valid
+ expect(note.note).to eq(':smile:')
+ end
end
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 839250b7d84..ebbaea4e59a 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1050,22 +1050,22 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- it "notifies the merger when merge_when_build_succeeds is true" do
- merge_request.merge_when_build_succeeds = true
+ it "notifies the merger when the pipeline succeeds is true" do
+ merge_request.merge_when_pipeline_succeeds = true
notification.merge_mr(merge_request, @u_watcher)
should_email(@u_watcher)
end
- it "does not notify the merger when merge_when_build_succeeds is false" do
- merge_request.merge_when_build_succeeds = false
+ it "does not notify the merger when the pipeline succeeds is false" do
+ merge_request.merge_when_pipeline_succeeds = false
notification.merge_mr(merge_request, @u_watcher)
should_not_email(@u_watcher)
end
- it "notifies the merger when merge_when_build_succeeds is false but they've opted into notifications about their activity" do
- merge_request.merge_when_build_succeeds = false
+ it "notifies the merger when the pipeline succeeds is false but they've opted into notifications about their activity" do
+ merge_request.merge_when_pipeline_succeeds = false
@u_watcher.notified_of_own_activity = true
notification.merge_mr(merge_request, @u_watcher)
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb
index 0b0925983eb..52e8678cb9d 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/slash_commands/interpret_service_spec.rb
@@ -267,6 +267,14 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ shared_examples 'award command' do
+ it 'toggle award 100 emoji if content containts /award :100:' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(emoji_award: "100")
+ end
+ end
+
it_behaves_like 'reopen command' do
let(:content) { '/reopen' }
let(:issuable) { issue }
@@ -654,6 +662,37 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ context '/award command' do
+ it_behaves_like 'award command' do
+ let(:content) { '/award :100:' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'award command' do
+ let(:content) { '/award :100:' }
+ let(:issuable) { merge_request }
+ end
+
+ context 'ignores command with no argument' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/award' }
+ let(:issuable) { issue }
+ end
+ end
+
+ context 'ignores non-existing / invalid emojis' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/award noop' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'empty command' do
+ let(:content) { '/award :lorem_ipsum:' }
+ let(:issuable) { issue }
+ end
+ end
+ end
+
context '/target_branch command' do
let(:non_empty_project) { create(:project) }
let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index eca5a418f2a..36a17a3bf2e 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -215,13 +215,13 @@ describe SystemNoteService, services: true do
end
end
- describe '.merge_when_build_succeeds' do
+ describe '.merge_when_pipeline_succeeds' do
let(:pipeline) { build(:ci_pipeline_without_jobs )}
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
- subject { described_class.merge_when_build_succeeds(noteable, project, author, noteable.diff_head_commit) }
+ subject { described_class.merge_when_pipeline_succeeds(noteable, project, author, noteable.diff_head_commit) }
it_behaves_like 'a system note'
@@ -230,12 +230,12 @@ describe SystemNoteService, services: true do
end
end
- describe '.cancel_merge_when_build_succeeds' do
+ describe '.cancel_merge_when_pipeline_succeeds' do
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
- subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) }
+ subject { described_class.cancel_merge_when_pipeline_succeeds(noteable, project, author) }
it_behaves_like 'a system note'
@@ -418,45 +418,6 @@ describe SystemNoteService, services: true do
to be_truthy
end
end
-
- context 'when noteable is an Issue' do
- let(:issue) { create(:issue, project: project) }
-
- it 'is truthy when issue is closed' do
- issue.close
-
- expect(described_class.cross_reference_disallowed?(issue, project.commit)).
- to be_truthy
- end
-
- it 'is falsey when issue is open' do
- expect(described_class.cross_reference_disallowed?(issue, project.commit)).
- to be_falsy
- end
- end
-
- context 'when noteable is a Merge Request' do
- let(:merge_request) { create(:merge_request, :simple, source_project: project) }
-
- it 'is truthy when merge request is closed' do
- allow(merge_request).to receive(:closed?).and_return(:true)
-
- expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
- to be_truthy
- end
-
- it 'is truthy when merge request is merged' do
- allow(merge_request).to receive(:closed?).and_return(:true)
-
- expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
- to be_truthy
- end
-
- it 'is falsey when merge request is open' do
- expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
- to be_falsy
- end
- end
end
describe '.cross_reference_exists?' do
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 9f24cc0f3f2..fb9a8462f84 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -680,7 +680,7 @@ describe TodoService, services: true do
end
it 'creates a pending todo for merge_user' do
- mr_unassigned.update(merge_when_build_succeeds: true, merge_user: admin)
+ mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
service.merge_request_build_failed(mr_unassigned)
should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::BUILD_FAILED)
@@ -700,7 +700,7 @@ describe TodoService, services: true do
describe '#merge_request_became_unmergeable' do
it 'creates a pending todo for a merge_user' do
- mr_unassigned.update(merge_when_build_succeeds: true, merge_user: admin)
+ mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
service.merge_request_became_unmergeable(mr_unassigned)
should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::UNMERGEABLE)
diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb
new file mode 100644
index 00000000000..9a3b0a731ad
--- /dev/null
+++ b/spec/support/features/rss_shared_examples.rb
@@ -0,0 +1,23 @@
+shared_examples "an autodiscoverable RSS feed with current_user's private token" do
+ it "has an RSS autodiscovery link tag with current_user's private token" do
+ expect(page).to have_css("link[type*='atom+xml'][href*='private_token=#{Thread.current[:current_user].private_token}']", visible: false)
+ end
+end
+
+shared_examples "it has an RSS button with current_user's private token" do
+ it "shows the RSS button with current_user's private token" do
+ expect(page).to have_css("a:has(.fa-rss)[href*='private_token=#{Thread.current[:current_user].private_token}']")
+ end
+end
+
+shared_examples "an autodiscoverable RSS feed without a private token" do
+ it "has an RSS autodiscovery link tag without a private token" do
+ expect(page).to have_css("link[type*='atom+xml']:not([href*='private_token'])", visible: false)
+ end
+end
+
+shared_examples "it has an RSS button without a private token" do
+ it "shows the RSS button without a private token" do
+ expect(page).to have_css("a:has(.fa-rss):not([href*='private_token'])")
+ end
+end
diff --git a/spec/support/matchers/gitaly_matchers.rb b/spec/support/matchers/gitaly_matchers.rb
new file mode 100644
index 00000000000..d7a53820684
--- /dev/null
+++ b/spec/support/matchers/gitaly_matchers.rb
@@ -0,0 +1,3 @@
+RSpec::Matchers.define :post_receive_request_with_repo_path do |path|
+ match { |actual| actual.repository.path == path }
+end
diff --git a/spec/support/project_features_apply_to_issuables_shared_examples.rb b/spec/support/project_features_apply_to_issuables_shared_examples.rb
index 4621d17549b..f8b7d0527ba 100644
--- a/spec/support/project_features_apply_to_issuables_shared_examples.rb
+++ b/spec/support/project_features_apply_to_issuables_shared_examples.rb
@@ -18,7 +18,7 @@ shared_examples 'project features apply to issuables' do |klass|
before do
_ = issuable
- login_as(user)
+ login_as(user) if user
visit path
end
diff --git a/spec/views/ci/status/_badge.html.haml_spec.rb b/spec/views/ci/status/_badge.html.haml_spec.rb
new file mode 100644
index 00000000000..c62450fb8e2
--- /dev/null
+++ b/spec/views/ci/status/_badge.html.haml_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe 'ci/status/_badge', :view do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, :private) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when rendering status for build' do
+ let(:build) do
+ create(:ci_build, :success, pipeline: pipeline)
+ end
+
+ context 'when user has ability to see details' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'has link to build details page' do
+ details_path = namespace_project_build_path(
+ project.namespace, project, build)
+
+ render_status(build)
+
+ expect(rendered).to have_link 'passed', href: details_path
+ end
+ end
+
+ context 'when user do not have ability to see build details' do
+ before do
+ render_status(build)
+ end
+
+ it 'contains build status text' do
+ expect(rendered).to have_content 'passed'
+ end
+
+ it 'does not contain links' do
+ expect(rendered).not_to have_link 'passed'
+ end
+ end
+ end
+
+ context 'when rendering status for external job' do
+ context 'when user has ability to see commit status details' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'status has external target url' do
+ before do
+ external_job = create(:generic_commit_status,
+ status: :running,
+ pipeline: pipeline,
+ target_url: 'http://gitlab.com')
+
+ render_status(external_job)
+ end
+
+ it 'contains valid commit status text' do
+ expect(rendered).to have_content 'running'
+ end
+
+ it 'has link to external status page' do
+ expect(rendered).to have_link 'running', href: 'http://gitlab.com'
+ end
+ end
+
+ context 'status do not have external target url' do
+ before do
+ external_job = create(:generic_commit_status, status: :canceled)
+
+ render_status(external_job)
+ end
+
+ it 'contains valid commit status text' do
+ expect(rendered).to have_content 'canceled'
+ end
+
+ it 'has link to external status page' do
+ expect(rendered).not_to have_link 'canceled'
+ end
+ end
+ end
+ end
+
+ def render_status(resource)
+ render 'ci/status/badge', status: resource.detailed_status(user)
+ end
+end