Commit 44e8ad2324d14b41f19d5b03ad4d2f6c32887a97

Authored by Cyril Mougel
2 parents ea727cfc f2409444
Exists in master and in 1 other branch production

Merge pull request #497 from shingara/features/#456

Improve user management
CHANGELOG.md
... ... @@ -5,6 +5,8 @@
5 5 - Update some gems ([@shingara][])
6 6 - [#492][] Improve some Pjax call ([@nfedyashev][])
7 7 - [#428][] Add the support of Unfuddle Tracker ([@parallel588][])
  8 +- Avoid to delete his own user ([@shingara][])
  9 +- [#456] Avoid to delete admin access of current user logged ([@shingara][])
8 10  
9 11 ### Bug Fixes
10 12  
... ... @@ -48,6 +50,7 @@
48 50 [#428]: https://github.com/errbit/errbit/issues/428
49 51 [#453]: https://github.com/errbit/errbit/issues/453
50 52 [#455]: https://github.com/errbit/errbit/issues/455
  53 +[#456]: https://github.com/errbit/errbit/issues/456
51 54 [#457]: https://github.com/errbit/errbit/issues/457
52 55 [#460]: https://github.com/errbit/errbit/issues/460
53 56 [#466]: https://github.com/errbit/errbit/issues/466
... ...
Gemfile
... ... @@ -2,7 +2,9 @@ source 'http://rubygems.org'
2 2  
3 3 gem 'rails', '3.2.13'
4 4 gem 'mongoid', '~> 2.7.1'
5   -gem 'mongoid_rails_migrations'
  5 +
  6 +# Mongoid rails migration > 0.0.14 is not compatible to Mongoid 2.x
  7 +gem 'mongoid_rails_migrations', '~> 0.0.14'
6 8 gem 'devise', '~> 1.5.4'
7 9 gem 'haml'
8 10 gem 'htmlentities'
... ... @@ -10,6 +12,8 @@ gem 'rack-ssl', :require => 'rack/ssl' # force SSL
10 12  
11 13 gem 'useragent'
12 14 gem 'inherited_resources'
  15 +gem 'decent_exposure'
  16 +gem 'strong_parameters'
13 17 gem 'SystemTimer', :platform => :ruby_18
14 18 gem 'actionmailer_inline_css', "~> 1.3.0"
15 19 gem 'kaminari', '>= 0.14.1'
... ...
Gemfile.lock
1 1 GIT
2 2 remote: https://github.com/NARKOZ/gitlab.git
3   - revision: f2ba111dba70eca5346a880c541dafaf35d3332a
  3 + revision: 53d7a8a86dfed63e56eeb16ea496bb7a82de337e
4 4 specs:
5 5 gitlab (2.2.0)
6 6 httparty
... ... @@ -91,9 +91,8 @@ GEM
91 91 simplecov (>= 0.7)
92 92 thor
93 93 crack (0.3.2)
94   - css_parser (1.2.6)
  94 + css_parser (1.3.4)
95 95 addressable
96   - rdoc
97 96 daemons (1.1.9)
98 97 database_cleaner (0.9.1)
99 98 debug_inspector (0.0.2)
... ... @@ -103,6 +102,7 @@ GEM
103 102 debugger-ruby_core_source (~> 1.2.1)
104 103 debugger-linecache (1.2.0)
105 104 debugger-ruby_core_source (1.2.2)
  105 + decent_exposure (2.2.0)
106 106 devise (1.5.4)
107 107 bcrypt-ruby (~> 3.0)
108 108 orm_adapter (~> 0.0.3)
... ... @@ -135,6 +135,7 @@ GEM
135 135 hike (1.2.2)
136 136 hipchat (0.9.0)
137 137 httparty
  138 + httparty
138 139 hoi (0.0.6)
139 140 httparty (> 0.6.0)
140 141 json (> 1.4.0)
... ... @@ -151,9 +152,9 @@ GEM
151 152 has_scope (~> 0.5.0)
152 153 responders (~> 0.9)
153 154 journey (1.0.4)
154   - jquery-rails (2.1.3)
155   - railties (>= 3.1.0, < 5.0)
156   - thor (~> 0.14)
  155 + jquery-rails (3.0.0)
  156 + railties (>= 3.0, < 5.0)
  157 + thor (>= 0.14, < 2.0)
157 158 json (1.8.0)
158 159 jwt (0.1.8)
159 160 multi_json (>= 1.5)
... ... @@ -229,12 +230,14 @@ GEM
229 230 activeresource (>= 2.3.0)
230 231 pivotal-tracker (0.5.10)
231 232 builder
  233 + builder
232 234 crack
233 235 happymapper (>= 0.3.2)
234 236 nokogiri (>= 1.4.3)
235 237 nokogiri (>= 1.5.5)
236 238 nokogiri-happymapper (>= 0.5.4)
237 239 rest-client (~> 1.6.0)
  240 + rest-client (~> 1.6.0)
238 241 pjax_rails (0.3.4)
239 242 jquery-rails
240 243 polyglot (0.3.3)
... ... @@ -279,7 +282,7 @@ GEM
279 282 rbx-require-relative (0.0.9)
280 283 rdoc (3.12.2)
281 284 json (~> 1.4)
282   - ref (1.0.4)
  285 + ref (1.0.5)
283 286 responders (0.9.3)
284 287 railties (~> 3.1)
285 288 rest-client (1.6.7)
... ... @@ -323,6 +326,10 @@ GEM
323 326 multi_json (~> 1.0)
324 327 rack (~> 1.0)
325 328 tilt (~> 1.1, != 1.3.0)
  329 + strong_parameters (0.2.1)
  330 + actionpack (~> 3.0)
  331 + activemodel (~> 3.0)
  332 + railties (~> 3.0)
326 333 taskmapper (0.8.0)
327 334 activeresource (~> 3.0)
328 335 activesupport (~> 3.0)
... ... @@ -385,6 +392,7 @@ DEPENDENCIES
385 392 coveralls
386 393 database_cleaner (~> 0.9.0)
387 394 debugger
  395 + decent_exposure
388 396 devise (~> 1.5.4)
389 397 email_spec
390 398 execjs
... ... @@ -405,7 +413,7 @@ DEPENDENCIES
405 413 meta_request
406 414 mongo
407 415 mongoid (~> 2.7.1)
408   - mongoid_rails_migrations
  416 + mongoid_rails_migrations (~> 0.0.14)
409 417 octokit
410 418 omniauth-github
411 419 oruen_redmine_client
... ... @@ -421,6 +429,7 @@ DEPENDENCIES
421 429 ruby-debug
422 430 ruby-fogbugz
423 431 rushover
  432 + strong_parameters
424 433 taskmapper (~> 0.8.0)
425 434 taskmapper-unfuddle (~> 0.7.0)
426 435 therubyracer
... ...
app/controllers/application_controller.rb
... ... @@ -17,6 +17,9 @@ class ApplicationController &lt; ActionController::Base
17 17 protected
18 18  
19 19  
  20 + ##
  21 + # Check if the current_user is admin or not and redirect to root url if not
  22 + #
20 23 def require_admin!
21 24 unless user_signed_in? && current_user.admin?
22 25 flash[:error] = "Sorry, you don't have permission to do that"
... ...
app/controllers/users_controller.rb
... ... @@ -2,71 +2,78 @@ class UsersController &lt; ApplicationController
2 2 respond_to :html
3 3  
4 4 before_filter :require_admin!, :except => [:edit, :update]
5   - before_filter :find_user, :only => [:show, :edit, :update, :destroy, :unlink_github]
6 5 before_filter :require_user_edit_priviledges, :only => [:edit, :update]
7 6  
8   - def index
9   - @users = User.all.page(params[:page]).per(current_user.per_page)
10   - end
  7 + expose(:user) {
  8 + params[:id] ? User.find(params[:id]) : User.new(user_params)
  9 + }
  10 + expose(:users) {
  11 + User.all.page(params[:page]).per(current_user.per_page)
  12 + }
11 13  
12   - def new
13   - @user = User.new
14   - end
  14 + def index; end
  15 + def new; end
  16 + def show; end
15 17  
16 18 def create
17   - @user = User.new(params[:user])
18   -
19   - # Set protected attributes
20   - @user.admin = params[:user].try(:[], :admin) if current_user.admin?
21   -
22   - if @user.save
23   - flash[:success] = "#{@user.name} is now part of the team. Be sure to add them as a project watcher."
24   - redirect_to user_path(@user)
  19 + if user.save
  20 + flash[:success] = "#{user.name} is now part of the team. Be sure to add them as a project watcher."
  21 + redirect_to user_path(user)
25 22 else
26 23 render :new
27 24 end
28 25 end
29 26  
30 27 def update
31   - # Devise Hack
32   - if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
33   - params[:user].delete(:password)
34   - params[:user].delete(:password_confirmation)
35   - end
36   -
37   - # Set protected attributes
38   - @user.admin = params[:user][:admin] if current_user.admin?
39   -
40   - if @user.update_attributes(params[:user])
41   - flash[:success] = "#{@user.name}'s information was successfully updated"
42   - redirect_to user_path(@user)
  28 + if user.update_attributes(user_params)
  29 + flash[:success] = I18n.t('controllers.users.flash.update.success', :name => user.name)
  30 + redirect_to user_path(user)
43 31 else
44 32 render :edit
45 33 end
46 34 end
47 35  
  36 + ##
  37 + # Destroy the user pass in args
  38 + #
  39 + # @param [ String ] id the id of user we want delete
  40 + #
48 41 def destroy
49   - @user.destroy
50   -
51   - flash[:success] = "That's sad. #{@user.name} is no longer part of your team."
  42 + if user == current_user
  43 + flash[:error] = I18n.t('controllers.users.flash.destroy.error')
  44 + else
  45 + UserDestroy.new(user).destroy
  46 + flash[:success] = I18n.t('controllers.users.flash.destroy.success', :name => user.name)
  47 + end
52 48 redirect_to users_path
53 49 end
54 50  
55 51 def unlink_github
56   - @user.update_attributes :github_login => nil, :github_oauth_token => nil
57   - redirect_to user_path(@user)
  52 + user.update_attributes :github_login => nil, :github_oauth_token => nil
  53 + redirect_to user_path(user)
58 54 end
59 55  
60 56 protected
61 57  
62   - def find_user
63   - @user = User.find(params[:id])
64   - end
65   -
66 58 def require_user_edit_priviledges
67   - can_edit = current_user == @user || current_user.admin?
  59 + can_edit = current_user == user || current_user.admin?
68 60 redirect_to(root_path) and return(false) unless can_edit
69 61 end
70 62  
  63 + def user_params
  64 + @user_params ||= params[:user] ? params.require(:user).permit(*user_permit_params) : {}
  65 + end
  66 +
  67 + def user_permit_params
  68 + @user_permit_params ||= [:name,:username, :email, :github_login, :per_page, :time_zone]
  69 + @user_permit_params << :admin if current_user.admin? && current_user.id != params[:id]
  70 + @user_permit_params |= [:password, :password_confirmation] if user_password_params.values.all?{|pa| !pa.blank? }
  71 + @user_permit_params
  72 + end
  73 +
  74 + def user_password_params
  75 + @user_password_params ||= params[:user] ? params.require(:user).permit(:password, :password_confirmation) : {}
  76 + end
  77 +
71 78 end
72 79  
... ...
app/interactors/user_destroy.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class UserDestroy
  2 + def initialize(user)
  3 + @user = user
  4 + end
  5 +
  6 + def destroy
  7 + @user.destroy
  8 + @user.watchers.each(&:destroy)
  9 + end
  10 +
  11 +end
... ...
app/models/user.rb
... ... @@ -13,14 +13,11 @@ class User
13 13 field :per_page, :type => Fixnum, :default => PER_PAGE
14 14 field :time_zone, :default => "UTC"
15 15  
16   - after_destroy :destroy_watchers
17 16 before_save :ensure_authentication_token
18 17  
19 18 validates_presence_of :name
20 19 validates_uniqueness_of :github_login, :allow_nil => true
21 20  
22   - attr_protected :admin
23   -
24 21 has_many :apps, :foreign_key => 'watchers.user_id'
25 22  
26 23 if Errbit::Config.user_has_username
... ... @@ -59,10 +56,5 @@ class User
59 56 self[:github_login] = login
60 57 end
61 58  
62   - protected
63   -
64   - def destroy_watchers
65   - watchers.each(&:destroy)
66   - end
67 59 end
68 60  
... ...
app/views/users/_fields.html.haml
1   -= errors_for @user
  1 += errors_for user
2 2  
3 3 .required
4 4 = f.label :name
... ...
app/views/users/edit.html.haml
1   -- content_for :title, "Edit #{@user.name}"
  1 +- content_for :title, "Edit #{user.name}"
2 2 - content_for :action_bar do
3   - = render 'shared/link_github_account', :user => @user
4   - = link_to('cancel', user_path(@user), :class => 'button')
  3 + = render 'shared/link_github_account', :user => user
  4 + = link_to('cancel', user_path(user), :class => 'button')
5 5  
6   -= form_for @user, :html => {:autocomplete => "off"} do |f|
7   - = @user.errors.full_messages.to_sentence
  6 += form_for user, :html => {:autocomplete => "off"} do |f|
  7 + = user.errors.full_messages.to_sentence
8 8 = render 'fields', :f => f
9 9  
10 10 %div.buttons= f.submit 'Update User'
... ...
app/views/users/index.html.haml
... ... @@ -13,8 +13,8 @@
13 13 %th.main Email
14 14 %th Admin?
15 15 %tbody
16   - - @users.each do |user|
17   - %tr
  16 + - users.each do |user|
  17 + %tr.user_list
18 18 - if Errbit::Config.use_gravatar
19 19 %td= gravatar_tag user.email, :s => 24
20 20 %td.nowrap= link_to user.name, user_path(user)
... ... @@ -22,5 +22,5 @@
22 22 %td= user.username
23 23 %td= user.email
24 24 %td= user.admin? ? 'Y' : 'N'
25   -= paginate @users
  25 += paginate users
26 26  
... ...
app/views/users/new.html.haml
1 1 - content_for :title, 'New User'
2 2 - content_for :action_bar, link_to('cancel', users_path, :class => 'button')
3 3  
4   -= form_for @user do |f|
5   -
  4 += form_for user do |f|
  5 +
6 6 = render 'fields', :f => f
7   -
8   - %div.buttons= f.submit 'Add User'
9 7 \ No newline at end of file
  8 +
  9 + %div.buttons= f.submit 'Add User'
... ...
app/views/users/show.html.haml
1   -- content_for :title, @user.name
2   -- if Errbit::Config.use_gravatar && gravatar = gravatar_url(@user.email, :s => 86)
  1 +- content_for :title, user.name
  2 +
  3 +- if Errbit::Config.use_gravatar && gravatar = gravatar_url(user.email, :s => 86)
3 4 - content_for :title_style do
4 5 background: url('#{gravatar}') no-repeat;
5 6 padding-left: 106px;
6 7  
7 8 - content_for :action_bar do
8   - = render 'shared/link_github_account', :user => @user
  9 + = render 'shared/link_github_account'
9 10 %span= link_to('Add a New User', new_user_path, :class => 'add')
10   - = link_to 'edit', edit_user_path(@user), :class => 'button'
11   - = link_to 'destroy', user_path(@user), :method => :delete, :data => { :confirm => 'Seriously?' }, :class => 'button'
  11 + = link_to 'edit', edit_user_path(user), :class => 'button'
  12 + = link_to 'destroy', user_path(user), :method => :delete, :data => { :confirm => 'Seriously?' }, :class => 'button'
12 13  
13 14 %table.single_user
14 15 %tr
15 16 %th Email
16   - %td.main= @user.email
  17 + %td.main= user.email
17 18 - if Errbit::Config.user_has_username
18 19 %tr
19 20 %th Username
20   - %td.main= @user.username
21   - - if Errbit::Config.github_authentication && @user.github_login.present?
  21 + %td.main= user.username
  22 + - if Errbit::Config.github_authentication && user.github_login.present?
22 23 %tr
23 24 %th GitHub Login
24   - %td.main= link_to @user.github_login, "https://github.com/#{@user.github_login}"
  25 + %td.main= link_to user.github_login, "https://github.com/#{user.github_login}"
25 26 %tr
26 27 %th Admin?
27   - %td= @user.admin? ? 'Y' : 'N'
  28 + %td= user.admin? ? 'Y' : 'N'
28 29 %tr
29 30 %th Created
30   - %td= @user.created_at.to_s(:micro)
  31 + %td= user.created_at.to_s(:micro)
31 32  
... ...
config/locales/en.yml
... ... @@ -14,3 +14,13 @@ en:
14 14 n_errs_have:
15 15 one: "%{count} err has"
16 16 other: "%{count} errs have"
  17 +
  18 +
  19 + controllers:
  20 + users:
  21 + flash:
  22 + destroy:
  23 + success: "That's sad. %{name} is no longer part of your team."
  24 + error: "You can't delete yourself"
  25 + update:
  26 + success: "%{name}'s information was successfully updated."
... ...
spec/controllers/users_controller_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe UsersController do
4   - render_views
5 4  
6 5 it_requires_authentication
7 6 it_requires_admin_privileges :for => {
... ... @@ -12,42 +11,39 @@ describe UsersController do
12 11 :destroy => :delete
13 12 }
14 13  
  14 + let(:admin) { Fabricate(:admin) }
  15 + let(:user) { Fabricate(:user) }
  16 + let(:other_user) { Fabricate(:user) }
  17 +
15 18 context 'Signed in as a regular user' do
  19 +
16 20 before do
17   - sign_in @user = Fabricate(:user)
  21 + sign_in user
18 22 end
19 23  
20 24 it "should set a time zone" do
21   - Time.zone.should.to_s == @user.time_zone
  25 + Time.zone.should.to_s == user.time_zone
22 26 end
23 27  
24 28 context "GET /users/:other_id/edit" do
25 29 it "redirects to the home page" do
26   - get :edit, :id => Fabricate(:user).id
  30 + get :edit, :id => other_user.id
27 31 response.should redirect_to(root_path)
28 32 end
29 33 end
30 34  
31 35 context "GET /users/:my_id/edit" do
32 36 it 'finds the user' do
33   - get :edit, :id => @user.id
34   - assigns(:user).should == @user
35   - end
36   -
37   - it "should have per_page option" do
38   - get :edit, :id => @user.id
39   - response.body.should match(/id="user_per_page"/)
  37 + get :edit, :id => user.id
  38 + controller.user.should == user
  39 + expect(response).to render_template 'edit'
40 40 end
41 41  
42   - it "should have time_zone option" do
43   - get :edit, :id => @user.id
44   - response.body.should match(/id="user_time_zone"/)
45   - end
46 42 end
47 43  
48 44 context "PUT /users/:other_id" do
49 45 it "redirects to the home page" do
50   - put :update, :id => Fabricate(:user).id
  46 + put :update, :id => other_user.id
51 47 response.should redirect_to(root_path)
52 48 end
53 49 end
... ... @@ -55,44 +51,47 @@ describe UsersController do
55 51 context "PUT /users/:my_id/id" do
56 52 context "when the update is successful" do
57 53 it "sets a message to display" do
58   - put :update, :id => @user.to_param, :user => {:name => 'Kermit'}
  54 + put :update, :id => user.to_param, :user => {:name => 'Kermit'}
59 55 request.flash[:success].should include('updated')
60 56 end
61 57  
62 58 it "redirects to the user's page" do
63   - put :update, :id => @user.to_param, :user => {:name => 'Kermit'}
64   - response.should redirect_to(user_path(@user))
  59 + put :update, :id => user.to_param, :user => {:name => 'Kermit'}
  60 + response.should redirect_to(user_path(user))
65 61 end
66 62  
67 63 it "should not be able to become an admin" do
68   - put :update, :id => @user.to_param, :user => {:admin => true}
69   - @user.reload.admin.should be_false
  64 + expect {
  65 + put :update, :id => user.to_param, :user => {:admin => true}
  66 + }.to_not change {
  67 + user.reload.admin
  68 + }.from(false)
70 69 end
71 70  
72 71 it "should be able to set per_page option" do
73   - put :update, :id => @user.to_param, :user => {:per_page => 555}
74   - @user.reload.per_page.should == 555
  72 + put :update, :id => user.to_param, :user => {:per_page => 555}
  73 + user.reload.per_page.should == 555
75 74 end
76 75  
77 76 it "should be able to set time_zone option" do
78   - put :update, :id => @user.to_param, :user => {:time_zone => "Warsaw"}
79   - @user.reload.time_zone.should == "Warsaw"
  77 + put :update, :id => user.to_param, :user => {:time_zone => "Warsaw"}
  78 + user.reload.time_zone.should == "Warsaw"
80 79 end
81 80  
82 81 it "should be able to not set github_login option" do
83   - put :update, :id => @user.to_param, :user => {:github_login => " "}
84   - @user.reload.github_login.should == nil
  82 + put :update, :id => user.to_param, :user => {:github_login => " "}
  83 + user.reload.github_login.should == nil
85 84 end
86 85  
87 86 it "should be able to set github_login option" do
88   - put :update, :id => @user.to_param, :user => {:github_login => "awesome_name"}
89   - @user.reload.github_login.should == "awesome_name"
  87 + put :update, :id => user.to_param, :user => {:github_login => "awesome_name"}
  88 + user.reload.github_login.should == "awesome_name"
90 89 end
91 90 end
92 91  
93 92 context "when the update is unsuccessful" do
94 93 it "renders the edit page" do
95   - put :update, :id => @user.to_param, :user => {:name => nil}
  94 + put :update, :id => user.to_param, :user => {:name => nil}
96 95 response.should render_template(:edit)
97 96 end
98 97 end
... ... @@ -101,81 +100,82 @@ describe UsersController do
101 100  
102 101 context 'Signed in as an admin' do
103 102 before do
104   - @user = Fabricate(:admin)
105   - sign_in @user
  103 + sign_in admin
106 104 end
107 105  
108 106 context "GET /users" do
  107 +
109 108 it 'paginates all users' do
110   - @user.update_attribute :per_page, 2
111   - users = 3.times { Fabricate(:user) }
  109 + admin.update_attribute :per_page, 2
  110 + users = 3.times {
  111 + Fabricate(:user)
  112 + }
112 113 get :index
113   - assigns(:users).to_a.size.should == 2
  114 + controller.users.to_a.size.should == 2
114 115 end
  116 +
115 117 end
116 118  
117 119 context "GET /users/:id" do
118 120 it 'finds the user' do
119   - user = Fabricate(:user)
120 121 get :show, :id => user.id
121   - assigns(:user).should == user
  122 + controller.user.should == user
122 123 end
123 124 end
124 125  
125 126 context "GET /users/new" do
126 127 it 'assigns a new user' do
127 128 get :new
128   - assigns(:user).should be_a(User)
129   - assigns(:user).should be_new_record
  129 + controller.user.should be_a(User)
  130 + controller.user.should be_new_record
130 131 end
131 132 end
132 133  
133 134 context "GET /users/:id/edit" do
134 135 it 'finds the user' do
135   - user = Fabricate(:user)
136 136 get :edit, :id => user.id
137   - assigns(:user).should == user
  137 + controller.user.should == user
138 138 end
139 139 end
140 140  
141 141 context "POST /users" do
142 142 context "when the create is successful" do
143   - before do
144   - @attrs = {:user => Fabricate.attributes_for(:user)}
145   - end
  143 + let(:attrs) { {:user => Fabricate.attributes_for(:user)} }
146 144  
147 145 it "sets a message to display" do
148   - post :create, @attrs
  146 + post :create, attrs
149 147 request.flash[:success].should include('part of the team')
150 148 end
151 149  
152 150 it "redirects to the user's page" do
153   - post :create, @attrs
154   - response.should redirect_to(user_path(assigns(:user)))
  151 + post :create, attrs
  152 + response.should redirect_to(user_path(controller.user))
155 153 end
156 154  
157 155 it "should be able to create admin" do
158   - @attrs[:user][:admin] = true
159   - post :create, @attrs
  156 + attrs[:user][:admin] = true
  157 + post :create, attrs
160 158 response.should be_redirect
161   - User.find(assigns(:user).to_param).admin.should be_true
  159 + User.find(controller.user.to_param).admin.should be_true
162 160 end
163 161  
164 162 it "should has auth token" do
165   - post :create, @attrs
  163 + post :create, attrs
166 164 User.last.authentication_token.should_not be_blank
167 165 end
168 166 end
169 167  
170 168 context "when the create is unsuccessful" do
  169 + let(:user) {
  170 + Struct.new(:admin, :attributes).new(true, {})
  171 + }
171 172 before do
172   - @user = Fabricate(:user)
173   - User.should_receive(:new).and_return(@user)
174   - @user.should_receive(:save).and_return(false)
  173 + User.should_receive(:new).and_return(user)
  174 + user.should_receive(:save).and_return(false)
175 175 end
176 176  
177 177 it "renders the new page" do
178   - post :create
  178 + post :create, :user => { :username => 'foo' }
179 179 response.should render_template(:new)
180 180 end
181 181 end
... ... @@ -183,59 +183,85 @@ describe UsersController do
183 183  
184 184 context "PUT /users/:id" do
185 185 context "when the update is successful" do
186   - before do
187   - @user = Fabricate(:user)
188   - end
189   -
190   - it "sets a message to display" do
191   - put :update, :id => @user.to_param, :user => {:name => 'Kermit'}
192   - request.flash[:success].should include('updated')
193   - end
194   -
195   - it "redirects to the user's page" do
196   - put :update, :id => @user.to_param, :user => {:name => 'Kermit'}
197   - response.should redirect_to(user_path(@user))
198   - end
199   -
200   - it "should be able to make user an admin" do
201   - put :update, :id => @user.to_param, :user => {:admin => true}
202   - response.should be_redirect
203   - User.find(assigns(:user).to_param).admin.should be_true
  186 + before {
  187 + put :update, :id => user.to_param, :user => user_params
  188 + }
  189 +
  190 + context "with normal params" do
  191 + let(:user_params) { {:name => 'Kermit'} }
  192 + it "sets a message to display" do
  193 + expect(request.flash[:success]).to eq I18n.t('controllers.users.flash.update.success', :name => user.name)
  194 + expect(response).to redirect_to(user_path(user))
  195 + end
204 196 end
205 197 end
206   -
207 198 context "when the update is unsuccessful" do
208   - before do
209   - @user = Fabricate(:user)
210   - end
211 199  
212 200 it "renders the edit page" do
213   - put :update, :id => @user.to_param, :user => {:name => nil}
  201 + put :update, :id => user.to_param, :user => {:name => nil}
214 202 response.should render_template(:edit)
215 203 end
216 204 end
217 205 end
218 206  
219 207 context "DELETE /users/:id" do
220   - before do
221   - @user = Fabricate(:user)
  208 +
  209 + context "with a destroy success" do
  210 + let(:user_destroy) { mock(:destroy => true) }
  211 +
  212 + before {
  213 + UserDestroy.should_receive(:new).with(user).and_return(user_destroy)
  214 + delete :destroy, :id => user.id
  215 + }
  216 +
  217 + it 'should destroy user' do
  218 + expect(request.flash[:success]).to eq I18n.t('controllers.users.flash.destroy.success', :name => user.name)
  219 + response.should redirect_to(users_path)
  220 + end
222 221 end
223 222  
224   - it "destroys the user" do
225   - delete :destroy, :id => @user.id
226   - User.where(:id => @user.id).first.should be_nil
  223 + context "with trying destroy himself" do
  224 + before {
  225 + UserDestroy.should_not_receive(:new)
  226 + delete :destroy, :id => admin.id
  227 + }
  228 +
  229 + it 'should not destroy user' do
  230 + response.should redirect_to(users_path)
  231 + expect(request.flash[:error]).to eq I18n.t('controllers.users.flash.destroy.error')
  232 + end
227 233 end
  234 + end
228 235  
229   - it "redirects to the users index page" do
230   - delete :destroy, :id => @user.id
231   - response.should redirect_to(users_path)
  236 + describe "#user_params" do
  237 + context "with current user not admin" do
  238 + before {
  239 + controller.stub(:current_user).and_return(user)
  240 + controller.stub(:params).and_return(ActionController::Parameters.new(user_param))
  241 + }
  242 + let(:user_param) { {'user' => { :name => 'foo', :admin => true }} }
  243 + it 'not have admin field' do
  244 + expect(controller.send(:user_params)).to eq ({'name' => 'foo'})
  245 + end
  246 + context "with password and password_confirmation empty?" do
  247 + let(:user_param) { {'user' => { :name => 'foo', 'password' => '', 'password_confirmation' => '' }} }
  248 + it 'not have password and password_confirmation field' do
  249 + expect(controller.send(:user_params)).to eq ({'name' => 'foo'})
  250 + end
  251 + end
232 252 end
233 253  
234   - it "sets a message to display" do
235   - delete :destroy, :id => @user.id
236   - request.flash[:success].should include('no longer part of your team')
  254 + context "with current user admin" do
  255 + it 'have admin field'
  256 + context "with password and password_confirmation empty?" do
  257 + it 'not have password and password_confirmation field'
  258 + end
  259 + context "on his own user" do
  260 + it 'not have admin field'
  261 + end
237 262 end
238 263 end
  264 +
239 265 end
240 266  
241 267 end
... ...
spec/fabricators/app_fabricator.rb
... ... @@ -4,7 +4,9 @@ Fabricator(:app) do
4 4 end
5 5  
6 6 Fabricator(:app_with_watcher, :from => :app) do
7   - watchers(:count => 1) { |parent, i| Fabricate.build(:watcher, :app => parent) }
  7 + watchers(:count => 1) { |parent, i|
  8 + Fabricate.build(:watcher, :app => parent)
  9 + }
8 10 end
9 11  
10 12 Fabricator(:watcher) do
... ...
spec/interactors/user_destroy_spec.rb 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +require 'spec_helper'
  2 +
  3 +describe UserDestroy do
  4 + let(:app) { Fabricate(
  5 + :app,
  6 + :watchers => [
  7 + Fabricate.build(:user_watcher, :user => user)
  8 + ])
  9 + }
  10 +
  11 + describe "#destroy" do
  12 + let!(:user) { Fabricate(:user) }
  13 + it 'should delete user' do
  14 + expect {
  15 + UserDestroy.new(user).destroy
  16 + }.to change(User, :count)
  17 + end
  18 +
  19 + it 'should delete watcher' do
  20 + expect {
  21 + UserDestroy.new(user).destroy
  22 + }.to change{
  23 + app.reload.watchers.where(:user_id => user.id).count
  24 + }.from(1).to(0)
  25 + end
  26 + end
  27 +end
... ...
spec/models/user_spec.rb
... ... @@ -49,15 +49,6 @@ describe User do
49 49 user.watchers.should include(watcher)
50 50 end
51 51  
52   - it "destroys any related watchers when it is destroyed" do
53   - user = Fabricate(:user)
54   - app = Fabricate(:app)
55   - watcher = Fabricate(:user_watcher, :app => app, :user => user)
56   - user.watchers.should_not be_empty
57   - user.destroy
58   - app.reload.watchers.should_not include(watcher)
59   - end
60   -
61 52 it "has many apps through watchers" do
62 53 user = Fabricate(:user)
63 54 watched_app = Fabricate(:app)
... ...
spec/views/users/edit.html.haml_spec.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +require 'spec_helper'
  2 +
  3 +describe 'users/edit.html.haml' do
  4 + let(:user) { stub_model(User, :name => 'shingara') }
  5 + before {
  6 + view.stub(:current_user).and_return(user)
  7 + view.stub(:user).and_return(user)
  8 + }
  9 + it 'should have per_page option' do
  10 + render
  11 + expect(rendered).to match(/id="user_per_page"/)
  12 + end
  13 +
  14 + it 'should have time_zone option' do
  15 + render
  16 + expect(rendered).to match(/id="user_time_zone"/)
  17 + end
  18 +end
... ...
spec/views/users/index.html.haml_spec.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +require 'spec_helper'
  2 +
  3 +describe 'users/index.html.haml' do
  4 + let(:user) { stub_model(User) }
  5 + before {
  6 + view.stub(:current_user).and_return(user)
  7 + view.stub(:users).and_return(
  8 + Kaminari.paginate_array([user], :total_count => 1).page(1)
  9 + )
  10 + }
  11 + it 'should see users option' do
  12 + render
  13 + expect(rendered).to match(/class='user_list'/)
  14 + end
  15 +
  16 +end
... ...
spec/views/users/new.html.haml_spec.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +require 'spec_helper'
  2 +
  3 +describe 'users/new.html.haml' do
  4 + let(:user) { stub_model(User) }
  5 + before {
  6 + view.stub(:current_user).and_return(user)
  7 + view.stub(:user).and_return(user)
  8 + }
  9 + it 'should have per_page option' do
  10 + render
  11 + expect(rendered).to match(/id="user_per_page"/)
  12 + end
  13 +
  14 + it 'should have time_zone option' do
  15 + render
  16 + expect(rendered).to match(/id="user_time_zone"/)
  17 + end
  18 +end
... ...
spec/views/users/show.html.haml_spec.rb
1 1 require 'spec_helper'
2 2  
3 3 describe 'users/show.html.haml' do
  4 +
4 5 let(:user) do
5 6 stub_model(User, :created_at => Time.now, :email => "test@example.com")
6 7 end
... ... @@ -8,12 +9,12 @@ describe &#39;users/show.html.haml&#39; do
8 9 before do
9 10 Errbit::Config.stub(:github_authentication) { true }
10 11 controller.stub(:current_user) { stub_model(User) }
  12 + view.stub(:user) { user }
11 13 end
12 14  
13 15 context 'with GitHub authentication' do
14 16 it 'shows github login' do
15 17 user.github_login = 'test_user'
16   - assign :user, user
17 18 render
18 19 rendered.should match(/GitHub/)
19 20 rendered.should match(/test_user/)
... ... @@ -21,7 +22,6 @@ describe &#39;users/show.html.haml&#39; do
21 22  
22 23 it 'does not show github if blank' do
23 24 user.github_login = ' '
24   - assign :user, user
25 25 render
26 26 rendered.should_not match(/GitHub/)
27 27 end
... ... @@ -30,7 +30,6 @@ describe &#39;users/show.html.haml&#39; do
30 30 context "Linking GitHub account" do
31 31 context 'viewing another user page' do
32 32 it "doesn't show and github linking buttons if user is not current user" do
33   - assign :user, user
34 33 render
35 34 view.content_for(:action_bar).should_not include('Link GitHub account')
36 35 view.content_for(:action_bar).should_not include('Unlink GitHub account')
... ... @@ -40,7 +39,6 @@ describe &#39;users/show.html.haml&#39; do
40 39 context 'viewing own user page' do
41 40 before do
42 41 controller.stub(:current_user) { user }
43   - assign :user, user
44 42 end
45 43  
46 44 it 'shows link github button when no login or token' do
... ...