summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2016-10-19 22:33:34 +0100
committerPhil Hughes <me@iamphill.com>2016-10-19 22:33:34 +0100
commitfcf0a4a12d1ca17438a99c2b7bf334b82fa0e26e (patch)
tree8e109ee1d989f741fd7c1b0fd26cfdbf9a545b31 /spec
parentcd5e83b6d6da3bddbc44334a1bcdbac287b35fb4 (diff)
parentc08435e3c25f0a7a705ed8a49b16dde176b41a40 (diff)
downloadgitlab-ce-fcf0a4a12d1ca17438a99c2b7bf334b82fa0e26e.tar.gz
Merge branch 'master' into issue-board-sidebar
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb120
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb61
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb195
-rw-r--r--spec/factories/group_members.rb1
-rw-r--r--spec/factories/label_priorities.rb7
-rw-r--r--spec/factories/labels.rb18
-rw-r--r--spec/factories/merge_requests.rb10
-rw-r--r--spec/factories/project_members.rb1
-rw-r--r--spec/features/environments_spec.rb112
-rw-r--r--spec/features/issues/award_emoji_spec.rb46
-rw-r--r--spec/features/issues/reset_filters_spec.rb8
-rw-r--r--spec/features/merge_requests/merge_when_build_succeeds_spec.rb2
-rw-r--r--spec/features/merge_requests/widget_deployments_spec.rb53
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin1363770 -> 681774 bytes
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb100
-rw-r--r--spec/finders/labels_finder_spec.rb69
-rw-r--r--spec/fixtures/api/schemas/list.json2
-rw-r--r--spec/helpers/labels_helper_spec.rb27
-rw-r--r--spec/lib/banzai/filter/external_issue_reference_filter_spec.rb72
-rw-r--r--spec/lib/banzai/filter/html_entity_filter_spec.rb5
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb17
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb82
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb48
-rw-r--r--spec/lib/gitlab/ci/config/node/environment_spec.rb64
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb26
-rw-r--r--spec/lib/gitlab/git_access_spec.rb12
-rw-r--r--spec/lib/gitlab/github_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml3
-rw-r--r--spec/lib/gitlab/import_export/project.json52
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb37
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb23
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml11
-rw-r--r--spec/models/deployment_spec.rb46
-rw-r--r--spec/models/environment_spec.rb70
-rw-r--r--spec/models/external_issue_spec.rb15
-rw-r--r--spec/models/group_label_spec.rb47
-rw-r--r--spec/models/group_spec.rb1
-rw-r--r--spec/models/label_priority_spec.rb20
-rw-r--r--spec/models/label_spec.rb120
-rw-r--r--spec/models/merge_request_spec.rb58
-rw-r--r--spec/models/project_label_spec.rb120
-rw-r--r--spec/models/project_services/jira_service_spec.rb9
-rw-r--r--spec/models/project_services/redmine_service_spec.rb8
-rw-r--r--spec/models/project_spec.rb2
-rw-r--r--spec/requests/api/boards_spec.rb25
-rw-r--r--spec/requests/api/commit_statuses_spec.rb4
-rw-r--r--spec/requests/api/labels_spec.rb12
-rw-r--r--spec/requests/api/system_hooks_spec.rb7
-rw-r--r--spec/services/boards/lists/create_service_spec.rb4
-rw-r--r--spec/services/boards/lists/generate_service_spec.rb4
-rw-r--r--spec/services/create_deployment_service_spec.rb65
-rw-r--r--spec/services/git_push_service_spec.rb60
-rw-r--r--spec/services/issues/create_service_spec.rb21
-rw-r--r--spec/services/labels/find_or_create_service_spec.rb51
-rw-r--r--spec/services/labels/transfer_service_spec.rb56
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb16
-rw-r--r--spec/services/projects/transfer_service_spec.rb10
-rw-r--r--spec/support/issue_tracker_service_shared_example.rb15
59 files changed, 1655 insertions, 504 deletions
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index a0870891cf4..ad15b3f8f40 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -2,16 +2,10 @@ require 'spec_helper'
describe Groups::GroupMembersController do
let(:user) { create(:user) }
+ let(:group) { create(:group, :public) }
- describe '#index' do
- let(:group) { create(:group) }
-
- before do
- group.add_owner(user)
- stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
- end
-
- it 'renders index with group members' do
+ describe 'GET index' do
+ it 'renders index with 200 status code' do
get :index, group_id: group
expect(response).to have_http_status(200)
@@ -19,74 +13,56 @@ describe Groups::GroupMembersController do
end
end
- describe '#destroy' do
- let(:group) { create(:group, :public) }
+ describe 'DELETE destroy' do
+ let(:member) { create(:group_member, :developer, group: group) }
+
+ before { sign_in(user) }
context 'when member is not found' do
it 'returns 403' do
- delete :destroy, group_id: group,
- id: 42
+ delete :destroy, group_id: group, id: 42
expect(response).to have_http_status(403)
end
end
context 'when member is found' do
- let(:user) { create(:user) }
- let(:group_user) { create(:user) }
- let(:member) do
- group.add_developer(group_user)
- group.members.find_by(user_id: group_user)
- end
-
context 'when user does not have enough rights' do
- before do
- group.add_developer(user)
- sign_in(user)
- end
+ before { group.add_developer(user) }
it 'returns 403' do
- delete :destroy, group_id: group,
- id: member
+ delete :destroy, group_id: group, id: member
expect(response).to have_http_status(403)
- expect(group.users).to include group_user
+ expect(group.members).to include member
end
end
context 'when user has enough rights' do
- before do
- group.add_owner(user)
- sign_in(user)
- end
+ before { group.add_owner(user) }
it '[HTML] removes user from members' do
- delete :destroy, group_id: group,
- id: member
+ delete :destroy, group_id: group, id: member
expect(response).to set_flash.to 'User was successfully removed from group.'
expect(response).to redirect_to(group_group_members_path(group))
- expect(group.users).not_to include group_user
+ expect(group.members).not_to include member
end
it '[JS] removes user from members' do
- xhr :delete, :destroy, group_id: group,
- id: member
+ xhr :delete, :destroy, group_id: group, id: member
expect(response).to be_success
- expect(group.users).not_to include group_user
+ expect(group.members).not_to include member
end
end
end
end
- describe '#leave' do
- let(:group) { create(:group, :public) }
- let(:user) { create(:user) }
+ describe 'DELETE leave' do
+ before { sign_in(user) }
context 'when member is not found' do
- before { sign_in(user) }
-
it 'returns 404' do
delete :leave, group_id: group
@@ -96,10 +72,7 @@ describe Groups::GroupMembersController do
context 'when member is found' do
context 'and is not an owner' do
- before do
- group.add_developer(user)
- sign_in(user)
- end
+ before { group.add_developer(user) }
it 'removes user from members' do
delete :leave, group_id: group
@@ -111,10 +84,7 @@ describe Groups::GroupMembersController do
end
context 'and is an owner' do
- before do
- group.add_owner(user)
- sign_in(user)
- end
+ before { group.add_owner(user) }
it 'cannot removes himself from the group' do
delete :leave, group_id: group
@@ -124,10 +94,7 @@ describe Groups::GroupMembersController do
end
context 'and is a requester' do
- before do
- group.request_access(user)
- sign_in(user)
- end
+ before { group.request_access(user) }
it 'removes user from members' do
delete :leave, group_id: group
@@ -141,13 +108,8 @@ describe Groups::GroupMembersController do
end
end
- describe '#request_access' do
- let(:group) { create(:group, :public) }
- let(:user) { create(:user) }
-
- before do
- sign_in(user)
- end
+ describe 'POST request_access' do
+ before { sign_in(user) }
it 'creates a new GroupMember that is not a team member' do
post :request_access, group_id: group
@@ -159,53 +121,39 @@ describe Groups::GroupMembersController do
end
end
- describe '#approve_access_request' do
- let(:group) { create(:group, :public) }
+ describe 'POST approve_access_request' do
+ let(:member) { create(:group_member, :access_request, group: group) }
+
+ before { sign_in(user) }
context 'when member is not found' do
it 'returns 403' do
- post :approve_access_request, group_id: group,
- id: 42
+ post :approve_access_request, group_id: group, id: 42
expect(response).to have_http_status(403)
end
end
context 'when member is found' do
- let(:user) { create(:user) }
- let(:group_requester) { create(:user) }
- let(:member) do
- group.request_access(group_requester)
- group.requesters.find_by(user_id: group_requester)
- end
-
context 'when user does not have enough rights' do
- before do
- group.add_developer(user)
- sign_in(user)
- end
+ before { group.add_developer(user) }
it 'returns 403' do
- post :approve_access_request, group_id: group,
- id: member
+ post :approve_access_request, group_id: group, id: member
expect(response).to have_http_status(403)
- expect(group.users).not_to include group_requester
+ expect(group.members).not_to include member
end
end
context 'when user has enough rights' do
- before do
- group.add_owner(user)
- sign_in(user)
- end
+ before { group.add_owner(user) }
it 'adds user to members' do
- post :approve_access_request, group_id: group,
- id: member
+ post :approve_access_request, group_id: group, id: member
expect(response).to redirect_to(group_group_members_path(group))
- expect(group.users).to include group_requester
+ expect(group.members).to include member
end
end
end
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index 3492b6ffbbb..622ab154493 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -1,52 +1,73 @@
require 'spec_helper'
describe Projects::LabelsController do
- let(:project) { create(:project) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
let(:user) { create(:user) }
before do
project.team << [user, :master]
+
sign_in(user)
end
describe 'GET #index' do
- def create_label(attributes)
- create(:label, attributes.merge(project: project))
- end
+ let!(:label_1) { create(:label, project: project, priority: 1, title: 'Label 1') }
+ let!(:label_2) { create(:label, project: project, priority: 3, title: 'Label 2') }
+ let!(:label_3) { create(:label, project: project, priority: 1, title: 'Label 3') }
+ let!(:label_4) { create(:label, project: project, title: 'Label 4') }
+ let!(:label_5) { create(:label, project: project, title: 'Label 5') }
- before do
- 15.times { |i| create_label(priority: (i % 3) + 1, title: "label #{15 - i}") }
- 5.times { |i| create_label(title: "label #{100 - i}") }
+ let!(:group_label_1) { create(:group_label, group: group, title: 'Group Label 1') }
+ let!(:group_label_2) { create(:group_label, group: group, title: 'Group Label 2') }
+ let!(:group_label_3) { create(:group_label, group: group, title: 'Group Label 3') }
+ let!(:group_label_4) { create(:group_label, group: group, title: 'Group Label 4') }
- get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
+ before do
+ create(:label_priority, project: project, label: group_label_1, priority: 3)
+ create(:label_priority, project: project, label: group_label_2, priority: 1)
end
context '@prioritized_labels' do
- let(:prioritized_labels) { assigns(:prioritized_labels) }
+ before do
+ list_labels
+ end
+
+ it 'does not include labels without priority' do
+ list_labels
- it 'contains only prioritized labels' do
- expect(prioritized_labels).to all(have_attributes(priority: a_value > 0))
+ expect(assigns(:prioritized_labels)).not_to include(group_label_3, group_label_4, label_4, label_5)
end
it 'is sorted by priority, then label title' do
- priorities_and_titles = prioritized_labels.pluck(:priority, :title)
-
- expect(priorities_and_titles.sort).to eq(priorities_and_titles)
+ expect(assigns(:prioritized_labels)).to eq [group_label_2, label_1, label_3, group_label_1, label_2]
end
end
context '@labels' do
- let(:labels) { assigns(:labels) }
+ it 'is sorted by label title' do
+ list_labels
- it 'contains only unprioritized labels' do
- expect(labels).to all(have_attributes(priority: nil))
+ expect(assigns(:labels)).to eq [group_label_3, group_label_4, label_4, label_5]
end
- it 'is sorted by label title' do
- titles = labels.pluck(:title)
+ it 'does not include labels with priority' do
+ list_labels
+
+ expect(assigns(:labels)).not_to include(group_label_2, label_1, label_3, group_label_1, label_2)
+ end
+
+ it 'does not include group labels when project does not belong to a group' do
+ project.update(namespace: create(:namespace))
- expect(titles.sort).to eq(titles)
+ list_labels
+
+ expect(assigns(:labels)).not_to include(group_label_3, group_label_4)
end
end
+
+ def list_labels
+ get :index, namespace_id: project.namespace.to_param, project_id: project.to_param
+ end
end
end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index 074f85157de..8519ebc1d5f 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -1,69 +1,22 @@
require('spec_helper')
describe Projects::ProjectMembersController do
- describe '#apply_import' do
- let(:project) { create(:project) }
- let(:another_project) { create(:project, :private) }
- let(:user) { create(:user) }
- let(:member) { create(:user) }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
- before do
- project.team << [user, :master]
- another_project.team << [member, :guest]
- sign_in(user)
- end
+ describe 'GET index' do
+ it 'renders index with 200 status code' do
+ get :index, namespace_id: project.namespace, project_id: project
- shared_context 'import applied' do
- before do
- post(:apply_import, namespace_id: project.namespace,
- project_id: project,
- source_project_id: another_project.id)
- end
- end
-
- context 'when user can access source project members' do
- before { another_project.team << [user, :guest] }
- include_context 'import applied'
-
- it 'imports source project members' do
- expect(project.team_members).to include member
- expect(response).to set_flash.to 'Successfully imported'
- expect(response).to redirect_to(
- namespace_project_project_members_path(project.namespace, project)
- )
- end
- end
-
- context 'when user is not member of a source project' do
- include_context 'import applied'
-
- it 'does not import team members' do
- expect(project.team_members).not_to include member
- end
-
- it 'responds with not found' do
- expect(response.status).to eq 404
- end
+ expect(response).to have_http_status(200)
+ expect(response).to render_template(:index)
end
end
- describe '#index' do
- context 'when user is member' do
- before do
- project = create(:project, :private)
- member = create(:user)
- project.team << [member, :guest]
- sign_in(member)
-
- get :index, namespace_id: project.namespace, project_id: project
- end
-
- it { expect(response).to have_http_status(200) }
- end
- end
+ describe 'DELETE destroy' do
+ let(:member) { create(:project_member, :developer, project: project) }
- describe '#destroy' do
- let(:project) { create(:project, :public) }
+ before { sign_in(user) }
context 'when member is not found' do
it 'returns 404' do
@@ -76,18 +29,8 @@ describe Projects::ProjectMembersController do
end
context 'when member is found' do
- let(:user) { create(:user) }
- let(:team_user) { create(:user) }
- let(:member) do
- project.team << [team_user, :developer]
- project.members.find_by(user_id: team_user.id)
- end
-
context 'when user does not have enough rights' do
- before do
- project.team << [user, :developer]
- sign_in(user)
- end
+ before { project.team << [user, :developer] }
it 'returns 404' do
delete :destroy, namespace_id: project.namespace,
@@ -95,15 +38,12 @@ describe Projects::ProjectMembersController do
id: member
expect(response).to have_http_status(404)
- expect(project.users).to include team_user
+ expect(project.members).to include member
end
end
context 'when user has enough rights' do
- before do
- project.team << [user, :master]
- sign_in(user)
- end
+ before { project.team << [user, :master] }
it '[HTML] removes user from members' do
delete :destroy, namespace_id: project.namespace,
@@ -113,7 +53,7 @@ describe Projects::ProjectMembersController do
expect(response).to redirect_to(
namespace_project_project_members_path(project.namespace, project)
)
- expect(project.users).not_to include team_user
+ expect(project.members).not_to include member
end
it '[JS] removes user from members' do
@@ -122,19 +62,16 @@ describe Projects::ProjectMembersController do
id: member
expect(response).to be_success
- expect(project.users).not_to include team_user
+ expect(project.members).not_to include member
end
end
end
end
- describe '#leave' do
- let(:project) { create(:project, :public) }
- let(:user) { create(:user) }
+ describe 'DELETE leave' do
+ before { sign_in(user) }
context 'when member is not found' do
- before { sign_in(user) }
-
it 'returns 404' do
delete :leave, namespace_id: project.namespace,
project_id: project
@@ -145,10 +82,7 @@ describe Projects::ProjectMembersController do
context 'when member is found' do
context 'and is not an owner' do
- before do
- project.team << [user, :developer]
- sign_in(user)
- end
+ before { project.team << [user, :developer] }
it 'removes user from members' do
delete :leave, namespace_id: project.namespace,
@@ -161,11 +95,9 @@ describe Projects::ProjectMembersController do
end
context 'and is an owner' do
- before do
- project.update(namespace_id: user.namespace_id)
- project.team << [user, :master, user]
- sign_in(user)
- end
+ let(:project) { create(:project, namespace: user.namespace) }
+
+ before { project.team << [user, :master] }
it 'cannot remove himself from the project' do
delete :leave, namespace_id: project.namespace,
@@ -176,10 +108,7 @@ describe Projects::ProjectMembersController do
end
context 'and is a requester' do
- before do
- project.request_access(user)
- sign_in(user)
- end
+ before { project.request_access(user) }
it 'removes user from members' do
delete :leave, namespace_id: project.namespace,
@@ -194,13 +123,8 @@ describe Projects::ProjectMembersController do
end
end
- describe '#request_access' do
- let(:project) { create(:project, :public) }
- let(:user) { create(:user) }
-
- before do
- sign_in(user)
- end
+ describe 'POST request_access' do
+ before { sign_in(user) }
it 'creates a new ProjectMember that is not a team member' do
post :request_access, namespace_id: project.namespace,
@@ -215,8 +139,10 @@ describe Projects::ProjectMembersController do
end
end
- describe '#approve' do
- let(:project) { create(:project, :public) }
+ describe 'POST approve' do
+ let(:member) { create(:project_member, :access_request, project: project) }
+
+ before { sign_in(user) }
context 'when member is not found' do
it 'returns 404' do
@@ -229,18 +155,8 @@ describe Projects::ProjectMembersController do
end
context 'when member is found' do
- let(:user) { create(:user) }
- let(:team_requester) { create(:user) }
- let(:member) do
- project.request_access(team_requester)
- project.requesters.find_by(user_id: team_requester.id)
- end
-
context 'when user does not have enough rights' do
- before do
- project.team << [user, :developer]
- sign_in(user)
- end
+ before { project.team << [user, :developer] }
it 'returns 404' do
post :approve_access_request, namespace_id: project.namespace,
@@ -248,15 +164,12 @@ describe Projects::ProjectMembersController do
id: member
expect(response).to have_http_status(404)
- expect(project.users).not_to include team_requester
+ expect(project.members).not_to include member
end
end
context 'when user has enough rights' do
- before do
- project.team << [user, :master]
- sign_in(user)
- end
+ before { project.team << [user, :master] }
it 'adds user to members' do
post :approve_access_request, namespace_id: project.namespace,
@@ -266,9 +179,53 @@ describe Projects::ProjectMembersController do
expect(response).to redirect_to(
namespace_project_project_members_path(project.namespace, project)
)
- expect(project.users).to include team_requester
+ expect(project.members).to include member
end
end
end
end
+
+ describe 'POST apply_import' do
+ let(:another_project) { create(:project, :private) }
+ let(:member) { create(:user) }
+
+ before do
+ project.team << [user, :master]
+ another_project.team << [member, :guest]
+ sign_in(user)
+ end
+
+ shared_context 'import applied' do
+ before do
+ post(:apply_import, namespace_id: project.namespace,
+ project_id: project,
+ source_project_id: another_project.id)
+ end
+ end
+
+ context 'when user can access source project members' do
+ before { another_project.team << [user, :guest] }
+ include_context 'import applied'
+
+ it 'imports source project members' do
+ expect(project.team_members).to include member
+ expect(response).to set_flash.to 'Successfully imported'
+ expect(response).to redirect_to(
+ namespace_project_project_members_path(project.namespace, project)
+ )
+ end
+ end
+
+ context 'when user is not member of a source project' do
+ include_context 'import applied'
+
+ it 'does not import team members' do
+ expect(project.team_members).not_to include member
+ end
+
+ it 'responds with not found' do
+ expect(response.status).to eq 404
+ end
+ end
+ end
end
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 795df5dfda9..080b2e75ea1 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -9,5 +9,6 @@ FactoryGirl.define do
trait(:developer) { access_level GroupMember::DEVELOPER }
trait(:master) { access_level GroupMember::MASTER }
trait(:owner) { access_level GroupMember::OWNER }
+ trait(:access_request) { requested_at Time.now }
end
end
diff --git a/spec/factories/label_priorities.rb b/spec/factories/label_priorities.rb
new file mode 100644
index 00000000000..f25939d2d3e
--- /dev/null
+++ b/spec/factories/label_priorities.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+ factory :label_priority do
+ project factory: :empty_project
+ label
+ sequence(:priority)
+ end
+end
diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb
index eb489099854..3e8822faf97 100644
--- a/spec/factories/labels.rb
+++ b/spec/factories/labels.rb
@@ -1,7 +1,23 @@
FactoryGirl.define do
- factory :label do
+ factory :label, class: ProjectLabel do
sequence(:title) { |n| "label#{n}" }
color "#990000"
project
+
+ transient do
+ priority nil
+ end
+
+ after(:create) do |label, evaluator|
+ if evaluator.priority
+ label.priorities.create(project: label.project, priority: evaluator.priority)
+ end
+ end
+ end
+
+ factory :group_label, class: GroupLabel do
+ sequence(:title) { |n| "label#{n}" }
+ color "#990000"
+ group
end
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index c6a08d78b78..f780e01253c 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -68,5 +68,15 @@ FactoryGirl.define do
factory :closed_merge_request, traits: [:closed]
factory :reopened_merge_request, traits: [:reopened]
factory :merge_request_with_diffs, traits: [:with_diffs]
+
+ factory :labeled_merge_request do
+ transient do
+ labels []
+ end
+
+ after(:create) do |merge_request, evaluator|
+ merge_request.update_attributes(labels: evaluator.labels)
+ end
+ end
end
end
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index 1ddb305a8af..c21927640d1 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -8,5 +8,6 @@ FactoryGirl.define do
trait(:reporter) { access_level ProjectMember::REPORTER }
trait(:developer) { access_level ProjectMember::DEVELOPER }
trait(:master) { access_level ProjectMember::MASTER }
+ trait(:access_request) { requested_at Time.now }
end
end
diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb
index 68ea4eeae31..b565586ee14 100644
--- a/spec/features/environments_spec.rb
+++ b/spec/features/environments_spec.rb
@@ -19,10 +19,22 @@ feature 'Environments', feature: true do
visit namespace_project_environments_path(project.namespace, project)
end
+ context 'shows two tabs' do
+ scenario 'shows "Available" and "Stopped" tab with links' do
+ expect(page).to have_link('Available')
+ expect(page).to have_link('Stopped')
+ end
+ end
+
context 'without environments' do
scenario 'does show no environments' do
expect(page).to have_content('You don\'t have any environments right now.')
end
+
+ scenario 'does show 0 as counter for environments in both tabs' do
+ expect(page.find('.js-available-environments-count').text).to eq('0')
+ expect(page.find('.js-stopped-environments-count').text).to eq('0')
+ end
end
context 'with environments' do
@@ -32,6 +44,11 @@ feature 'Environments', feature: true do
expect(page).to have_link(environment.name)
end
+ scenario 'does show number of available and stopped environments' do
+ expect(page.find('.js-available-environments-count').text).to eq('1')
+ expect(page.find('.js-stopped-environments-count').text).to eq('0')
+ end
+
context 'without deployments' do
scenario 'does show no deployments' do
expect(page).to have_content('No deployments yet')
@@ -44,7 +61,7 @@ feature 'Environments', feature: true do
scenario 'does show deployment SHA' do
expect(page).to have_link(deployment.short_sha)
end
-
+
scenario 'does show deployment internal id' do
expect(page).to have_content(deployment.iid)
end
@@ -65,20 +82,51 @@ feature 'Environments', feature: true do
expect(page).to have_content(manual.name)
expect(manual.reload).to be_pending
end
-
+
scenario 'does show build name and id' do
expect(page).to have_link("#{build.name} (##{build.id})")
end
-
+
+ scenario 'does not show stop button' do
+ expect(page).not_to have_selector('.stop-env-link')
+ end
+
+ scenario 'does not show external link button' do
+ expect(page).not_to have_css('external-url')
+ end
+
context 'with external_url' do
given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
given(:build) { create(:ci_build, pipeline: pipeline) }
given(:deployment) { create(:deployment, environment: environment, deployable: build) }
-
+
scenario 'does show an external link button' do
expect(page).to have_link(nil, href: environment.external_url)
end
end
+
+ context 'with stop action' do
+ given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
+ given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
+
+ scenario 'does show stop button' do
+ expect(page).to have_selector('.stop-env-link')
+ end
+
+ scenario 'starts build when stop button clicked' do
+ first('.stop-env-link').click
+
+ expect(page).to have_content('close_app')
+ end
+
+ context 'for reporter' do
+ let(:role) { :reporter }
+
+ scenario 'does not show stop button' do
+ expect(page).not_to have_selector('.stop-env-link')
+ end
+ end
+ end
end
end
end
@@ -127,6 +175,10 @@ feature 'Environments', feature: true do
expect(page).to have_link('Re-deploy')
end
+ scenario 'does not show stop button' do
+ expect(page).not_to have_link('Stop')
+ end
+
context 'with manual action' do
given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') }
@@ -140,16 +192,39 @@ feature 'Environments', feature: true do
expect(page).to have_content(manual.name)
expect(manual.reload).to be_pending
end
-
+
context 'with external_url' do
given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
given(:build) { create(:ci_build, pipeline: pipeline) }
given(:deployment) { create(:deployment, environment: environment, deployable: build) }
-
+
scenario 'does show an external link button' do
expect(page).to have_link(nil, href: environment.external_url)
end
end
+
+ context 'with stop action' do
+ given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
+ given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
+
+ scenario 'does show stop button' do
+ expect(page).to have_link('Stop')
+ end
+
+ scenario 'does allow to stop environment' do
+ click_link('Stop')
+
+ expect(page).to have_content('close_app')
+ end
+
+ context 'for reporter' do
+ let(:role) { :reporter }
+
+ scenario 'does not show stop button' do
+ expect(page).not_to have_link('Stop')
+ end
+ end
+ end
end
end
end
@@ -196,29 +271,4 @@ feature 'Environments', feature: true do
end
end
end
-
- describe 'when deleting existing environment' do
- given(:environment) { create(:environment, project: project) }
-
- before do
- visit namespace_project_environment_path(project.namespace, project, environment)
- end
-
- context 'when logged as master' do
- given(:role) { :master }
-
- scenario 'does delete environment' do
- click_link 'Destroy'
- expect(page).not_to have_link(environment.name)
- end
- end
-
- context 'when logged as developer' do
- given(:role) { :developer }
-
- scenario 'does not have a Destroy link' do
- expect(page).not_to have_link('Destroy')
- end
- end
- end
end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 79cc50bc18e..ef00f209998 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -1,6 +1,8 @@
require 'rails_helper'
describe 'Awards Emoji', feature: true do
+ include WaitForAjax
+
let!(:project) { create(:project) }
let!(:user) { create(:user) }
@@ -16,20 +18,22 @@ describe 'Awards Emoji', feature: true do
project: project)
end
+ let!(:note) { create(:note_on_issue, noteable: issue, project: issue.project, note: "Hello world") }
+
before do
visit namespace_project_issue_path(project.namespace, project, issue)
end
it 'increments the thumbsdown emoji', js: true do
find('[data-emoji="thumbsdown"]').click
- sleep 2
+ wait_for_ajax
expect(thumbsdown_emoji).to have_text("1")
end
context 'click the thumbsup emoji' do
it 'increments the thumbsup emoji', js: true do
find('[data-emoji="thumbsup"]').click
- sleep 2
+ wait_for_ajax
expect(thumbsup_emoji).to have_text("1")
end
@@ -41,7 +45,7 @@ describe 'Awards Emoji', feature: true do
context 'click the thumbsdown emoji' do
it 'increments the thumbsdown emoji', js: true do
find('[data-emoji="thumbsdown"]').click
- sleep 2
+ wait_for_ajax
expect(thumbsdown_emoji).to have_text("1")
end
@@ -49,13 +53,45 @@ describe 'Awards Emoji', feature: true do
expect(thumbsup_emoji).to have_text("0")
end
end
+
+ it 'toggles the smiley emoji on a note', js: true do
+ toggle_smiley_emoji(true)
+
+ within('.note-awards') do
+ expect(find(emoji_counter)).to have_text("1")
+ end
+
+ toggle_smiley_emoji(false)
+
+ within('.note-awards') do
+ expect(page).not_to have_selector(emoji_counter)
+ end
+ end
end
def thumbsup_emoji
- page.all('span.js-counter').first
+ page.all(emoji_counter).first
end
def thumbsdown_emoji
- page.all('span.js-counter').last
+ page.all(emoji_counter).last
+ end
+
+ def emoji_counter
+ 'span.js-counter'
+ end
+
+ def toggle_smiley_emoji(status)
+ within('.note') do
+ find('.note-emoji-button').click
+ end
+
+ unless status
+ first('[data-emoji="smiley"]').click
+ else
+ find('[data-emoji="smiley"]').click
+ end
+
+ wait_for_ajax
end
end
diff --git a/spec/features/issues/reset_filters_spec.rb b/spec/features/issues/reset_filters_spec.rb
index f4d0f13c3d5..c9a3ecf16ea 100644
--- a/spec/features/issues/reset_filters_spec.rb
+++ b/spec/features/issues/reset_filters_spec.rb
@@ -75,6 +75,14 @@ feature 'Issues filter reset button', feature: true, js: true do
end
end
+ context 'when no filters have been applied' do
+ it 'the reset link should not be visible' do
+ visit_issues(project)
+ expect(page).to have_css('.issue', count: 2)
+ expect(page).not_to have_css '.reset_filters'
+ end
+ end
+
def reset_filters
find('.reset-filters').click
end
diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
index bc2b0ff3e2c..c3c3ab33872 100644
--- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb
@@ -101,7 +101,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do
expect(page).not_to have_link "Merge When Build Succeeds"
end
end
-
+
def visit_merge_request(merge_request)
visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request)
end
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
index 8e23ec50d4a..6676821b807 100644
--- a/spec/features/merge_requests/widget_deployments_spec.rb
+++ b/spec/features/merge_requests/widget_deployments_spec.rb
@@ -4,23 +4,58 @@ feature 'Widget Deployments Header', feature: true, js: true do
include WaitForAjax
describe 'when deployed to an environment' do
- let(:project) { merge_request.target_project }
- let(:merge_request) { create(:merge_request, :merged) }
- let(:environment) { create(:environment, project: project) }
- let!(:deployment) do
- create(:deployment, environment: environment, sha: project.commit('master').id)
- end
+ given(:user) { create(:user) }
+ given(:project) { merge_request.target_project }
+ given(:merge_request) { create(:merge_request, :merged) }
+ given(:environment) { create(:environment, project: project) }
+ given(:role) { :developer }
+ given(:sha) { project.commit('master').id }
+ given!(:deployment) { create(:deployment, environment: environment, sha: sha) }
+ given!(:manual) { }
- before do
- login_as :admin
+ background do
+ login_as(user)
+ project.team << [user, role]
visit namespace_project_merge_request_path(project.namespace, project, merge_request)
end
- it 'displays that the environment is deployed' do
+ scenario 'displays that the environment is deployed' do
wait_for_ajax
expect(page).to have_content("Deployed to #{environment.name}")
expect(find('.ci_widget > span > span')['data-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
end
+
+ context 'with stop action' do
+ given(:pipeline) { create(:ci_pipeline, project: project) }
+ given(:build) { create(:ci_build, pipeline: pipeline) }
+ given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
+ given(:deployment) do
+ create(:deployment, environment: environment, ref: merge_request.target_branch,
+ sha: sha, deployable: build, on_stop: 'close_app')
+ end
+
+ background do
+ wait_for_ajax
+ end
+
+ scenario 'does show stop button' do
+ expect(page).to have_link('Stop environment')
+ end
+
+ scenario 'does start build when stop button clicked' do
+ click_link('Stop environment')
+
+ expect(page).to have_content('close_app')
+ end
+
+ context 'for reporter' do
+ given(:role) { :reporter }
+
+ scenario 'does not show stop button' do
+ expect(page).not_to have_link('Stop environment')
+ end
+ end
+ end
end
end
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index d04bdea0fe4..bfe59bdb90e 100644
--- a/spec/features/projects/import_export/test_project_export.tar.gz
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index cb7495da8eb..c9fa8315e79 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -3,18 +3,56 @@ require 'spec_helper'
feature 'Prioritize labels', feature: true do
include WaitForAjax
- context 'when project belongs to user' do
- let(:user) { create(:user) }
- let(:project) { create(:project, name: 'test', namespace: user.namespace) }
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let!(:bug) { create(:label, project: project, title: 'bug') }
+ let!(:wontfix) { create(:label, project: project, title: 'wontfix') }
+ let!(:feature) { create(:group_label, group: group, title: 'feature') }
- scenario 'user can prioritize a label', js: true do
- bug = create(:label, title: 'bug')
- wontfix = create(:label, title: 'wontfix')
-
- project.labels << bug
- project.labels << wontfix
+ context 'when user belongs to project team' do
+ before do
+ project.team << [user, :developer]
login_as user
+ end
+
+ scenario 'user can prioritize a group label', js: true do
+ visit namespace_project_labels_path(project.namespace, project)
+
+ expect(page).to have_content('No prioritized labels yet')
+
+ page.within('.other-labels') do
+ all('.js-toggle-priority')[1].click
+ wait_for_ajax
+ expect(page).not_to have_content('feature')
+ end
+
+ page.within('.prioritized-labels') do
+ expect(page).not_to have_content('No prioritized labels yet')
+ expect(page).to have_content('feature')
+ end
+ end
+
+ scenario 'user can unprioritize a group label', js: true do
+ create(:label_priority, project: project, label: feature, priority: 1)
+
+ visit namespace_project_labels_path(project.namespace, project)
+
+ page.within('.prioritized-labels') do
+ expect(page).to have_content('feature')
+
+ first('.js-toggle-priority').click
+ wait_for_ajax
+ expect(page).not_to have_content('bug')
+ end
+
+ page.within('.other-labels') do
+ expect(page).to have_content('feature')
+ end
+ end
+
+ scenario 'user can prioritize a project label', js: true do
visit namespace_project_labels_path(project.namespace, project)
expect(page).to have_content('No prioritized labels yet')
@@ -31,19 +69,14 @@ feature 'Prioritize labels', feature: true do
end
end
- scenario 'user can unprioritize a label', js: true do
- bug = create(:label, title: 'bug', priority: 1)
- wontfix = create(:label, title: 'wontfix')
-
- project.labels << bug
- project.labels << wontfix
+ scenario 'user can unprioritize a project label', js: true do
+ create(:label_priority, project: project, label: bug, priority: 1)
- login_as user
visit namespace_project_labels_path(project.namespace, project)
- expect(page).to have_content('bug')
-
page.within('.prioritized-labels') do
+ expect(page).to have_content('bug')
+
first('.js-toggle-priority').click
wait_for_ajax
expect(page).not_to have_content('bug')
@@ -56,23 +89,20 @@ feature 'Prioritize labels', feature: true do
end
scenario 'user can sort prioritized labels and persist across reloads', js: true do
- bug = create(:label, title: 'bug', priority: 1)
- wontfix = create(:label, title: 'wontfix', priority: 2)
-
- project.labels << bug
- project.labels << wontfix
+ create(:label_priority, project: project, label: bug, priority: 1)
+ create(:label_priority, project: project, label: feature, priority: 2)
- login_as user
visit namespace_project_labels_path(project.namespace, project)
expect(page).to have_content 'bug'
+ expect(page).to have_content 'feature'
expect(page).to have_content 'wontfix'
# Sort labels
- find("#label_#{bug.id}").drag_to find("#label_#{wontfix.id}")
+ find("#project_label_#{bug.id}").drag_to find("#group_label_#{feature.id}")
page.within('.prioritized-labels') do
- expect(first('li')).to have_content('wontfix')
+ expect(first('li')).to have_content('feature')
expect(page.all('li').last).to have_content('bug')
end
@@ -80,7 +110,7 @@ feature 'Prioritize labels', feature: true do
wait_for_ajax
page.within('.prioritized-labels') do
- expect(first('li')).to have_content('wontfix')
+ expect(first('li')).to have_content('feature')
expect(page.all('li').last).to have_content('bug')
end
end
@@ -88,28 +118,26 @@ feature 'Prioritize labels', feature: true do
context 'as a guest' do
it 'does not prioritize labels' do
- user = create(:user)
guest = create(:user)
- project = create(:project, name: 'test', namespace: user.namespace)
-
- create(:label, title: 'bug')
login_as guest
+
visit namespace_project_labels_path(project.namespace, project)
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content 'feature'
expect(page).not_to have_css('.prioritized-labels')
end
end
context 'as a non signed in user' do
it 'does not prioritize labels' do
- user = create(:user)
- project = create(:project, name: 'test', namespace: user.namespace)
-
- create(:label, title: 'bug')
-
visit namespace_project_labels_path(project.namespace, project)
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'wontfix'
+ expect(page).to have_content 'feature'
expect(page).not_to have_css('.prioritized-labels')
end
end
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
new file mode 100644
index 00000000000..27acc464ea2
--- /dev/null
+++ b/spec/finders/labels_finder_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe LabelsFinder do
+ describe '#execute' do
+ let(:group_1) { create(:group) }
+ let(:group_2) { create(:group) }
+ let(:group_3) { create(:group) }
+
+ let(:project_1) { create(:empty_project, namespace: group_1) }
+ let(:project_2) { create(:empty_project, namespace: group_2) }
+ let(:project_3) { create(:empty_project) }
+ let(:project_4) { create(:empty_project, :public) }
+ let(:project_5) { create(:empty_project, namespace: group_1) }
+
+ let!(:project_label_1) { create(:label, project: project_1, title: 'Label 1') }
+ let!(:project_label_2) { create(:label, project: project_2, title: 'Label 2') }
+ let!(:project_label_4) { create(:label, project: project_4, title: 'Label 4') }
+ let!(:project_label_5) { create(:label, project: project_5, title: 'Label 5') }
+
+ let!(:group_label_1) { create(:group_label, group: group_1, title: 'Label 1') }
+ let!(:group_label_2) { create(:group_label, group: group_1, title: 'Group Label 2') }
+ let!(:group_label_3) { create(:group_label, group: group_2, title: 'Group Label 3') }
+
+ let(:user) { create(:user) }
+
+ before do
+ create(:label, project: project_3, title: 'Label 3')
+ create(:group_label, group: group_3, title: 'Group Label 4')
+
+ project_1.team << [user, :developer]
+ end
+
+ context 'with no filter' do
+ it 'returns labels from projects the user have access' do
+ group_2.add_developer(user)
+
+ finder = described_class.new(user)
+
+ expect(finder.execute).to eq [group_label_2, group_label_3, project_label_1, group_label_1, project_label_2, project_label_4]
+ end
+ end
+
+ context 'filtering by group_id' do
+ it 'returns labels available for any project within the group' do
+ group_1.add_developer(user)
+
+ finder = described_class.new(user, group_id: group_1.id)
+
+ expect(finder.execute).to eq [group_label_2, project_label_1, group_label_1, project_label_5]
+ end
+ end
+
+ context 'filtering by project_id' do
+ it 'returns labels available for the project' do
+ finder = described_class.new(user, project_id: project_1.id)
+
+ expect(finder.execute).to eq [group_label_2, project_label_1, group_label_1]
+ end
+ end
+
+ context 'filtering by title' do
+ it 'returns label with that title' do
+ finder = described_class.new(user, title: 'Group Label 2')
+
+ expect(finder.execute).to eq [group_label_2]
+ end
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json
index f070fa3b254..8d94cf26ecb 100644
--- a/spec/fixtures/api/schemas/list.json
+++ b/spec/fixtures/api/schemas/list.json
@@ -13,7 +13,7 @@
"enum": ["backlog", "label", "done"]
},
"label": {
- "type": ["object"],
+ "type": ["object", "null"],
"required": [
"id",
"color",
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 501f150cfda..d30daf47543 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -5,27 +5,26 @@ describe LabelsHelper do
let(:project) { create(:empty_project) }
let(:label) { create(:label, project: project) }
- context 'with @project set' do
- before do
- @project = project
- end
-
- it 'uses the instance variable' do
- expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name%5B%5D=#{label.name}"><span class="[\w\s\-]*has-tooltip".*</span></a>}
+ context 'without subject' do
+ it "uses the label's project" do
+ expect(link_to_label(label)).to match %r{<a href="/#{label.project.to_reference}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
- context 'without @project set' do
- it "uses the label's project" do
- expect(link_to_label(label)).to match %r{<a href="/#{label.project.to_reference}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ context 'with a project as subject' do
+ let(:namespace) { build(:namespace, name: 'foo3') }
+ let(:another_project) { build(:empty_project, namespace: namespace, name: 'bar3') }
+
+ it 'links to project issues page' do
+ expect(link_to_label(label, subject: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
- context 'with a project argument' do
- let(:another_project) { double('project', namespace: 'foo3', to_param: 'bar3') }
+ context 'with a group as subject' do
+ let(:group) { build(:group, name: 'bar') }
- it 'links to merge requests page' do
- expect(link_to_label(label, project: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ it 'links to group issues page' do
+ expect(link_to_label(label, subject: group)).to match %r{<a href="/groups/bar/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index 7116c09fb21..2f9343fadaf 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -7,12 +7,7 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
IssuesHelper
end
- let(:project) { create(:jira_project) }
-
- context 'JIRA issue references' do
- let(:issue) { ExternalIssue.new('JIRA-123', project) }
- let(:reference) { issue.to_reference }
-
+ shared_examples_for "external issue tracker" do
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
@@ -20,6 +15,7 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
%w(pre code a style).each do |elem|
it "ignores valid references contained inside '#{elem}' element" do
exp = act = "<#{elem}>Issue #{reference}</#{elem}>"
+
expect(filter(act).to_html).to eq exp
end
end
@@ -33,25 +29,30 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
it 'links to a valid reference' do
doc = filter("Issue #{reference}")
+ issue_id = doc.css('a').first.attr("data-external-issue")
+
expect(doc.css('a').first.attr('href'))
- .to eq helper.url_for_issue(reference, project)
+ .to eq helper.url_for_issue(issue_id, project)
end
it 'links to the external tracker' do
doc = filter("Issue #{reference}")
+
link = doc.css('a').first.attr('href')
+ issue_id = doc.css('a').first.attr("data-external-issue")
- expect(link).to eq "http://jira.example/browse/#{reference}"
+ expect(link).to eq(helper.url_for_issue(issue_id, project))
end
it 'links with adjacent text' do
doc = filter("Issue (#{reference}.)")
+
expect(doc.to_html).to match(/\(<a.+>#{reference}<\/a>\.\)/)
end
it 'includes a title attribute' do
doc = filter("Issue #{reference}")
- expect(doc.css('a').first.attr('title')).to eq "Issue in JIRA tracker"
+ expect(doc.css('a').first.attr('title')).to include("Issue in #{project.issues_tracker.title}")
end
it 'escapes the title attribute' do
@@ -69,9 +70,60 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
it 'supports an :only_path context' do
doc = filter("Issue #{reference}", only_path: true)
+
link = doc.css('a').first.attr('href')
+ issue_id = doc.css('a').first["data-external-issue"]
+
+ expect(link).to eq helper.url_for_issue(issue_id, project, only_path: true)
+ end
+
+ context 'with RequestStore enabled' do
+ let(:reference_filter) { HTML::Pipeline.new([described_class]) }
+
+ before { allow(RequestStore).to receive(:active?).and_return(true) }
+
+ it 'queries the collection on the first call' do
+ expect_any_instance_of(Project).to receive(:default_issues_tracker?).once.and_call_original
+ expect_any_instance_of(Project).to receive(:issue_reference_pattern).once.and_call_original
+
+ not_cached = reference_filter.call("look for #{reference}", { project: project })
+
+ expect_any_instance_of(Project).not_to receive(:default_issues_tracker?)
+ expect_any_instance_of(Project).not_to receive(:issue_reference_pattern)
+
+ cached = reference_filter.call("look for #{reference}", { project: project })
- expect(link).to eq helper.url_for_issue("#{reference}", project, only_path: true)
+ # Links must be the same
+ expect(cached[:output].css('a').first[:href]).to eq(not_cached[:output].css('a').first[:href])
+ end
+ end
+ end
+
+ context "redmine project" do
+ let(:project) { create(:redmine_project) }
+ let(:issue) { ExternalIssue.new("#123", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
+
+ context "jira project" do
+ let(:project) { create(:jira_project) }
+ let(:reference) { issue.to_reference }
+
+ context "with right markdown" do
+ let(:issue) { ExternalIssue.new("JIRA-123", project) }
+
+ it_behaves_like "external issue tracker"
+ end
+
+ context "with wrong markdown" do
+ let(:issue) { ExternalIssue.new("#123", project) }
+
+ it "ignores reference" do
+ exp = act = "Issue #{reference}"
+ expect(filter(act).to_html).to eq exp
+ end
end
end
end
diff --git a/spec/lib/banzai/filter/html_entity_filter_spec.rb b/spec/lib/banzai/filter/html_entity_filter_spec.rb
index 4c68ce6d6e4..f9e6bd609f0 100644
--- a/spec/lib/banzai/filter/html_entity_filter_spec.rb
+++ b/spec/lib/banzai/filter/html_entity_filter_spec.rb
@@ -11,4 +11,9 @@ describe Banzai::Filter::HtmlEntityFilter, lib: true do
expect(output).to eq(escaped)
end
+
+ it 'does not double-escape' do
+ escaped = ERB::Util.html_escape("Merge branch 'blabla' into 'master'")
+ expect(filter(escaped)).to eq(escaped)
+ end
end
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index fce86a9b6ad..a2025672ad9 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -25,9 +25,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
let(:reference) { issue.to_reference }
it 'ignores valid references when using non-default tracker' do
- expect_any_instance_of(described_class).to receive(:find_object).
- with(project, issue.iid).
- and_return(nil)
+ allow(project).to receive(:default_issues_tracker?).and_return(false)
exp = act = "Issue #{reference}"
expect(reference_filter(act).to_html).to eq exp
@@ -199,19 +197,6 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
end
end
- context 'referencing external issues' do
- let(:project) { create(:redmine_project) }
-
- it 'renders internal issue IDs as external issue links' do
- doc = reference_filter('#1')
- link = doc.css('a').first
-
- expect(link.attr('data-reference-type')).to eq('external_issue')
- expect(link.attr('title')).to eq('Issue in Redmine')
- expect(link.attr('data-external-issue')).to eq('1')
- end
- end
-
describe '#issues_per_Project' do
context 'using an internal issue tracker' do
it 'returns a Hash containing the issues per project' do
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 908ccebbf87..9c09f00ae8a 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -305,6 +305,58 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
end
+ describe 'group label references' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:group_label) { create(:group_label, name: 'gfm references', group: group) }
+
+ context 'without project reference' do
+ let(:reference) { group_label.to_reference(format: :name) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}", project: project)
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
+ expect(doc.text).to eq 'See gfm references'
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Label (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>#{group_label.name}</span></a>\.\)))
+ end
+
+ it 'ignores invalid label names' do
+ exp = act = %(Label #{Label.reference_prefix}"#{group_label.name.reverse}")
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'with project reference' do
+ let(:reference) { project.to_reference + group_label.to_reference(format: :name) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}", project: project)
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
+ expect(doc.text).to eq 'See gfm references'
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Label (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+><span.+>#{group_label.name}</span></a>\.\)))
+ end
+
+ it 'ignores invalid label names' do
+ exp = act = %(Label #{project.to_reference}#{Label.reference_prefix}"#{group_label.name.reverse}")
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+ end
+
describe 'cross project label references' do
context 'valid project referenced' do
let(:another_project) { create(:empty_project, :public) }
@@ -339,4 +391,34 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
end
end
+
+ describe 'cross group label references' do
+ context 'valid project referenced' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:another_group) { create(:group) }
+ let(:another_project) { create(:empty_project, :public, namespace: another_group) }
+ let(:project_name) { another_project.name_with_namespace }
+ let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') }
+ let(:reference) { another_project.to_reference + group_label.to_reference }
+
+ let!(:result) { reference_filter("See #{reference}", project: project) }
+
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(another_project.namespace,
+ another_project,
+ label_name: group_label.name)
+ end
+
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style'))
+ .to match /background-color: #00ff00/
+ end
+
+ it 'contains cross project content' do
+ expect(result.css('a').first.text).to eq "#{group_label.name} in #{project_name}"
+ end
+ end
+ end
end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 6dedd25e9d3..84f21631719 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -754,7 +754,7 @@ module Ci
it 'does return production' do
expect(builds.size).to eq(1)
expect(builds.first[:environment]).to eq(environment)
- expect(builds.first[:options]).to include(environment: { name: environment })
+ expect(builds.first[:options]).to include(environment: { name: environment, action: "start" })
end
end
@@ -796,6 +796,52 @@ module Ci
expect { builds }.to raise_error("jobs:deploy_to_production:environment name #{Gitlab::Regex.environment_name_regex_message}")
end
end
+
+ context 'when on_stop is specified' do
+ let(:review) { { stage: 'deploy', script: 'test', environment: { name: 'review', on_stop: 'close_review' } } }
+ let(:config) { { review: review, close_review: close_review }.compact }
+
+ context 'with matching job' do
+ let(:close_review) { { stage: 'deploy', script: 'test', environment: { name: 'review', action: 'stop' } } }
+
+ it 'does return a list of builds' do
+ expect(builds.size).to eq(2)
+ expect(builds.first[:environment]).to eq('review')
+ end
+ end
+
+ context 'without matching job' do
+ let(:close_review) { nil }
+
+ it 'raises error' do
+ expect { builds }.to raise_error('review job: on_stop job close_review is not defined')
+ end
+ end
+
+ context 'with close job without environment' do
+ let(:close_review) { { stage: 'deploy', script: 'test' } }
+
+ it 'raises error' do
+ expect { builds }.to raise_error('review job: on_stop job close_review does not have environment defined')
+ end
+ end
+
+ context 'with close job for different environment' do
+ let(:close_review) { { stage: 'deploy', script: 'test', environment: 'production' } }
+
+ it 'raises error' do
+ expect { builds }.to raise_error('review job: on_stop job close_review have different environment name')
+ end
+ end
+
+ context 'with close job without stop action' do
+ let(:close_review) { { stage: 'deploy', script: 'test', environment: { name: 'review' } } }
+
+ it 'raises error' do
+ expect { builds }.to raise_error('review job: on_stop job close_review needs to have action stop defined')
+ end
+ end
+ end
end
describe "Dependencies" do
diff --git a/spec/lib/gitlab/ci/config/node/environment_spec.rb b/spec/lib/gitlab/ci/config/node/environment_spec.rb
index df453223da7..df925ff1afd 100644
--- a/spec/lib/gitlab/ci/config/node/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/node/environment_spec.rb
@@ -28,7 +28,7 @@ describe Gitlab::Ci::Config::Node::Environment do
describe '#value' do
it 'returns valid hash' do
- expect(entry.value).to eq(name: 'production')
+ expect(entry.value).to include(name: 'production')
end
end
@@ -87,6 +87,68 @@ describe Gitlab::Ci::Config::Node::Environment do
end
end
+ context 'when valid action is used' do
+ let(:config) do
+ { name: 'production',
+ action: 'start' }
+ end
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'when invalid action is used' do
+ let(:config) do
+ { name: 'production',
+ action: 'invalid' }
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors' do
+ it 'contains error about invalid action' do
+ expect(entry.errors)
+ .to include 'environment action should be start or stop'
+ end
+ end
+ end
+
+ context 'when on_stop is used' do
+ let(:config) do
+ { name: 'production',
+ on_stop: 'close_app' }
+ end
+
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'when invalid on_stop is used' do
+ let(:config) do
+ { name: 'production',
+ on_stop: false }
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors' do
+ it 'contains error about invalid action' do
+ expect(entry.errors)
+ .to include 'environment on stop should be a string'
+ end
+ end
+ end
+
context 'when variables are used for environment' do
let(:config) do
{ name: 'review/$CI_BUILD_REF_NAME',
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 0af249d8690..f045463c1cb 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -2,8 +2,8 @@ require 'spec_helper'
describe Gitlab::Gfm::ReferenceRewriter do
let(:text) { 'some text' }
- let(:old_project) { create(:project) }
- let(:new_project) { create(:project) }
+ let(:old_project) { create(:project, name: 'old') }
+ let(:new_project) { create(:project, name: 'new') }
let(:user) { create(:user) }
before { old_project.team << [user, :guest] }
@@ -62,7 +62,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
it { is_expected.to eq "#{ref}, `#1`, #{ref}, `#1`" }
end
- context 'description with labels' do
+ context 'description with project labels' do
let!(:label) { create(:label, id: 123, name: 'test', project: old_project) }
let(:project_ref) { old_project.to_reference }
@@ -76,6 +76,26 @@ describe Gitlab::Gfm::ReferenceRewriter do
it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} }
end
end
+
+ context 'description with group labels' do
+ let(:old_group) { create(:group) }
+ let!(:group_label) { create(:group_label, id: 321, name: 'group label', group: old_group) }
+ let(:project_ref) { old_project.to_reference }
+
+ before do
+ old_project.update(namespace: old_group)
+ end
+
+ context 'label referenced by id' do
+ let(:text) { '#1 and ~321' }
+ it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~321} }
+ end
+
+ context 'label referenced by text' do
+ let(:text) { '#1 and ~"group label"' }
+ it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~321} }
+ end
+ end
end
context 'reference contains milestone' do
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index de68e32e5b4..a5aa387f4f7 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -185,6 +185,7 @@ describe Gitlab::GitAccess, lib: true do
end
end
+ # Run permission checks for a user
def self.run_permission_checks(permissions_matrix)
permissions_matrix.keys.each do |role|
describe "#{role} access" do
@@ -194,13 +195,12 @@ describe Gitlab::GitAccess, lib: true do
else
project.team << [user, role]
end
- end
-
- permissions_matrix[role].each do |action, allowed|
- context action do
- subject { access.push_access_check(changes[action]) }
- it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
+ permissions_matrix[role].each do |action, allowed|
+ context action do
+ subject { access.push_access_check(changes[action]) }
+ it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
+ end
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb
index 8854c8431b5..1af553f8f03 100644
--- a/spec/lib/gitlab/github_import/importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer_spec.rb
@@ -157,7 +157,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
{ type: :pull_request, url: "https://api.github.com/repos/octocat/Hello-World/pulls/1347", errors: "Validation failed: Validate branches Cannot Create: This merge request already exists: [\"New feature\"]" },
{ type: :wiki, errors: "Gitlab::Shell::Error" },
{ type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" }
- ]
+ ]
}
described_class.new(project).execute
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 54f85f8cffc..097861fd34d 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -15,6 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer, lib: true do
subject { described_class.new(project) }
before do
+ project.team << [project.creator, :master]
project.create_import_data(data: import_data)
end
@@ -31,9 +32,9 @@ describe Gitlab::GoogleCodeImport::Importer, lib: true do
subject.execute
%w(
- Type-Defect Type-Enhancement Type-Task Type-Review Type-Other Milestone-0.12 Priority-Critical
- Priority-High Priority-Medium Priority-Low OpSys-All OpSys-Windows OpSys-Linux OpSys-OSX Security
- Performance Usability Maintainability Component-Panel Component-Taskbar Component-Battery
+ Type-Defect Type-Enhancement Type-Task Type-Review Type-Other Milestone-0.12 Priority-Critical
+ Priority-High Priority-Medium Priority-Low OpSys-All OpSys-Windows OpSys-Linux OpSys-OSX Security
+ Performance Usability Maintainability Component-Panel Component-Taskbar Component-Battery
Component-Systray Component-Clock Component-Launcher Component-Tint2conf Component-Docs Component-New
).each do |label|
label.sub!("-", ": ")
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 8fcbf12eab8..02b11bd999a 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -38,6 +38,7 @@ label:
- label_links
- issues
- merge_requests
+- priorities
milestone:
- project
- issues
@@ -186,3 +187,5 @@ project:
award_emoji:
- awardable
- user
+priorities:
+- label \ No newline at end of file
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 98323fe6be4..ed9df468ced 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2,6 +2,21 @@
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
"visibility_level": 10,
"archived": false,
+ "labels": [
+ {
+ "id": 2,
+ "title": "test2",
+ "color": "#428bca",
+ "project_id": 8,
+ "created_at": "2016-07-22T08:55:44.161Z",
+ "updated_at": "2016-07-22T08:55:44.161Z",
+ "template": false,
+ "description": "",
+ "type": "ProjectLabel",
+ "priorities": [
+ ]
+ }
+ ],
"issues": [
{
"id": 40,
@@ -64,7 +79,37 @@
"updated_at": "2016-07-22T08:55:44.161Z",
"template": false,
"description": "",
- "priority": null
+ "type": "ProjectLabel"
+ }
+ },
+ {
+ "id": 3,
+ "label_id": 3,
+ "target_id": 40,
+ "target_type": "Issue",
+ "created_at": "2016-07-22T08:57:02.841Z",
+ "updated_at": "2016-07-22T08:57:02.841Z",
+ "label": {
+ "id": 3,
+ "title": "test3",
+ "color": "#428bca",
+ "group_id": 8,
+ "created_at": "2016-07-22T08:55:44.161Z",
+ "updated_at": "2016-07-22T08:55:44.161Z",
+ "template": false,
+ "description": "",
+ "project_id": null,
+ "type": "GroupLabel",
+ "priorities": [
+ {
+ "id": 1,
+ "project_id": 5,
+ "label_id": 1,
+ "priority": 1,
+ "created_at": "2016-10-18T09:35:43.338Z",
+ "updated_at": "2016-10-18T09:35:43.338Z"
+ }
+ ]
}
}
],
@@ -536,7 +581,7 @@
"updated_at": "2016-07-22T08:55:44.161Z",
"template": false,
"description": "",
- "priority": null
+ "type": "ProjectLabel"
}
}
],
@@ -2227,9 +2272,6 @@
]
}
],
- "labels": [
-
- ],
"milestones": [
{
"id": 1,
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 7582a732cdf..069ea960321 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -32,7 +32,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
it 'has the same label associated to two issues' do
restored_project_json
- expect(Label.first.issues.count).to eq(2)
+ expect(ProjectLabel.find_by_title('test2').issues.count).to eq(2)
end
it 'has milestones associated to two separate issues' do
@@ -107,6 +107,41 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(Label.first.label_links.first.target).not_to be_nil
end
+ it 'has project labels' do
+ restored_project_json
+
+ expect(ProjectLabel.count).to eq(2)
+ end
+
+ it 'has no group labels' do
+ restored_project_json
+
+ expect(GroupLabel.count).to eq(0)
+ end
+
+ context 'with group' do
+ let!(:project) do
+ create(:empty_project,
+ name: 'project',
+ path: 'project',
+ builds_access_level: ProjectFeature::DISABLED,
+ issues_access_level: ProjectFeature::DISABLED,
+ group: create(:group))
+ end
+
+ it 'has group labels' do
+ restored_project_json
+
+ expect(GroupLabel.count).to eq(1)
+ end
+
+ it 'has label priorities' do
+ restored_project_json
+
+ expect(GroupLabel.first.priorities).not_to be_empty
+ end
+ end
+
it 'has a project feature' do
restored_project_json
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index cf8f2200c57..c8bba553558 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -111,6 +111,18 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(saved_project_json['issues'].first['label_links'].first['label']).not_to be_empty
end
+ it 'has project and group labels' do
+ label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type']}
+
+ expect(label_types).to match_array(['ProjectLabel', 'GroupLabel'])
+ end
+
+ it 'has priorities associated to labels' do
+ priorities = saved_project_json['issues'].first['label_links'].map { |link| link['label']['priorities']}
+
+ expect(priorities.flatten).not_to be_empty
+ end
+
it 'saves the correct service type' do
expect(saved_project_json['services'].first['type']).to eq('CustomIssueTrackerService')
end
@@ -135,15 +147,20 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
issue = create(:issue, assignee: user)
snippet = create(:project_snippet)
release = create(:release)
+ group = create(:group)
project = create(:project,
:public,
issues: [issue],
snippets: [snippet],
- releases: [release]
+ releases: [release],
+ group: group
)
- label = create(:label, project: project)
- create(:label_link, label: label, target: issue)
+ project_label = create(:label, project: project)
+ group_label = create(:group_label, group: group)
+ create(:label_link, label: project_label, target: issue)
+ create(:label_link, label: group_label, target: issue)
+ create(:label_priority, label: group_label, priority: 1)
milestone = create(:milestone, project: project)
merge_request = create(:merge_request, source_project: project, milestone: milestone)
commit_status = create(:commit_status, project: project)
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 8c8be66df9f..feee0f025d8 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -60,11 +60,13 @@ LabelLink:
- target_type
- created_at
- updated_at
-Label:
+ProjectLabel:
- id
- title
- color
+- group_id
- project_id
+- type
- created_at
- updated_at
- template
@@ -329,3 +331,10 @@ AwardEmoji:
- awardable_type
- created_at
- updated_at
+LabelPriority:
+- id
+- project_id
+- label_id
+- priority
+- created_at
+- updated_at \ No newline at end of file
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index 01a4a53a264..ca594a320c0 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -48,4 +48,50 @@ describe Deployment, models: true do
end
end
end
+
+ describe '#stop_action' do
+ let(:build) { create(:ci_build) }
+
+ subject { deployment.stop_action }
+
+ context 'when no other actions' do
+ let(:deployment) { FactoryGirl.build(:deployment, deployable: build) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'with other actions' do
+ let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+
+ context 'when matching action is defined' do
+ let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_other_app') }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when no matching action is defined' do
+ let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_app') }
+
+ it { is_expected.to eq(close_action) }
+ end
+ end
+ end
+
+ describe '#stoppable?' do
+ subject { deployment.stoppable? }
+
+ context 'when no other actions' do
+ let(:deployment) { build(:deployment) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when matching action is defined' do
+ let(:build) { create(:ci_build) }
+ let(:deployment) { FactoryGirl.build(:deployment, deployable: build, on_stop: 'close_app') }
+ let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+
+ it { is_expected.to be_truthy }
+ end
+ end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index e172ee8e590..a94e6d0165f 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -8,6 +8,8 @@ describe Environment, models: true do
it { is_expected.to delegate_method(:last_deployment).to(:deployments).as(:last) }
+ it { is_expected.to delegate_method(:stop_action).to(:last_deployment) }
+
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
it { is_expected.to validate_length_of(:name).is_within(0..255) }
@@ -96,4 +98,72 @@ describe Environment, models: true do
is_expected.to be_nil
end
end
+
+ describe '#stoppable?' do
+ subject { environment.stoppable? }
+
+ context 'when no other actions' do
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when matching action is defined' do
+ let(:build) { create(:ci_build) }
+ let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
+ let!(:close_action) { create(:ci_build, pipeline: build.pipeline, name: 'close_app', when: :manual) }
+
+ context 'when environment is available' do
+ before do
+ environment.start
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when environment is stopped' do
+ before do
+ environment.stop
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe '#stop!' do
+ let(:user) { create(:user) }
+
+ subject { environment.stop!(user) }
+
+ before do
+ expect(environment).to receive(:stoppable?).and_call_original
+ end
+
+ context 'when no other actions' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when matching action is defined' do
+ let(:build) { create(:ci_build) }
+ let!(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
+
+ context 'when action did not yet finish' do
+ let!(:close_action) { create(:ci_build, :manual, pipeline: build.pipeline, name: 'close_app') }
+
+ it 'returns the same action' do
+ expect(subject).to eq(close_action)
+ expect(subject.user).to eq(user)
+ end
+ end
+
+ context 'if action did finish' do
+ let!(:close_action) { create(:ci_build, :manual, :success, pipeline: build.pipeline, name: 'close_app') }
+
+ it 'returns a new action of the same type' do
+ is_expected.to be_persisted
+ expect(subject.name).to eq(close_action.name)
+ expect(subject.user).to eq(user)
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb
index 4fc3b065592..ebba6e14578 100644
--- a/spec/models/external_issue_spec.rb
+++ b/spec/models/external_issue_spec.rb
@@ -10,21 +10,6 @@ describe ExternalIssue, models: true do
it { is_expected.to include_module(Referable) }
end
- describe '.reference_pattern' do
- it 'allows underscores in the project name' do
- expect(ExternalIssue.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
- end
-
- it 'allows numbers in the project name' do
- expect(ExternalIssue.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234'
- end
-
- it 'requires the project name to begin with A-Z' do
- expect(ExternalIssue.reference_pattern.match('3EXT_EXT-1234')).to eq nil
- expect(ExternalIssue.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
- end
- end
-
describe '#to_reference' do
it 'returns a String reference to the object' do
expect(issue.to_reference).to eq issue.id
diff --git a/spec/models/group_label_spec.rb b/spec/models/group_label_spec.rb
new file mode 100644
index 00000000000..85eb889225b
--- /dev/null
+++ b/spec/models/group_label_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe GroupLabel, models: true do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:group) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:group) }
+ end
+
+ describe '#subject' do
+ it 'aliases group to subject' do
+ subject = described_class.new(group: build(:group))
+
+ expect(subject.subject).to be(subject.group)
+ end
+ end
+
+ describe '#to_reference' do
+ let(:label) { create(:group_label) }
+
+ context 'using id' do
+ it 'returns a String reference to the object' do
+ expect(label.to_reference).to eq "~#{label.id}"
+ end
+ end
+
+ context 'using name' do
+ it 'returns a String reference to the object' do
+ expect(label.to_reference(format: :name)).to eq %(~"#{label.name}")
+ end
+
+ it 'uses id when name contains double quote' do
+ label = create(:label, name: %q{"irony"})
+ expect(label.to_reference(format: :name)).to eq "~#{label.id}"
+ end
+ end
+
+ context 'using invalid format' do
+ it 'raises error' do
+ expect { label.to_reference(format: :invalid) }
+ .to raise_error StandardError, /Unknown format/
+ end
+ end
+ end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 0b3ef9b98fd..ac862055ebc 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -12,6 +12,7 @@ describe Group, models: true do
it { is_expected.to have_many(:project_group_links).dependent(:destroy) }
it { is_expected.to have_many(:shared_projects).through(:project_group_links) }
it { is_expected.to have_many(:notification_settings).dependent(:destroy) }
+ it { is_expected.to have_many(:labels).class_name('GroupLabel') }
describe '#members & #requesters' do
let(:requester) { create(:user) }
diff --git a/spec/models/label_priority_spec.rb b/spec/models/label_priority_spec.rb
new file mode 100644
index 00000000000..d18c2f7949a
--- /dev/null
+++ b/spec/models/label_priority_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe LabelPriority, models: true do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:label) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:label) }
+ it { is_expected.to validate_numericality_of(:priority).only_integer.is_greater_than_or_equal_to(0) }
+
+ it 'validates uniqueness of label_id scoped to project_id' do
+ create(:label_priority)
+
+ expect(subject).to validate_uniqueness_of(:label_id).scoped_to(:project_id)
+ end
+ end
+end
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 5a5d1a5d60c..0c163659a71 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -1,46 +1,42 @@
require 'spec_helper'
describe Label, models: true do
- let(:label) { create(:label) }
+ describe 'modules' do
+ it { is_expected.to include_module(Referable) }
+ it { is_expected.to include_module(Subscribable) }
+ end
describe 'associations' do
- it { is_expected.to belong_to(:project) }
-
- it { is_expected.to have_many(:label_links).dependent(:destroy) }
it { is_expected.to have_many(:issues).through(:label_links).source(:target) }
+ it { is_expected.to have_many(:label_links).dependent(:destroy) }
it { is_expected.to have_many(:lists).dependent(:destroy) }
- end
-
- describe 'modules' do
- subject { described_class }
-
- it { is_expected.to include_module(Referable) }
+ it { is_expected.to have_many(:priorities).class_name('LabelPriority') }
end
describe 'validation' do
- it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_uniqueness_of(:title).scoped_to([:group_id, :project_id]) }
it 'validates color code' do
- expect(label).not_to allow_value('G-ITLAB').for(:color)
- expect(label).not_to allow_value('AABBCC').for(:color)
- expect(label).not_to allow_value('#AABBCCEE').for(:color)
- expect(label).not_to allow_value('GGHHII').for(:color)
- expect(label).not_to allow_value('#').for(:color)
- expect(label).not_to allow_value('').for(:color)
-
- expect(label).to allow_value('#AABBCC').for(:color)
- expect(label).to allow_value('#abcdef').for(:color)
+ is_expected.not_to allow_value('G-ITLAB').for(:color)
+ is_expected.not_to allow_value('AABBCC').for(:color)
+ is_expected.not_to allow_value('#AABBCCEE').for(:color)
+ is_expected.not_to allow_value('GGHHII').for(:color)
+ is_expected.not_to allow_value('#').for(:color)
+ is_expected.not_to allow_value('').for(:color)
+
+ is_expected.to allow_value('#AABBCC').for(:color)
+ is_expected.to allow_value('#abcdef').for(:color)
end
it 'validates title' do
- expect(label).not_to allow_value('G,ITLAB').for(:title)
- expect(label).not_to allow_value('').for(:title)
-
- expect(label).to allow_value('GITLAB').for(:title)
- expect(label).to allow_value('gitlab').for(:title)
- expect(label).to allow_value('G?ITLAB').for(:title)
- expect(label).to allow_value('G&ITLAB').for(:title)
- expect(label).to allow_value("customer's request").for(:title)
+ is_expected.not_to allow_value('G,ITLAB').for(:title)
+ is_expected.not_to allow_value('').for(:title)
+
+ is_expected.to allow_value('GITLAB').for(:title)
+ is_expected.to allow_value('gitlab').for(:title)
+ is_expected.to allow_value('G?ITLAB').for(:title)
+ is_expected.to allow_value('G&ITLAB').for(:title)
+ is_expected.to allow_value("customer's request").for(:title)
end
end
@@ -51,45 +47,59 @@ describe Label, models: true do
end
end
- describe '#to_reference' do
- context 'using id' do
- it 'returns a String reference to the object' do
- expect(label.to_reference).to eq "~#{label.id}"
- end
- end
+ describe 'priorization' do
+ subject(:label) { create(:label) }
- context 'using name' do
- it 'returns a String reference to the object' do
- expect(label.to_reference(format: :name)).to eq %(~"#{label.name}")
+ let(:project) { label.project }
+
+ describe '#prioritize!' do
+ context 'when label is not prioritized' do
+ it 'creates a label priority' do
+ expect { label.prioritize!(project, 1) }.to change(label.priorities, :count).by(1)
+ end
+
+ it 'sets label priority' do
+ label.prioritize!(project, 1)
+
+ expect(label.priorities.first.priority).to eq 1
+ end
end
- it 'uses id when name contains double quote' do
- label = create(:label, name: %q{"irony"})
- expect(label.to_reference(format: :name)).to eq "~#{label.id}"
+ context 'when label is prioritized' do
+ let!(:priority) { create(:label_priority, project: project, label: label, priority: 0) }
+
+ it 'does not create a label priority' do
+ expect { label.prioritize!(project, 1) }.not_to change(label.priorities, :count)
+ end
+
+ it 'updates label priority' do
+ label.prioritize!(project, 1)
+
+ expect(priority.reload.priority).to eq 1
+ end
end
end
- context 'using invalid format' do
- it 'raises error' do
- expect { label.to_reference(format: :invalid) }
- .to raise_error StandardError, /Unknown format/
+ describe '#unprioritize!' do
+ it 'removes label priority' do
+ create(:label_priority, project: project, label: label, priority: 0)
+
+ expect { label.unprioritize!(project) }.to change(label.priorities, :count).by(-1)
end
end
- context 'cross project reference' do
- let(:project) { create(:project) }
-
- context 'using name' do
- it 'returns cross reference with label name' do
- expect(label.to_reference(project, format: :name))
- .to eq %Q(#{label.project.to_reference}~"#{label.name}")
+ describe '#priority' do
+ context 'when label is not prioritized' do
+ it 'returns nil' do
+ expect(label.priority(project)).to be_nil
end
end
- context 'using id' do
- it 'returns cross reference with label id' do
- expect(label.to_reference(project, format: :id))
- .to eq %Q(#{label.project.to_reference}~#{label.id})
+ context 'when label is prioritized' do
+ it 'returns label priority' do
+ create(:label_priority, project: project, label: label, priority: 1)
+
+ expect(label.priority(project)).to eq 1
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 1acc8d748af..6db5e7f7d80 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -640,32 +640,56 @@ describe MergeRequest, models: true do
end
describe '#all_commits_sha' do
- let(:all_commits_sha) do
- subject.merge_request_diffs.flat_map(&:commits).map(&:sha).uniq
- end
+ context 'when merge request is persisted' do
+ let(:all_commits_sha) do
+ subject.merge_request_diffs.flat_map(&:commits).map(&:sha).uniq
+ end
- shared_examples 'returning all SHA' do
- it 'returns all SHA from all merge_request_diffs' do
- expect(subject.merge_request_diffs.size).to eq(2)
- expect(subject.all_commits_sha).to eq(all_commits_sha)
+ shared_examples 'returning all SHA' do
+ it 'returns all SHA from all merge_request_diffs' do
+ expect(subject.merge_request_diffs.size).to eq(2)
+ expect(subject.all_commits_sha).to eq(all_commits_sha)
+ end
end
- end
- context 'with a completely different branch' do
- before do
- subject.update(target_branch: 'v1.0.0')
+ context 'with a completely different branch' do
+ before do
+ subject.update(target_branch: 'v1.0.0')
+ end
+
+ it_behaves_like 'returning all SHA'
end
- it_behaves_like 'returning all SHA'
+ context 'with a branch having no difference' do
+ before do
+ subject.update(target_branch: 'v1.1.0')
+ subject.reload # make sure commits were not cached
+ end
+
+ it_behaves_like 'returning all SHA'
+ end
end
- context 'with a branch having no difference' do
- before do
- subject.update(target_branch: 'v1.1.0')
- subject.reload # make sure commits were not cached
+ context 'when merge request is not persisted' do
+ context 'when compare commits are set in the service' do
+ let(:commit) { spy('commit') }
+
+ subject do
+ build(:merge_request, compare_commits: [commit, commit])
+ end
+
+ it 'returns commits from compare commits temporary data' do
+ expect(subject.all_commits_sha).to eq [commit, commit]
+ end
end
- it_behaves_like 'returning all SHA'
+ context 'when compare commits are not set in the service' do
+ subject { build(:merge_request) }
+
+ it 'returns array with diff head sha element only' do
+ expect(subject.all_commits_sha).to eq [subject.diff_head_sha]
+ end
+ end
end
end
diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb
new file mode 100644
index 00000000000..18c9d449ee5
--- /dev/null
+++ b/spec/models/project_label_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe ProjectLabel, models: true do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:project) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:project) }
+
+ context 'validates if title must not exist at group level' do
+ let(:group) { create(:group, name: 'gitlab-org') }
+ let(:project) { create(:empty_project, group: group) }
+
+ before do
+ create(:group_label, group: group, title: 'Bug')
+ end
+
+ it 'returns error if title already exists at group level' do
+ label = described_class.new(project: project, title: 'Bug')
+
+ label.valid?
+
+ expect(label.errors[:title]).to include 'already exists at group level for gitlab-org. Please choose another one.'
+ end
+
+ it 'does not returns error if title does not exist at group level' do
+ label = described_class.new(project: project, title: 'Security')
+
+ label.valid?
+
+ expect(label.errors[:title]).to be_empty
+ end
+
+ it 'does not returns error if project does not belong to group' do
+ another_project = create(:empty_project)
+ label = described_class.new(project: another_project, title: 'Bug')
+
+ label.valid?
+
+ expect(label.errors[:title]).to be_empty
+ end
+
+ it 'does not returns error when title does not change' do
+ project_label = create(:label, project: project, name: 'Security')
+ create(:group_label, group: group, name: 'Security')
+ project_label.description = 'Security related stuff.'
+
+ project_label.valid?
+
+ expect(project_label.errors[:title]).to be_empty
+ end
+ end
+
+ context 'when attempting to add more than one priority to the project label' do
+ it 'returns error' do
+ subject.priorities.build
+ subject.priorities.build
+
+ subject.valid?
+
+ expect(subject.errors[:priorities]).to include 'Number of permitted priorities exceeded'
+ end
+ end
+ end
+
+ describe '#subject' do
+ it 'aliases project to subject' do
+ subject = described_class.new(project: build(:empty_project))
+
+ expect(subject.subject).to be(subject.project)
+ end
+ end
+
+ describe '#to_reference' do
+ let(:label) { create(:label) }
+
+ context 'using id' do
+ it 'returns a String reference to the object' do
+ expect(label.to_reference).to eq "~#{label.id}"
+ end
+ end
+
+ context 'using name' do
+ it 'returns a String reference to the object' do
+ expect(label.to_reference(format: :name)).to eq %(~"#{label.name}")
+ end
+
+ it 'uses id when name contains double quote' do
+ label = create(:label, name: %q{"irony"})
+ expect(label.to_reference(format: :name)).to eq "~#{label.id}"
+ end
+ end
+
+ context 'using invalid format' do
+ it 'raises error' do
+ expect { label.to_reference(format: :invalid) }
+ .to raise_error StandardError, /Unknown format/
+ end
+ end
+
+ context 'cross project reference' do
+ let(:project) { create(:project) }
+
+ context 'using name' do
+ it 'returns cross reference with label name' do
+ expect(label.to_reference(project, format: :name))
+ .to eq %Q(#{label.project.to_reference}~"#{label.name}")
+ end
+ end
+
+ context 'using id' do
+ it 'returns cross reference with label id' do
+ expect(label.to_reference(project, format: :id))
+ .to eq %Q(#{label.project.to_reference}~#{label.id})
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index b48a3176007..6ff32aea018 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -30,6 +30,15 @@ describe JiraService, models: true do
end
end
+ describe '#reference_pattern' do
+ it_behaves_like 'allows project key on reference pattern'
+
+ it 'does not allow # on the code' do
+ expect(subject.reference_pattern.match('#123')).to be_nil
+ expect(subject.reference_pattern.match('1#23#12')).to be_nil
+ end
+ end
+
describe "Execute" do
let(:user) { create(:user) }
let(:project) { create(:project) }
diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb
index b8679cd2563..0a7b237a051 100644
--- a/spec/models/project_services/redmine_service_spec.rb
+++ b/spec/models/project_services/redmine_service_spec.rb
@@ -26,4 +26,12 @@ describe RedmineService, models: true do
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
end
+
+ describe '#reference_pattern' do
+ it_behaves_like 'allows project key on reference pattern'
+
+ it 'does allow # on the reference' do
+ expect(subject.reference_pattern.match('#123')[:issue]).to eq('123')
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 67dbcc362f6..e6d98e25d0b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -56,7 +56,7 @@ describe Project, models: true do
it { is_expected.to have_many(:runners) }
it { is_expected.to have_many(:variables) }
it { is_expected.to have_many(:triggers) }
- it { is_expected.to have_many(:labels).dependent(:destroy) }
+ it { is_expected.to have_many(:labels).class_name('ProjectLabel').dependent(:destroy) }
it { is_expected.to have_many(:users_star_projects).dependent(:destroy) }
it { is_expected.to have_many(:environments).dependent(:destroy) }
it { is_expected.to have_many(:deployments).dependent(:destroy) }
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index f4b04445c6c..4f5c09a3029 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -106,9 +106,20 @@ describe API::API, api: true do
describe "POST /projects/:id/board/lists" do
let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
- it 'creates a new issue board list' do
- post api(base_url, user),
- label_id: ux_label.id
+ it 'creates a new issue board list for group labels' do
+ group = create(:group)
+ group_label = create(:group_label, group: group)
+ project.update(group: group)
+
+ post api(base_url, user), label_id: group_label.id
+
+ expect(response).to have_http_status(201)
+ expect(json_response['label']['name']).to eq(group_label.title)
+ expect(json_response['position']).to eq(3)
+ end
+
+ it 'creates a new issue board list for project labels' do
+ post api(base_url, user), label_id: ux_label.id
expect(response).to have_http_status(201)
expect(json_response['label']['name']).to eq(ux_label.title)
@@ -116,15 +127,13 @@ describe API::API, api: true do
end
it 'returns 400 when creating a new list if label_id is invalid' do
- post api(base_url, user),
- label_id: 23423
+ post api(base_url, user), label_id: 23423
expect(response).to have_http_status(400)
end
- it "returns 403 for project members with guest role" do
- put api("#{base_url}/#{test_list.id}", guest),
- position: 1
+ it 'returns 403 for project members with guest role' do
+ put api("#{base_url}/#{test_list.id}", guest), position: 1
expect(response).to have_http_status(403)
end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 7aa7e85a9e2..335efc4db6c 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -196,7 +196,7 @@ describe API::CommitStatuses, api: true do
end
context 'reporter user' do
- before { post api(post_url, reporter) }
+ before { post api(post_url, reporter), state: 'running' }
it 'does not create commit status' do
expect(response).to have_http_status(403)
@@ -204,7 +204,7 @@ describe API::CommitStatuses, api: true do
end
context 'guest user' do
- before { post api(post_url, guest) }
+ before { post api(post_url, guest), state: 'running' }
it 'does not create commit status' do
expect(response).to have_http_status(403)
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 83789223019..1da9988978b 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -12,12 +12,18 @@ describe API::API, api: true do
end
describe 'GET /projects/:id/labels' do
- it 'returns project labels' do
+ it 'returns all available labels to the project' do
+ group = create(:group)
+ group_label = create(:group_label, group: group)
+ project.update(group: group)
+
get api("/projects/#{project.id}/labels", 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(label1.name)
+ expect(json_response.size).to eq(2)
+ expect(json_response.first['name']).to eq(group_label.name)
+ expect(json_response.second['name']).to eq(label1.name)
end
end
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index 1ce2658569e..f8a1aed5441 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -73,9 +73,10 @@ describe API::API, api: true do
end.to change { SystemHook.count }.by(-1)
end
- it "returns success if hook id not found" do
- delete api("/hooks/12345", admin)
- expect(response).to have_http_status(200)
+ it 'returns 404 if the system hook does not exist' do
+ delete api('/hooks/12345', admin)
+
+ expect(response).to have_http_status(404)
end
end
end
diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb
index e7806add916..a7e9efcf93f 100644
--- a/spec/services/boards/lists/create_service_spec.rb
+++ b/spec/services/boards/lists/create_service_spec.rb
@@ -9,6 +9,10 @@ describe Boards::Lists::CreateService, services: true do
subject(:service) { described_class.new(project, user, label_id: label.id) }
+ before do
+ project.team << [user, :developer]
+ end
+
context 'when board lists is empty' do
it 'creates a new list at beginning of the list' do
list = service.execute(board)
diff --git a/spec/services/boards/lists/generate_service_spec.rb b/spec/services/boards/lists/generate_service_spec.rb
index 8b2f5e81338..ed0337662af 100644
--- a/spec/services/boards/lists/generate_service_spec.rb
+++ b/spec/services/boards/lists/generate_service_spec.rb
@@ -8,6 +8,10 @@ describe Boards::Lists::GenerateService, services: true do
subject(:service) { described_class.new(project, user) }
+ before do
+ project.team << [user, :developer]
+ end
+
context 'when board lists is empty' do
it 'creates the default lists' do
expect { service.execute(board) }.to change(board.lists, :count).by(2)
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 0b84c7262c3..cf0a18aacec 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -7,11 +7,13 @@ describe CreateDeploymentService, services: true do
let(:service) { described_class.new(project, user, params) }
describe '#execute' do
+ let(:options) { nil }
let(:params) do
{ environment: 'production',
ref: 'master',
tag: false,
sha: '97de212e80737a608d939f648d959671fb0a0142',
+ options: options
}
end
@@ -28,7 +30,7 @@ describe CreateDeploymentService, services: true do
end
context 'when environment exist' do
- before { create(:environment, project: project, name: 'production') }
+ let!(:environment) { create(:environment, project: project, name: 'production') }
it 'does not create a new environment' do
expect { subject }.not_to change { Environment.count }
@@ -37,6 +39,46 @@ describe CreateDeploymentService, services: true do
it 'does create a deployment' do
expect(subject).to be_persisted
end
+
+ context 'and start action is defined' do
+ let(:options) { { action: 'start' } }
+
+ context 'and environment is stopped' do
+ before do
+ environment.stop
+ end
+
+ it 'makes environment available' do
+ subject
+
+ expect(environment.reload).to be_available
+ end
+
+ it 'does create a deployment' do
+ expect(subject).to be_persisted
+ end
+ end
+ end
+
+ context 'and stop action is defined' do
+ let(:options) { { action: 'stop' } }
+
+ context 'and environment is available' do
+ before do
+ environment.start
+ end
+
+ it 'makes environment stopped' do
+ subject
+
+ expect(environment.reload).to be_stopped
+ end
+
+ it 'does not create a deployment' do
+ expect(subject).to be_nil
+ end
+ end
+ end
end
context 'for environment with invalid name' do
@@ -53,7 +95,7 @@ describe CreateDeploymentService, services: true do
end
it 'does not create a deployment' do
- expect(subject).not_to be_persisted
+ expect(subject).to be_nil
end
end
@@ -83,6 +125,25 @@ describe CreateDeploymentService, services: true do
it 'does create a new deployment' do
expect(subject).to be_persisted
end
+
+ context 'and environment exist' do
+ let!(:environment) { create(:environment, project: project, name: 'review-apps/feature-review-apps') }
+
+ it 'does not create a new environment' do
+ expect { subject }.not_to change { Environment.count }
+ end
+
+ it 'updates external url' do
+ subject
+
+ expect(subject.environment.name).to eq('review-apps/feature-review-apps')
+ expect(subject.environment.external_url).to eq('http://feature-review-apps.review-apps.gitlab.com')
+ end
+
+ it 'does create a new deployment' do
+ expect(subject).to be_persisted
+ end
+ end
end
context 'when project was removed' do
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 8dda34c7a03..ad5170afc21 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -415,7 +415,7 @@ describe GitPushService, services: true do
it "doesn't close issues when external issue tracker is in use" do
allow_any_instance_of(Project).to receive(:default_issues_tracker?).
and_return(false)
- external_issue_tracker = double(title: 'My Tracker', issue_path: issue.iid)
+ external_issue_tracker = double(title: 'My Tracker', issue_path: issue.iid, reference_pattern: project.issue_reference_pattern)
allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(external_issue_tracker)
# The push still shouldn't create cross-reference notes.
@@ -484,30 +484,46 @@ describe GitPushService, services: true do
end
context "closing an issue" do
- let(:message) { "this is some work.\n\ncloses JIRA-1" }
-
- it "initiates one api call to jira server to close the issue" do
- transition_body = {
- transition: {
- id: '2'
- }
- }.to_json
-
- execute_service(project, commit_author, @oldrev, @newrev, @ref )
- expect(WebMock).to have_requested(:post, jira_api_transition_url).with(
- body: transition_body
- ).once
+ let(:message) { "this is some work.\n\ncloses JIRA-1" }
+ let(:transition_body) { { transition: { id: '2' } }.to_json }
+ let(:comment_body) { { body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]." }.to_json }
+
+ context "using right markdown" do
+ it "initiates one api call to jira server to close the issue" do
+ execute_service(project, commit_author, @oldrev, @newrev, @ref )
+
+ expect(WebMock).to have_requested(:post, jira_api_transition_url).with(
+ body: transition_body
+ ).once
+ end
+
+ it "initiates one api call to jira server to comment on the issue" do
+ execute_service(project, commit_author, @oldrev, @newrev, @ref )
+
+ expect(WebMock).to have_requested(:post, jira_api_comment_url).with(
+ body: comment_body
+ ).once
+ end
end
- it "initiates one api call to jira server to comment on the issue" do
- comment_body = {
- body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]."
- }.to_json
+ context "using wrong markdown" do
+ let(:message) { "this is some work.\n\ncloses #1" }
- execute_service(project, commit_author, @oldrev, @newrev, @ref )
- expect(WebMock).to have_requested(:post, jira_api_comment_url).with(
- body: comment_body
- ).once
+ it "does not initiates one api call to jira server to close the issue" do
+ execute_service(project, commit_author, @oldrev, @newrev, @ref )
+
+ expect(WebMock).not_to have_requested(:post, jira_api_transition_url).with(
+ body: transition_body
+ )
+ end
+
+ it "does not initiates one api call to jira server to comment on the issue" do
+ execute_service(project, commit_author, @oldrev, @newrev, @ref )
+
+ expect(WebMock).not_to have_requested(:post, jira_api_comment_url).with(
+ body: comment_body
+ ).once
+ end
end
end
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 1050502fa19..5c0331ebe66 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -67,6 +67,27 @@ describe Issues::CreateService, services: true do
expect(Todo.where(attributes).count).to eq 1
end
+ context 'when label belongs to project group' do
+ let(:group) { create(:group) }
+ let(:group_labels) { create_pair(:group_label, group: group) }
+
+ let(:opts) do
+ {
+ title: 'Title',
+ description: 'Description',
+ label_ids: group_labels.map(&:id)
+ }
+ end
+
+ before do
+ project.update(group: group)
+ end
+
+ it 'assigns group labels' do
+ expect(issue.labels).to match_array group_labels
+ end
+ end
+
context 'when label belongs to different project' do
let(:label) { create(:label) }
diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb
new file mode 100644
index 00000000000..cbfc63de811
--- /dev/null
+++ b/spec/services/labels/find_or_create_service_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe Labels::FindOrCreateService, services: true do
+ describe '#execute' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
+ let(:params) do
+ {
+ title: 'Security',
+ description: 'Security related stuff.',
+ color: '#FF0000'
+ }
+ end
+
+ subject(:service) { described_class.new(user, project, params) }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'when label does not exist at group level' do
+ it 'creates a new label at project level' do
+ expect { service.execute }.to change(project.labels, :count).by(1)
+ end
+ end
+
+ context 'when label exists at group level' do
+ it 'returns the group label' do
+ group_label = create(:group_label, group: group, title: 'Security')
+
+ expect(service.execute).to eq group_label
+ end
+ end
+
+ context 'when label does not exist at group level' do
+ it 'creates a new label at project leve' do
+ expect { service.execute }.to change(project.labels, :count).by(1)
+ end
+ end
+
+ context 'when label exists at project level' do
+ it 'returns the project label' do
+ project_label = create(:label, project: project, title: 'Security')
+
+ expect(service.execute).to eq project_label
+ end
+ end
+ end
+end
diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb
new file mode 100644
index 00000000000..ddf3527dc0f
--- /dev/null
+++ b/spec/services/labels/transfer_service_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe Labels::TransferService, services: true do
+ describe '#execute' do
+ let(:user) { create(:user) }
+ let(:group_1) { create(:group) }
+ let(:group_2) { create(:group) }
+ let(:group_3) { create(:group) }
+ let(:project_1) { create(:project, namespace: group_2) }
+ let(:project_2) { create(:project, namespace: group_3) }
+
+ let(:group_label_1) { create(:group_label, group: group_1, name: 'Group Label 1') }
+ let(:group_label_2) { create(:group_label, group: group_1, name: 'Group Label 2') }
+ let(:group_label_3) { create(:group_label, group: group_1, name: 'Group Label 3') }
+ let(:group_label_4) { create(:group_label, group: group_2, name: 'Group Label 4') }
+ let(:group_label_5) { create(:group_label, group: group_3, name: 'Group Label 5') }
+ let(:project_label_1) { create(:label, project: project_1, name: 'Project Label 1') }
+
+ subject(:service) { described_class.new(user, group_1, project_1) }
+
+ before do
+ create(:labeled_issue, project: project_1, labels: [group_label_1])
+ create(:labeled_issue, project: project_1, labels: [group_label_4])
+ create(:labeled_issue, project: project_1, labels: [project_label_1])
+ create(:labeled_issue, project: project_2, labels: [group_label_5])
+ create(:labeled_merge_request, source_project: project_1, labels: [group_label_1, group_label_2])
+ create(:labeled_merge_request, source_project: project_2, labels: [group_label_5])
+ end
+
+ it 'recreates the missing group labels at project level' do
+ expect { service.execute }.to change(project_1.labels, :count).by(2)
+ end
+
+ it 'recreates label priorities related to the missing group labels' do
+ create(:label_priority, project: project_1, label: group_label_1, priority: 1)
+
+ service.execute
+
+ new_project_label = project_1.labels.find_by(title: group_label_1.title)
+ expect(new_project_label.id).not_to eq group_label_1.id
+ expect(new_project_label.priorities).not_to be_empty
+ end
+
+ it 'does not recreate missing group labels that are not applied to issues or merge requests' do
+ service.execute
+
+ expect(project_1.labels.where(title: group_label_3.title)).to be_empty
+ end
+
+ it 'does not recreate missing group labels that already exist in the project group' do
+ service.execute
+
+ expect(project_1.labels.where(title: group_label_4.title)).to be_empty
+ end
+ end
+end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index ee53e110aee..f93d7732a9a 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -74,6 +74,18 @@ describe MergeRequests::MergeService, services: true do
service.execute(merge_request)
end
+
+ context "wrong issue markdown" do
+ it 'does not close issues on JIRA issue tracker' do
+ jira_issue = ExternalIssue.new('#123', project)
+ commit = double('commit', safe_message: "Fixes #{jira_issue.to_reference}")
+ allow(merge_request).to receive(:commits).and_return([commit])
+
+ expect_any_instance_of(JiraService).not_to receive(:close_issue)
+
+ service.execute(merge_request)
+ end
+ end
end
end
@@ -120,13 +132,13 @@ describe MergeRequests::MergeService, services: true do
let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
it 'saves error if there is an exception' do
- allow(service).to receive(:repository).and_raise("error")
+ allow(service).to receive(:repository).and_raise("error message")
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
- expect(merge_request.merge_error).to eq("Something went wrong during merge")
+ expect(merge_request.merge_error).to eq("Something went wrong during merge: error message")
end
it 'saves error if there is an PreReceiveError exception' do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 57c71544dff..1540b90163a 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -71,4 +71,14 @@ describe Projects::TransferService, services: true do
it { expect(private_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) }
end
end
+
+ context 'missing group labels applied to issues or merge requests' do
+ it 'delegates tranfer to Labels::TransferService' do
+ group.add_owner(user)
+
+ expect_any_instance_of(Labels::TransferService).to receive(:execute).once.and_call_original
+
+ transfer_project(project, user, group)
+ end
+ end
end
diff --git a/spec/support/issue_tracker_service_shared_example.rb b/spec/support/issue_tracker_service_shared_example.rb
index b6d7436c360..e70b3963d9d 100644
--- a/spec/support/issue_tracker_service_shared_example.rb
+++ b/spec/support/issue_tracker_service_shared_example.rb
@@ -5,3 +5,18 @@ RSpec.shared_examples 'issue tracker service URL attribute' do |url_attr|
it { is_expected.not_to allow_value('ftp://example.com').for(url_attr) }
it { is_expected.not_to allow_value('herp-and-derp').for(url_attr) }
end
+
+RSpec.shared_examples 'allows project key on reference pattern' do |url_attr|
+ it 'allows underscores in the project name' do
+ expect(subject.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
+ end
+
+ it 'allows numbers in the project name' do
+ expect(subject.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234'
+ end
+
+ it 'requires the project name to begin with A-Z' do
+ expect(subject.reference_pattern.match('3EXT_EXT-1234')).to eq nil
+ expect(subject.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
+ end
+end