Commit 439a61783d0b61bbcc8f3c9e5b828b2270a679aa

Authored by Ciro Santillli
1 parent c86553cd

User can leave group from group page.

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
@@ -217,3 +217,4 @@ @@ -217,3 +217,4 @@
217 %td 217 %td
218 %td 218 %td
219 %td.permission-x &#10003; 219 %td.permission-x &#10003;
  220 + %p.light Any user can remove himself from a group, unless he is the last Owner of the group.
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
features/group.feature 0 → 100644
@@ -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  
features/profile/group.feature 0 → 100644
@@ -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 &lt; Spinach::FeatureSteps @@ -49,14 +41,29 @@ class Groups &lt; 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 &lt; Spinach::FeatureSteps @@ -68,78 +75,94 @@ class Groups &lt; 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
features/steps/profile/group.rb 0 → 100644
@@ -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
features/steps/shared/group.rb 0 → 100644
@@ -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
1 Feature: User 1 Feature: User
2 Background: 2 Background:
3 - Given Create user "John Doe" 3 + Given User "John Doe" exists
4 And "John Doe" is authorized to private project "Enterprise" 4 And "John Doe" is authorized to private project "Enterprise"
5 5
6 # Signed out 6 # Signed out