Commit 439a61783d0b61bbcc8f3c9e5b828b2270a679aa
1 parent
c86553cd
Exists in
spb-stable
and in
3 other branches
User can leave group from group page.
Showing
23 changed files
with
430 additions
and
151 deletions
Show diff stats
app/controllers/application_controller.rb
| @@ -135,12 +135,12 @@ class ApplicationController < ActionController::Base | @@ -135,12 +135,12 @@ class ApplicationController < ActionController::Base | ||
| 135 | end | 135 | end |
| 136 | end | 136 | end |
| 137 | 137 | ||
| 138 | - def render_404 | ||
| 139 | - render file: Rails.root.join("public", "404"), layout: false, status: "404" | 138 | + def render_403 |
| 139 | + head :forbidden | ||
| 140 | end | 140 | end |
| 141 | 141 | ||
| 142 | - def render_403 | ||
| 143 | - render file: Rails.root.join("public", "403"), layout: false, status: "403" | 142 | + def render_404 |
| 143 | + render file: Rails.root.join("public", "404"), layout: false, status: "404" | ||
| 144 | end | 144 | end |
| 145 | 145 | ||
| 146 | def require_non_empty_project | 146 | def require_non_empty_project |
app/controllers/profiles/groups_controller.rb
| @@ -7,12 +7,11 @@ class Profiles::GroupsController < ApplicationController | @@ -7,12 +7,11 @@ class Profiles::GroupsController < ApplicationController | ||
| 7 | 7 | ||
| 8 | def leave | 8 | def leave |
| 9 | @users_group = group.users_groups.where(user_id: current_user.id).first | 9 | @users_group = group.users_groups.where(user_id: current_user.id).first |
| 10 | - | ||
| 11 | - if group.last_owner?(current_user) | ||
| 12 | - redirect_to(profile_groups_path, alert: "You can't leave group. You must add at least one more owner to it.") | ||
| 13 | - else | 10 | + if can?(current_user, :destroy, @users_group) |
| 14 | @users_group.destroy | 11 | @users_group.destroy |
| 15 | redirect_to(profile_groups_path, info: "You left #{group.name} group.") | 12 | redirect_to(profile_groups_path, info: "You left #{group.name} group.") |
| 13 | + else | ||
| 14 | + return render_403 | ||
| 16 | end | 15 | end |
| 17 | end | 16 | end |
| 18 | 17 |
app/controllers/users_groups_controller.rb
| @@ -19,11 +19,14 @@ class UsersGroupsController < ApplicationController | @@ -19,11 +19,14 @@ class UsersGroupsController < ApplicationController | ||
| 19 | 19 | ||
| 20 | def destroy | 20 | def destroy |
| 21 | @users_group = @group.users_groups.find(params[:id]) | 21 | @users_group = @group.users_groups.find(params[:id]) |
| 22 | - @users_group.destroy | ||
| 23 | - | ||
| 24 | - respond_to do |format| | ||
| 25 | - format.html { redirect_to members_group_path(@group), notice: 'User was successfully removed from group.' } | ||
| 26 | - format.js { render nothing: true } | 22 | + if can?(current_user, :destroy, @users_group) # May fail if last owner. |
| 23 | + @users_group.destroy | ||
| 24 | + respond_to do |format| | ||
| 25 | + format.html { redirect_to members_group_path(@group), notice: 'User was successfully removed from group.' } | ||
| 26 | + format.js { render nothing: true } | ||
| 27 | + end | ||
| 28 | + else | ||
| 29 | + return render_403 | ||
| 27 | end | 30 | end |
| 28 | end | 31 | end |
| 29 | 32 |
app/helpers/groups_helper.rb
| 1 | module GroupsHelper | 1 | module GroupsHelper |
| 2 | def remove_user_from_group_message(group, user) | 2 | def remove_user_from_group_message(group, user) |
| 3 | - "You are going to remove #{user.name} from #{group.name} Group. Are you sure?" | 3 | + "Are you sure you want to remove \"#{user.name}\" from \"#{group.name}\"?" |
| 4 | + end | ||
| 5 | + | ||
| 6 | + def leave_group_message(group) | ||
| 7 | + "Are you sure you want to leave \"#{group}\" group?" | ||
| 4 | end | 8 | end |
| 5 | 9 | ||
| 6 | def group_head_title | 10 | def group_head_title |
app/models/ability.rb
| @@ -14,6 +14,7 @@ class Ability | @@ -14,6 +14,7 @@ class Ability | ||
| 14 | when "MergeRequest" then merge_request_abilities(user, subject) | 14 | when "MergeRequest" then merge_request_abilities(user, subject) |
| 15 | when "Group" then group_abilities(user, subject) | 15 | when "Group" then group_abilities(user, subject) |
| 16 | when "Namespace" then namespace_abilities(user, subject) | 16 | when "Namespace" then namespace_abilities(user, subject) |
| 17 | + when "UsersGroup" then users_group_abilities(user, subject) | ||
| 17 | else [] | 18 | else [] |
| 18 | end.concat(global_abilities(user)) | 19 | end.concat(global_abilities(user)) |
| 19 | end | 20 | end |
| @@ -219,5 +220,19 @@ class Ability | @@ -219,5 +220,19 @@ class Ability | ||
| 219 | end | 220 | end |
| 220 | end | 221 | end |
| 221 | end | 222 | end |
| 223 | + | ||
| 224 | + def users_group_abilities(user, subject) | ||
| 225 | + rules = [] | ||
| 226 | + target_user = subject.user | ||
| 227 | + group = subject.group | ||
| 228 | + can_manage = group_abilities(user, group).include?(:manage_group) | ||
| 229 | + if can_manage && (user != target_user) | ||
| 230 | + rules << :modify | ||
| 231 | + end | ||
| 232 | + if !group.last_owner?(user) && (can_manage || (user == target_user)) | ||
| 233 | + rules << :destroy | ||
| 234 | + end | ||
| 235 | + rules | ||
| 236 | + end | ||
| 222 | end | 237 | end |
| 223 | end | 238 | end |
app/views/groups/members.html.haml
| @@ -6,7 +6,6 @@ | @@ -6,7 +6,6 @@ | ||
| 6 | %strong= link_to "here", help_permissions_path, class: "vlink" | 6 | %strong= link_to "here", help_permissions_path, class: "vlink" |
| 7 | 7 | ||
| 8 | %hr | 8 | %hr |
| 9 | -- can_manage_group = current_user.can? :manage_group, @group | ||
| 10 | .ui-box | 9 | .ui-box |
| 11 | .title | 10 | .title |
| 12 | %strong #{@group.name} | 11 | %strong #{@group.name} |
| @@ -15,6 +14,6 @@ | @@ -15,6 +14,6 @@ | ||
| 15 | (#{@members.count}) | 14 | (#{@members.count}) |
| 16 | %ul.well-list | 15 | %ul.well-list |
| 17 | - @members.each do |member| | 16 | - @members.each do |member| |
| 18 | - = render 'users_groups/users_group', member: member, show_controls: can_manage_group | ||
| 19 | -- if can_manage_group | 17 | + = render 'users_groups/users_group', member: member, show_controls: true |
| 18 | +- if current_user.can? :manage_group, @group | ||
| 20 | = render "new_group_member" | 19 | = render "new_group_member" |
app/views/help/permissions.html.haml
app/views/profiles/groups/index.html.haml
| @@ -22,9 +22,10 @@ | @@ -22,9 +22,10 @@ | ||
| 22 | %i.icon-cogs | 22 | %i.icon-cogs |
| 23 | Settings | 23 | Settings |
| 24 | 24 | ||
| 25 | - = link_to leave_profile_group_path(group), data: { confirm: "Are you sure you want to leave #{group.name} group?"}, method: :delete, class: "btn-small btn grouped", title: 'Remove user from group' do | ||
| 26 | - %i.icon-signout | ||
| 27 | - Leave | 25 | + - if can?(current_user, :destroy, user_group) |
| 26 | + = link_to leave_profile_group_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-small btn grouped", title: 'Remove user from group' do | ||
| 27 | + %i.icon-signout | ||
| 28 | + Leave | ||
| 28 | 29 | ||
| 29 | = link_to group, class: 'group-name' do | 30 | = link_to group, class: 'group-name' do |
| 30 | %strong= group.name | 31 | %strong= group.name |
app/views/users_groups/_users_group.html.haml
| @@ -9,12 +9,17 @@ | @@ -9,12 +9,17 @@ | ||
| 9 | 9 | ||
| 10 | %span.pull-right | 10 | %span.pull-right |
| 11 | %strong= member.human_access | 11 | %strong= member.human_access |
| 12 | - | ||
| 13 | - - if show_controls && can?(current_user, :manage_group, @group) && current_user != user | ||
| 14 | - = link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do | ||
| 15 | - %i.icon-edit | ||
| 16 | - = link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do | ||
| 17 | - %i.icon-minus.icon-white | 12 | + - if show_controls |
| 13 | + - if can?(current_user, :modify, member) | ||
| 14 | + = link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do | ||
| 15 | + %i.icon-edit | ||
| 16 | + - if can?(current_user, :destroy, member) | ||
| 17 | + - if current_user == member.user | ||
| 18 | + = link_to leave_profile_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do | ||
| 19 | + %i.icon-minus.icon-white | ||
| 20 | + - else | ||
| 21 | + = link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do | ||
| 22 | + %i.icon-minus.icon-white | ||
| 18 | 23 | ||
| 19 | .edit-member.hide.js-toggle-content | 24 | .edit-member.hide.js-toggle-content |
| 20 | = form_for [@group, member], remote: true do |f| | 25 | = form_for [@group, member], remote: true do |f| |
features/admin/groups.feature
| @@ -2,7 +2,7 @@ Feature: Admin Groups | @@ -2,7 +2,7 @@ Feature: Admin Groups | ||
| 2 | Background: | 2 | Background: |
| 3 | Given I sign in as an admin | 3 | Given I sign in as an admin |
| 4 | And I have group with projects | 4 | And I have group with projects |
| 5 | - And Create user "John Doe" | 5 | + And User "John Doe" exists |
| 6 | And I visit admin groups page | 6 | And I visit admin groups page |
| 7 | 7 | ||
| 8 | Scenario: See group list | 8 | Scenario: See group list |
| @@ -0,0 +1,116 @@ | @@ -0,0 +1,116 @@ | ||
| 1 | +Feature: Groups | ||
| 2 | + Background: | ||
| 3 | + Given I sign in as "John Doe" | ||
| 4 | + And "John Doe" is owner of group "Owned" | ||
| 5 | + And "John Doe" is guest of group "Guest" | ||
| 6 | + | ||
| 7 | + @javascript | ||
| 8 | + Scenario: I should see group "Owned" dashboard list | ||
| 9 | + When I visit group "Owned" page | ||
| 10 | + Then I should see group "Owned" projects list | ||
| 11 | + And I should see projects activity feed | ||
| 12 | + | ||
| 13 | + Scenario: Create a group from dasboard | ||
| 14 | + When I visit group "Owned" page | ||
| 15 | + And I visit dashboard page | ||
| 16 | + And I click new group link | ||
| 17 | + And submit form with new group "Samurai" info | ||
| 18 | + Then I should be redirected to group "Samurai" page | ||
| 19 | + And I should see newly created group "Samurai" | ||
| 20 | + | ||
| 21 | + Scenario: I should see group "Owned" issues list | ||
| 22 | + Given project from group "Owned" has issues assigned to me | ||
| 23 | + When I visit group "Owned" issues page | ||
| 24 | + Then I should see issues from group "Owned" assigned to me | ||
| 25 | + | ||
| 26 | + Scenario: I should see group "Owned" merge requests list | ||
| 27 | + Given project from group "Owned" has merge requests assigned to me | ||
| 28 | + When I visit group "Owned" merge requests page | ||
| 29 | + Then I should see merge requests from group "Owned" assigned to me | ||
| 30 | + | ||
| 31 | + @javascript | ||
| 32 | + Scenario: I should add user to projects in group "Owned" | ||
| 33 | + Given User "Mary Jane" exists | ||
| 34 | + When I visit group "Owned" members page | ||
| 35 | + And I select user "Mary Jane" from list with role "Reporter" | ||
| 36 | + Then I should see user "Mary Jane" in team list | ||
| 37 | + | ||
| 38 | + Scenario: I should see edit group "Owned" page | ||
| 39 | + When I visit group "Owned" settings page | ||
| 40 | + And I change group "Owned" name to "new-name" | ||
| 41 | + Then I should see new group "Owned" name | ||
| 42 | + | ||
| 43 | + Scenario: I edit group "Owned" avatar | ||
| 44 | + When I visit group "Owned" settings page | ||
| 45 | + And I change group "Owned" avatar | ||
| 46 | + And I visit group "Owned" settings page | ||
| 47 | + Then I should see new group "Owned" avatar | ||
| 48 | + And I should see the "Remove avatar" button | ||
| 49 | + | ||
| 50 | + Scenario: I remove group "Owned" avatar | ||
| 51 | + When I visit group "Owned" settings page | ||
| 52 | + And I have group "Owned" avatar | ||
| 53 | + And I visit group "Owned" settings page | ||
| 54 | + And I remove group "Owned" avatar | ||
| 55 | + Then I should not see group "Owned" avatar | ||
| 56 | + And I should not see the "Remove avatar" button | ||
| 57 | + | ||
| 58 | + # Leave | ||
| 59 | + | ||
| 60 | + @javascript | ||
| 61 | + Scenario: Owner should be able to remove himself from group if he is not the last owner | ||
| 62 | + Given "Mary Jane" is owner of group "Owned" | ||
| 63 | + When I visit group "Owned" members page | ||
| 64 | + Then I should see user "John Doe" in team list | ||
| 65 | + Then I should see user "Mary Jane" in team list | ||
| 66 | + When I click on the "Remove User From Group" button for "John Doe" | ||
| 67 | + And I visit group "Owned" members page | ||
| 68 | + Then I should not see user "John Doe" in team list | ||
| 69 | + Then I should see user "Mary Jane" in team list | ||
| 70 | + | ||
| 71 | + @javascript | ||
| 72 | + Scenario: Owner should not be able to remove himself from group if he is the last owner | ||
| 73 | + Given "Mary Jane" is guest of group "Owned" | ||
| 74 | + When I visit group "Owned" members page | ||
| 75 | + Then I should see user "John Doe" in team list | ||
| 76 | + Then I should see user "Mary Jane" in team list | ||
| 77 | + Then I should not see the "Remove User From Group" button for "Mary Jane" | ||
| 78 | + | ||
| 79 | + @javascript | ||
| 80 | + Scenario: Guest should be able to remove himself from group | ||
| 81 | + Given "Mary Jane" is guest of group "Guest" | ||
| 82 | + When I visit group "Guest" members page | ||
| 83 | + Then I should see user "John Doe" in team list | ||
| 84 | + Then I should see user "Mary Jane" in team list | ||
| 85 | + When I click on the "Remove User From Group" button for "John Doe" | ||
| 86 | + When I visit group "Guest" members page | ||
| 87 | + Then I should not see user "John Doe" in team list | ||
| 88 | + Then I should see user "Mary Jane" in team list | ||
| 89 | + | ||
| 90 | + @javascript | ||
| 91 | + Scenario: Guest should be able to remove himself from group even if he is the only user in the group | ||
| 92 | + When I visit group "Guest" members page | ||
| 93 | + Then I should see user "John Doe" in team list | ||
| 94 | + When I click on the "Remove User From Group" button for "John Doe" | ||
| 95 | + When I visit group "Guest" members page | ||
| 96 | + Then I should not see user "John Doe" in team list | ||
| 97 | + | ||
| 98 | + # Remove others | ||
| 99 | + | ||
| 100 | + @javascript | ||
| 101 | + Scenario: Owner should be able to remove other users from group | ||
| 102 | + Given "Mary Jane" is owner of group "Owned" | ||
| 103 | + When I visit group "Owned" members page | ||
| 104 | + Then I should see user "John Doe" in team list | ||
| 105 | + Then I should see user "Mary Jane" in team list | ||
| 106 | + When I click on the "Remove User From Group" button for "Mary Jane" | ||
| 107 | + When I visit group "Owned" members page | ||
| 108 | + Then I should see user "John Doe" in team list | ||
| 109 | + Then I should not see user "Mary Jane" in team list | ||
| 110 | + | ||
| 111 | + Scenario: Guest should not be able to remove other users from group | ||
| 112 | + Given "Mary Jane" is guest of group "Guest" | ||
| 113 | + When I visit group "Guest" members page | ||
| 114 | + Then I should see user "John Doe" in team list | ||
| 115 | + Then I should see user "Mary Jane" in team list | ||
| 116 | + Then I should not see the "Remove User From Group" button for "Mary Jane" |
features/group/create_group.feature
| @@ -1,11 +0,0 @@ | @@ -1,11 +0,0 @@ | ||
| 1 | -Feature: Groups | ||
| 2 | - Background: | ||
| 3 | - Given I sign in as a user | ||
| 4 | - | ||
| 5 | - Scenario: Create a group from dasboard | ||
| 6 | - Given I have group with projects | ||
| 7 | - And I visit dashboard page | ||
| 8 | - When I click new group link | ||
| 9 | - And submit form with new group info | ||
| 10 | - Then I should be redirected to group page | ||
| 11 | - And I should see newly created group |
features/group/group.feature
| @@ -1,47 +0,0 @@ | @@ -1,47 +0,0 @@ | ||
| 1 | -Feature: Groups | ||
| 2 | - Background: | ||
| 3 | - Given I sign in as a user | ||
| 4 | - And I have group with projects | ||
| 5 | - | ||
| 6 | - @javascript | ||
| 7 | - Scenario: I should see group dashboard list | ||
| 8 | - When I visit group page | ||
| 9 | - Then I should see projects list | ||
| 10 | - And I should see projects activity feed | ||
| 11 | - | ||
| 12 | - Scenario: I should see group issues list | ||
| 13 | - Given project from group has issues assigned to me | ||
| 14 | - When I visit group issues page | ||
| 15 | - Then I should see issues from this group assigned to me | ||
| 16 | - | ||
| 17 | - Scenario: I should see group merge requests list | ||
| 18 | - Given project from group has merge requests assigned to me | ||
| 19 | - When I visit group merge requests page | ||
| 20 | - Then I should see merge requests from this group assigned to me | ||
| 21 | - | ||
| 22 | - @javascript | ||
| 23 | - Scenario: I should add user to projects in Group | ||
| 24 | - Given Create user "John Doe" | ||
| 25 | - When I visit group members page | ||
| 26 | - And I select user "John Doe" from list with role "Reporter" | ||
| 27 | - Then I should see user "John Doe" in team list | ||
| 28 | - | ||
| 29 | - Scenario: I should see edit group page | ||
| 30 | - When I visit group settings page | ||
| 31 | - And I change group name | ||
| 32 | - Then I should see new group name | ||
| 33 | - | ||
| 34 | - Scenario: I edit my group avatar | ||
| 35 | - When I visit group settings page | ||
| 36 | - And I change my group avatar | ||
| 37 | - And I visit group settings page | ||
| 38 | - Then I should see new group avatar | ||
| 39 | - And I should see the "Remove avatar" button | ||
| 40 | - | ||
| 41 | - Scenario: I remove my group avatar | ||
| 42 | - When I visit group settings page | ||
| 43 | - And I have an group avatar | ||
| 44 | - And I visit group settings page | ||
| 45 | - And I remove my group avatar | ||
| 46 | - Then I should not see my group avatar | ||
| 47 | - And I should not see the "Remove avatar" button |
| @@ -0,0 +1,47 @@ | @@ -0,0 +1,47 @@ | ||
| 1 | +Feature: Profile Group | ||
| 2 | + Background: | ||
| 3 | + Given I sign in as "John Doe" | ||
| 4 | + And "John Doe" is owner of group "Owned" | ||
| 5 | + And "John Doe" is guest of group "Guest" | ||
| 6 | + | ||
| 7 | + # Leave groups | ||
| 8 | + | ||
| 9 | + @javascript | ||
| 10 | + Scenario: Owner should be able to leave from group if he is not the last owner | ||
| 11 | + Given "Mary Jane" is owner of group "Owned" | ||
| 12 | + When I visit profile groups page | ||
| 13 | + Then I should see group "Owned" in group list | ||
| 14 | + Then I should see group "Guest" in group list | ||
| 15 | + When I click on the "Leave" button for group "Owned" | ||
| 16 | + And I visit profile groups page | ||
| 17 | + Then I should not see group "Owned" in group list | ||
| 18 | + Then I should see group "Guest" in group list | ||
| 19 | + | ||
| 20 | + @javascript | ||
| 21 | + Scenario: Owner should not be able to leave from group if he is the last owner | ||
| 22 | + Given "Mary Jane" is guest of group "Owned" | ||
| 23 | + When I visit profile groups page | ||
| 24 | + Then I should see group "Owned" in group list | ||
| 25 | + Then I should see group "Guest" in group list | ||
| 26 | + Then I should not see the "Leave" button for group "Owned" | ||
| 27 | + | ||
| 28 | + @javascript | ||
| 29 | + Scenario: Guest should be able to leave from group | ||
| 30 | + Given "Mary Jane" is guest of group "Guest" | ||
| 31 | + When I visit profile groups page | ||
| 32 | + Then I should see group "Owned" in group list | ||
| 33 | + Then I should see group "Guest" in group list | ||
| 34 | + When I click on the "Leave" button for group "Guest" | ||
| 35 | + When I visit profile groups page | ||
| 36 | + Then I should see group "Owned" in group list | ||
| 37 | + Then I should not see group "Guest" in group list | ||
| 38 | + | ||
| 39 | + @javascript | ||
| 40 | + Scenario: Guest should be able to leave from group even if he is the only user in the group | ||
| 41 | + When I visit profile groups page | ||
| 42 | + Then I should see group "Owned" in group list | ||
| 43 | + Then I should see group "Guest" in group list | ||
| 44 | + When I click on the "Leave" button for group "Guest" | ||
| 45 | + When I visit profile groups page | ||
| 46 | + Then I should see group "Owned" in group list | ||
| 47 | + Then I should not see group "Guest" in group list |
features/steps/group/group.rb
| 1 | class Groups < Spinach::FeatureSteps | 1 | class Groups < Spinach::FeatureSteps |
| 2 | include SharedAuthentication | 2 | include SharedAuthentication |
| 3 | include SharedPaths | 3 | include SharedPaths |
| 4 | + include SharedGroup | ||
| 4 | include SharedUser | 5 | include SharedUser |
| 5 | include Select2Helper | 6 | include Select2Helper |
| 6 | 7 | ||
| 7 | - Then 'I should see projects list' do | ||
| 8 | - current_user.authorized_projects.each do |project| | 8 | + Then 'I should see group "Owned" projects list' do |
| 9 | + Group.find_by(name: "Owned").projects.each do |project| | ||
| 9 | page.should have_link project.name | 10 | page.should have_link project.name |
| 10 | end | 11 | end |
| 11 | end | 12 | end |
| 12 | 13 | ||
| 13 | - And 'I have group with projects' do | ||
| 14 | - @group = create(:group) | ||
| 15 | - @group.add_owner(current_user) | ||
| 16 | - @project = create(:project, namespace: @group) | ||
| 17 | - @event = create(:closed_issue_event, project: @project) | ||
| 18 | - | ||
| 19 | - @project.team << [current_user, :master] | ||
| 20 | - end | ||
| 21 | - | ||
| 22 | And 'I should see projects activity feed' do | 14 | And 'I should see projects activity feed' do |
| 23 | page.should have_content 'closed issue' | 15 | page.should have_content 'closed issue' |
| 24 | end | 16 | end |
| 25 | 17 | ||
| 26 | - Then 'I should see issues from this group assigned to me' do | 18 | + Then 'I should see issues from group "Owned" assigned to me' do |
| 27 | assigned_to_me(:issues).each do |issue| | 19 | assigned_to_me(:issues).each do |issue| |
| 28 | page.should have_content issue.title | 20 | page.should have_content issue.title |
| 29 | end | 21 | end |
| 30 | end | 22 | end |
| 31 | 23 | ||
| 32 | - Then 'I should see merge requests from this group assigned to me' do | 24 | + Then 'I should see merge requests from group "Owned" assigned to me' do |
| 33 | assigned_to_me(:merge_requests).each do |issue| | 25 | assigned_to_me(:merge_requests).each do |issue| |
| 34 | page.should have_content issue.title[0..80] | 26 | page.should have_content issue.title[0..80] |
| 35 | end | 27 | end |
| 36 | end | 28 | end |
| 37 | 29 | ||
| 38 | - And 'I select user "John Doe" from list with role "Reporter"' do | ||
| 39 | - user = User.find_by(name: "John Doe") | 30 | + And 'I select user "Mary Jane" from list with role "Reporter"' do |
| 31 | + user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") | ||
| 40 | within ".users-group-form" do | 32 | within ".users-group-form" do |
| 41 | select2(user.id, from: "#user_ids", multiple: true) | 33 | select2(user.id, from: "#user_ids", multiple: true) |
| 42 | select "Reporter", from: "group_access" | 34 | select "Reporter", from: "group_access" |
| @@ -49,14 +41,29 @@ class Groups < Spinach::FeatureSteps | @@ -49,14 +41,29 @@ class Groups < Spinach::FeatureSteps | ||
| 49 | projects_with_access.should have_content("John Doe") | 41 | projects_with_access.should have_content("John Doe") |
| 50 | end | 42 | end |
| 51 | 43 | ||
| 52 | - Given 'project from group has issues assigned to me' do | 44 | + Then 'I should not see user "John Doe" in team list' do |
| 45 | + projects_with_access = find(".ui-box .well-list") | ||
| 46 | + projects_with_access.should_not have_content("John Doe") | ||
| 47 | + end | ||
| 48 | + | ||
| 49 | + Then 'I should see user "Mary Jane" in team list' do | ||
| 50 | + projects_with_access = find(".ui-box .well-list") | ||
| 51 | + projects_with_access.should have_content("Mary Jane") | ||
| 52 | + end | ||
| 53 | + | ||
| 54 | + Then 'I should not see user "Mary Jane" in team list' do | ||
| 55 | + projects_with_access = find(".ui-box .well-list") | ||
| 56 | + projects_with_access.should_not have_content("Mary Jane") | ||
| 57 | + end | ||
| 58 | + | ||
| 59 | + Given 'project from group "Owned" has issues assigned to me' do | ||
| 53 | create :issue, | 60 | create :issue, |
| 54 | project: project, | 61 | project: project, |
| 55 | assignee: current_user, | 62 | assignee: current_user, |
| 56 | author: current_user | 63 | author: current_user |
| 57 | end | 64 | end |
| 58 | 65 | ||
| 59 | - Given 'project from group has merge requests assigned to me' do | 66 | + Given 'project from group "Owned" has merge requests assigned to me' do |
| 60 | create :merge_request, | 67 | create :merge_request, |
| 61 | source_project: project, | 68 | source_project: project, |
| 62 | target_project: project, | 69 | target_project: project, |
| @@ -68,78 +75,94 @@ class Groups < Spinach::FeatureSteps | @@ -68,78 +75,94 @@ class Groups < Spinach::FeatureSteps | ||
| 68 | click_link "New group" | 75 | click_link "New group" |
| 69 | end | 76 | end |
| 70 | 77 | ||
| 71 | - And 'submit form with new group info' do | 78 | + And 'submit form with new group "Samurai" info' do |
| 72 | fill_in 'group_name', with: 'Samurai' | 79 | fill_in 'group_name', with: 'Samurai' |
| 73 | fill_in 'group_description', with: 'Tokugawa Shogunate' | 80 | fill_in 'group_description', with: 'Tokugawa Shogunate' |
| 74 | click_button "Create group" | 81 | click_button "Create group" |
| 75 | end | 82 | end |
| 76 | 83 | ||
| 77 | - Then 'I should see newly created group' do | 84 | + Then 'I should be redirected to group "Samurai" page' do |
| 85 | + current_path.should == group_path(Group.last) | ||
| 86 | + end | ||
| 87 | + | ||
| 88 | + Then 'I should see newly created group "Samurai"' do | ||
| 78 | page.should have_content "Samurai" | 89 | page.should have_content "Samurai" |
| 79 | page.should have_content "Tokugawa Shogunate" | 90 | page.should have_content "Tokugawa Shogunate" |
| 80 | page.should have_content "You will only see events from projects in this group" | 91 | page.should have_content "You will only see events from projects in this group" |
| 81 | end | 92 | end |
| 82 | 93 | ||
| 83 | - Then 'I should be redirected to group page' do | ||
| 84 | - current_path.should == group_path(Group.last) | ||
| 85 | - end | ||
| 86 | - | ||
| 87 | - And 'I change group name' do | 94 | + And 'I change group "Owned" name to "new-name"' do |
| 88 | fill_in 'group_name', with: 'new-name' | 95 | fill_in 'group_name', with: 'new-name' |
| 89 | click_button "Save group" | 96 | click_button "Save group" |
| 90 | end | 97 | end |
| 91 | 98 | ||
| 92 | - Then 'I should see new group name' do | 99 | + Then 'I should see new group "Owned" name' do |
| 93 | within ".navbar-gitlab" do | 100 | within ".navbar-gitlab" do |
| 94 | page.should have_content "group: new-name" | 101 | page.should have_content "group: new-name" |
| 95 | end | 102 | end |
| 96 | end | 103 | end |
| 97 | 104 | ||
| 98 | - step 'I change my group avatar' do | 105 | + step 'I change group "Owned" avatar' do |
| 99 | attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) | 106 | attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) |
| 100 | click_button "Save group" | 107 | click_button "Save group" |
| 101 | - @group.reload | 108 | + Group.find_by(name: "Owned").reload |
| 102 | end | 109 | end |
| 103 | 110 | ||
| 104 | - step 'I should see new group avatar' do | ||
| 105 | - @group.avatar.should be_instance_of AttachmentUploader | ||
| 106 | - @group.avatar.url.should == "/uploads/group/avatar/#{ @group.id }/gitlab_logo.png" | 111 | + step 'I should see new group "Owned" avatar' do |
| 112 | + Group.find_by(name: "Owned").avatar.should be_instance_of AttachmentUploader | ||
| 113 | + Group.find_by(name: "Owned").avatar.url.should == "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/gitlab_logo.png" | ||
| 107 | end | 114 | end |
| 108 | 115 | ||
| 109 | step 'I should see the "Remove avatar" button' do | 116 | step 'I should see the "Remove avatar" button' do |
| 110 | page.should have_link("Remove avatar") | 117 | page.should have_link("Remove avatar") |
| 111 | end | 118 | end |
| 112 | 119 | ||
| 113 | - step 'I have an group avatar' do | 120 | + step 'I have group "Owned" avatar' do |
| 114 | attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) | 121 | attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) |
| 115 | click_button "Save group" | 122 | click_button "Save group" |
| 116 | - @group.reload | 123 | + Group.find_by(name: "Owned").reload |
| 117 | end | 124 | end |
| 118 | 125 | ||
| 119 | - step 'I remove my group avatar' do | 126 | + step 'I remove group "Owned" avatar' do |
| 120 | click_link "Remove avatar" | 127 | click_link "Remove avatar" |
| 121 | - @group.reload | 128 | + Group.find_by(name: "Owned").reload |
| 122 | end | 129 | end |
| 123 | 130 | ||
| 124 | - step 'I should not see my group avatar' do | ||
| 125 | - @group.avatar?.should be_false | 131 | + step 'I should not see group "Owned" avatar' do |
| 132 | + Group.find_by(name: "Owned").avatar?.should be_false | ||
| 126 | end | 133 | end |
| 127 | 134 | ||
| 128 | step 'I should not see the "Remove avatar" button' do | 135 | step 'I should not see the "Remove avatar" button' do |
| 129 | page.should_not have_link("Remove avatar") | 136 | page.should_not have_link("Remove avatar") |
| 130 | end | 137 | end |
| 131 | 138 | ||
| 132 | - protected | 139 | + step 'I click on the "Remove User From Group" button for "John Doe"' do |
| 140 | + find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click | ||
| 141 | + # poltergeist always confirms popups. | ||
| 142 | + end | ||
| 133 | 143 | ||
| 134 | - def current_group | ||
| 135 | - @group ||= Group.first | 144 | + step 'I click on the "Remove User From Group" button for "Mary Jane"' do |
| 145 | + find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click | ||
| 146 | + # poltergeist always confirms popups. | ||
| 136 | end | 147 | end |
| 137 | 148 | ||
| 138 | - def project | ||
| 139 | - current_group.projects.first | 149 | + step 'I should not see the "Remove User From Group" button for "John Doe"' do |
| 150 | + find(:css, 'li', text: "John Doe").should_not have_selector(:css, 'a.btn-remove') | ||
| 151 | + # poltergeist always confirms popups. | ||
| 140 | end | 152 | end |
| 141 | 153 | ||
| 154 | + step 'I should not see the "Remove User From Group" button for "Mary Jane"' do | ||
| 155 | + find(:css, 'li', text: "Mary Jane").should_not have_selector(:css, 'a.btn-remove') | ||
| 156 | + # poltergeist always confirms popups. | ||
| 157 | + end | ||
| 158 | + | ||
| 159 | + protected | ||
| 160 | + | ||
| 142 | def assigned_to_me key | 161 | def assigned_to_me key |
| 143 | project.send(key).where(assignee_id: current_user.id) | 162 | project.send(key).where(assignee_id: current_user.id) |
| 144 | end | 163 | end |
| 164 | + | ||
| 165 | + def project | ||
| 166 | + Group.find_by(name: "Owned").projects.first | ||
| 167 | + end | ||
| 145 | end | 168 | end |
| @@ -0,0 +1,44 @@ | @@ -0,0 +1,44 @@ | ||
| 1 | +class ProfileGroup < Spinach::FeatureSteps | ||
| 2 | + include SharedAuthentication | ||
| 3 | + include SharedGroup | ||
| 4 | + include SharedPaths | ||
| 5 | + include SharedUser | ||
| 6 | + | ||
| 7 | + # Leave | ||
| 8 | + | ||
| 9 | + step 'I click on the "Leave" button for group "Owned"' do | ||
| 10 | + find(:css, 'li', text: "Owner").find(:css, 'i.icon-signout').click | ||
| 11 | + # poltergeist always confirms popups. | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + step 'I click on the "Leave" button for group "Guest"' do | ||
| 15 | + find(:css, 'li', text: "Guest").find(:css, 'i.icon-signout').click | ||
| 16 | + # poltergeist always confirms popups. | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + step 'I should not see the "Leave" button for group "Owned"' do | ||
| 20 | + find(:css, 'li', text: "Owner").should_not have_selector(:css, 'i.icon-signout') | ||
| 21 | + # poltergeist always confirms popups. | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + step 'I should not see the "Leave" button for groupr "Guest"' do | ||
| 25 | + find(:css, 'li', text: "Guest").should_not have_selector(:css, 'i.icon-signout') | ||
| 26 | + # poltergeist always confirms popups. | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + step 'I should see group "Owned" in group list' do | ||
| 30 | + page.should have_content("Owned") | ||
| 31 | + end | ||
| 32 | + | ||
| 33 | + step 'I should not see group "Owned" in group list' do | ||
| 34 | + page.should_not have_content("Owned") | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + step 'I should see group "Guest" in group list' do | ||
| 38 | + page.should have_content("Guest") | ||
| 39 | + end | ||
| 40 | + | ||
| 41 | + step 'I should not see group "Guest" in group list' do | ||
| 42 | + page.should_not have_content("Guest") | ||
| 43 | + end | ||
| 44 | +end |
features/steps/project/project_network_graph.rb
| 1 | class ProjectNetworkGraph < Spinach::FeatureSteps | 1 | class ProjectNetworkGraph < Spinach::FeatureSteps |
| 2 | include SharedAuthentication | 2 | include SharedAuthentication |
| 3 | + include SharedPaths | ||
| 3 | include SharedProject | 4 | include SharedProject |
| 4 | 5 | ||
| 5 | Then 'page should have network graph' do | 6 | Then 'page should have network graph' do |
features/steps/shared/authentication.rb
| @@ -12,6 +12,14 @@ module SharedAuthentication | @@ -12,6 +12,14 @@ module SharedAuthentication | ||
| 12 | login_as :admin | 12 | login_as :admin |
| 13 | end | 13 | end |
| 14 | 14 | ||
| 15 | + step 'I sign in as "John Doe"' do | ||
| 16 | + login_with(user_exists("John Doe")) | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + step 'I sign in as "Mary Jane"' do | ||
| 20 | + login_with(user_exists("Mary Jane")) | ||
| 21 | + end | ||
| 22 | + | ||
| 15 | step 'I should be redirected to sign in page' do | 23 | step 'I should be redirected to sign in page' do |
| 16 | current_path.should == new_user_session_path | 24 | current_path.should == new_user_session_path |
| 17 | end | 25 | end |
| @@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
| 1 | +module SharedGroup | ||
| 2 | + include Spinach::DSL | ||
| 3 | + | ||
| 4 | + step '"John Doe" is owner of group "Owned"' do | ||
| 5 | + is_member_of("John Doe", "Owned", Gitlab::Access::OWNER) | ||
| 6 | + end | ||
| 7 | + | ||
| 8 | + step '"John Doe" is guest of group "Guest"' do | ||
| 9 | + is_member_of("John Doe", "Guest", Gitlab::Access::GUEST) | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + step '"Mary Jane" is owner of group "Owned"' do | ||
| 13 | + is_member_of("Mary Jane", "Owned", Gitlab::Access::OWNER) | ||
| 14 | + end | ||
| 15 | + | ||
| 16 | + step '"Mary Jane" is guest of group "Owned"' do | ||
| 17 | + is_member_of("Mary Jane", "Owned", Gitlab::Access::GUEST) | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + step '"Mary Jane" is guest of group "Guest"' do | ||
| 21 | + is_member_of("Mary Jane", "Guest", Gitlab::Access::GUEST) | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + protected | ||
| 25 | + | ||
| 26 | + def is_member_of(username, groupname, role) | ||
| 27 | + @project_count ||= 0 | ||
| 28 | + user = User.find_by(name: username) || create(:user, name: username) | ||
| 29 | + group = Group.find_by(name: groupname) || create(:group, name: groupname) | ||
| 30 | + group.add_user(user, role) | ||
| 31 | + project ||= create(:project, namespace: group, path: "project#{@project_count}") | ||
| 32 | + event ||= create(:closed_issue_event, project: project) | ||
| 33 | + project.team << [user, :master] | ||
| 34 | + @project_count += 1 | ||
| 35 | + end | ||
| 36 | +end |
features/steps/shared/paths.rb
| @@ -17,24 +17,44 @@ module SharedPaths | @@ -17,24 +17,44 @@ module SharedPaths | ||
| 17 | # Group | 17 | # Group |
| 18 | # ---------------------------------------- | 18 | # ---------------------------------------- |
| 19 | 19 | ||
| 20 | - step 'I visit group page' do | ||
| 21 | - visit group_path(current_group) | 20 | + step 'I visit group "Owned" page' do |
| 21 | + visit group_path(Group.find_by(name:"Owned")) | ||
| 22 | end | 22 | end |
| 23 | 23 | ||
| 24 | - step 'I visit group issues page' do | ||
| 25 | - visit issues_group_path(current_group) | 24 | + step 'I visit group "Owned" issues page' do |
| 25 | + visit issues_group_path(Group.find_by(name:"Owned")) | ||
| 26 | end | 26 | end |
| 27 | 27 | ||
| 28 | - step 'I visit group merge requests page' do | ||
| 29 | - visit merge_requests_group_path(current_group) | 28 | + step 'I visit group "Owned" merge requests page' do |
| 29 | + visit merge_requests_group_path(Group.find_by(name:"Owned")) | ||
| 30 | end | 30 | end |
| 31 | 31 | ||
| 32 | - step 'I visit group members page' do | ||
| 33 | - visit members_group_path(current_group) | 32 | + step 'I visit group "Owned" members page' do |
| 33 | + visit members_group_path(Group.find_by(name:"Owned")) | ||
| 34 | end | 34 | end |
| 35 | 35 | ||
| 36 | - step 'I visit group settings page' do | ||
| 37 | - visit edit_group_path(current_group) | 36 | + step 'I visit group "Owned" settings page' do |
| 37 | + visit edit_group_path(Group.find_by(name:"Owned")) | ||
| 38 | + end | ||
| 39 | + | ||
| 40 | + step 'I visit group "Guest" page' do | ||
| 41 | + visit group_path(Group.find_by(name:"Guest")) | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + step 'I visit group "Guest" issues page' do | ||
| 45 | + visit issues_group_path(Group.find_by(name:"Guest")) | ||
| 46 | + end | ||
| 47 | + | ||
| 48 | + step 'I visit group "Guest" merge requests page' do | ||
| 49 | + visit merge_requests_group_path(Group.find_by(name:"Guest")) | ||
| 50 | + end | ||
| 51 | + | ||
| 52 | + step 'I visit group "Guest" members page' do | ||
| 53 | + visit members_group_path(Group.find_by(name:"Guest")) | ||
| 54 | + end | ||
| 55 | + | ||
| 56 | + step 'I visit group "Guest" settings page' do | ||
| 57 | + visit edit_group_path(Group.find_by(name:"Guest")) | ||
| 38 | end | 58 | end |
| 39 | 59 | ||
| 40 | # ---------------------------------------- | 60 | # ---------------------------------------- |
| @@ -93,6 +113,14 @@ module SharedPaths | @@ -93,6 +113,14 @@ module SharedPaths | ||
| 93 | visit history_profile_path | 113 | visit history_profile_path |
| 94 | end | 114 | end |
| 95 | 115 | ||
| 116 | + step 'I visit profile groups page' do | ||
| 117 | + visit profile_groups_path | ||
| 118 | + end | ||
| 119 | + | ||
| 120 | + step 'I should be redirected to the profile groups page' do | ||
| 121 | + current_path.should == profile_groups_path | ||
| 122 | + end | ||
| 123 | + | ||
| 96 | # ---------------------------------------- | 124 | # ---------------------------------------- |
| 97 | # Admin | 125 | # Admin |
| 98 | # ---------------------------------------- | 126 | # ---------------------------------------- |
| @@ -326,4 +354,12 @@ module SharedPaths | @@ -326,4 +354,12 @@ module SharedPaths | ||
| 326 | def project | 354 | def project |
| 327 | project = Project.find_by!(name: "Shop") | 355 | project = Project.find_by!(name: "Shop") |
| 328 | end | 356 | end |
| 357 | + | ||
| 358 | + # ---------------------------------------- | ||
| 359 | + # Errors | ||
| 360 | + # ---------------------------------------- | ||
| 361 | + | ||
| 362 | + Then 'page status code should be 404' do | ||
| 363 | + page.status_code.should == 404 | ||
| 364 | + end | ||
| 329 | end | 365 | end |
features/steps/shared/project.rb
| @@ -58,10 +58,6 @@ module SharedProject | @@ -58,10 +58,6 @@ module SharedProject | ||
| 58 | page.should have_content("Features:") | 58 | page.should have_content("Features:") |
| 59 | end | 59 | end |
| 60 | 60 | ||
| 61 | - Then 'page status code should be 404' do | ||
| 62 | - page.status_code.should == 404 | ||
| 63 | - end | ||
| 64 | - | ||
| 65 | def current_project | 61 | def current_project |
| 66 | @project ||= Project.first | 62 | @project ||= Project.first |
| 67 | end | 63 | end |
| @@ -107,24 +103,21 @@ module SharedProject | @@ -107,24 +103,21 @@ module SharedProject | ||
| 107 | end | 103 | end |
| 108 | 104 | ||
| 109 | step '"John Doe" is authorized to private project "Enterprise"' do | 105 | step '"John Doe" is authorized to private project "Enterprise"' do |
| 110 | - user = User.find_by(name: "John Doe") | ||
| 111 | - user ||= create(:user, name: "John Doe", username: "john_doe") | 106 | + user = user_exists("John Doe", username: "john_doe") |
| 112 | project = Project.find_by(name: "Enterprise") | 107 | project = Project.find_by(name: "Enterprise") |
| 113 | project ||= create(:project, name: "Enterprise", namespace: user.namespace) | 108 | project ||= create(:project, name: "Enterprise", namespace: user.namespace) |
| 114 | project.team << [user, :master] | 109 | project.team << [user, :master] |
| 115 | end | 110 | end |
| 116 | 111 | ||
| 117 | step '"John Doe" is authorized to internal project "Internal"' do | 112 | step '"John Doe" is authorized to internal project "Internal"' do |
| 118 | - user = User.find_by(name: "John Doe") | ||
| 119 | - user ||= create(:user, name: "John Doe", username: "john_doe") | 113 | + user = user_exists("John Doe", username: "john_doe") |
| 120 | project = Project.find_by(name: "Internal") | 114 | project = Project.find_by(name: "Internal") |
| 121 | project ||= create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL | 115 | project ||= create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL |
| 122 | project.team << [user, :master] | 116 | project.team << [user, :master] |
| 123 | end | 117 | end |
| 124 | 118 | ||
| 125 | step '"John Doe" is authorized to public project "Community"' do | 119 | step '"John Doe" is authorized to public project "Community"' do |
| 126 | - user = User.find_by(name: "John Doe") | ||
| 127 | - user ||= create(:user, name: "John Doe", username: "john_doe") | 120 | + user = user_exists("John Doe", username: "john_doe") |
| 128 | project = Project.find_by(name: "Community") | 121 | project = Project.find_by(name: "Community") |
| 129 | project ||= create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC | 122 | project ||= create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC |
| 130 | project.team << [user, :master] | 123 | project.team << [user, :master] |
features/steps/shared/user.rb
| 1 | module SharedUser | 1 | module SharedUser |
| 2 | include Spinach::DSL | 2 | include Spinach::DSL |
| 3 | 3 | ||
| 4 | - step 'Create user "John Doe"' do | ||
| 5 | - create(:user, name: "John Doe", username: "john_doe") | 4 | + step 'User "John Doe" exists' do |
| 5 | + user_exists("John Doe", {username: "john_doe"}) | ||
| 6 | end | 6 | end |
| 7 | 7 | ||
| 8 | - step 'I sign in as "John Doe"' do | ||
| 9 | - login_with(User.find_by(name: "John Doe")) | 8 | + step 'User "Mary Jane" exists' do |
| 9 | + user_exists("Mary Jane", {username: "mary_jane"}) | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + protected | ||
| 13 | + | ||
| 14 | + def user_exists(name, options = {}) | ||
| 15 | + User.find_by(name: name) || create(:user, {name: name, admin: false}.merge(options)) | ||
| 10 | end | 16 | end |
| 11 | end | 17 | end |
features/user.feature