Commit ac4a09e9cca9018dbb8e52446078b91a4b87e410

Authored by Sebastian Ziebell
2 parents 9ee6c58a 135418dc

Merge branch 'master' into fixes/api

Showing 113 changed files with 2529 additions and 2325 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 113 files displayed.

1 language: ruby 1 language: ruby
2 env: 2 env:
3 - - DB=postgresql  
4 - DB=mysql 3 - DB=mysql
5 before_install: 4 before_install:
6 - sudo apt-get install libicu-dev -y 5 - sudo apt-get install libicu-dev -y
1 v 5.0.0 1 v 5.0.0
2 - Replaced gitolite with gitlab-shell 2 - Replaced gitolite with gitlab-shell
  3 + - Removed gitolite-related libraries
  4 + - State machine added
  5 + - Setup gitlab as git user
  6 + - Internal API
  7 + - Show team tab for empty projects
  8 + - Import repository feature
  9 + - Updated rails
  10 + - Use lambda for scopes
  11 + - Redesign admin area -> users
  12 + - Redesign admin area -> user
  13 + - Secure link to file attachments
  14 + - Add validations for Group and Team names
  15 + - Restyle team page for project
  16 + - Update capybara, rspec-rails, poltergeist to recent versions
3 17
4 v 4.2.0 18 v 4.2.0
5 - Teams 19 - Teams
6 - User show page. Via /u/username 20 - User show page. Via /u/username
7 - Show help contents on pages for better navigation 21 - Show help contents on pages for better navigation
  22 + - Async gitolite calls
  23 + - added satellites logs
  24 + - can_create_group, can_create_team booleans for User
  25 + - Process web hooks async
  26 + - GFM: Fix images escaped inside links
  27 + - Network graph improved
  28 + - Switchable branches for network graph
  29 + - API: Groups
  30 + - Fixed project download
8 31
9 v 4.1.0 32 v 4.1.0
10 - Optional Sign-Up 33 - Optional Sign-Up
@@ -22,7 +22,7 @@ gem 'omniauth-twitter' @@ -22,7 +22,7 @@ gem 'omniauth-twitter'
22 gem 'omniauth-github' 22 gem 'omniauth-github'
23 23
24 # GITLAB patched libs 24 # GITLAB patched libs
25 -gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '7f35cb98ff17d534a07e3ce6ec3d580f67402837' 25 +gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '9e98418ce2d654485b967003726aa2706a10060b'
26 gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8' 26 gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'
27 gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '8e6afc2da821354774aa4d1ee8a1aa2082f84a3e' 27 gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '8e6afc2da821354774aa4d1ee8a1aa2082f84a3e'
28 28
@@ -81,8 +81,8 @@ gem "draper", "~> 0.18.0" @@ -81,8 +81,8 @@ gem "draper", "~> 0.18.0"
81 81
82 # Background jobs 82 # Background jobs
83 gem 'slim' 83 gem 'slim'
84 -gem 'sinatra', :require => nil  
85 -gem 'sidekiq', '2.6.4' 84 +gem 'sinatra', require: nil
  85 +gem 'sidekiq', '2.7.3'
86 86
87 # HTTP requests 87 # HTTP requests
88 gem "httparty" 88 gem "httparty"
@@ -134,12 +134,12 @@ end @@ -134,12 +134,12 @@ end
134 134
135 group :development, :test do 135 group :development, :test do
136 gem 'rails-dev-tweaks' 136 gem 'rails-dev-tweaks'
137 - gem 'spinach-rails'  
138 - gem "rspec-rails"  
139 - gem "capybara" 137 + gem 'spinach-rails', '0.2.0'
  138 + gem "rspec-rails", '2.12.2'
  139 + gem "capybara", '2.0.2'
140 gem "pry" 140 gem "pry"
141 gem "awesome_print" 141 gem "awesome_print"
142 - gem "database_cleaner", ref: "f89c34300e114be99532f14c115b2799a3380ac6", git: "https://github.com/bmabey/database_cleaner.git" 142 + gem "database_cleaner", ref: "9f898fc50d87a5d51760f9dcf374bf5ffda21baf", git: "https://github.com/bmabey/database_cleaner.git"
143 gem "launchy" 143 gem "launchy"
144 gem 'factory_girl_rails' 144 gem 'factory_girl_rails'
145 145
@@ -153,7 +153,7 @@ group :development, :test do @@ -153,7 +153,7 @@ group :development, :test do
153 gem 'rb-inotify', require: linux_only('rb-inotify') 153 gem 'rb-inotify', require: linux_only('rb-inotify')
154 154
155 # PhantomJS driver for Capybara 155 # PhantomJS driver for Capybara
156 - gem 'poltergeist', git: 'https://github.com/jonleighton/poltergeist.git', ref: '5c2e092001074a8cf09f332d3714e9ba150bc8ca' 156 + gem 'poltergeist', '1.1.0'
157 end 157 end
158 158
159 group :test do 159 group :test do
1 GIT 1 GIT
2 remote: https://github.com/bmabey/database_cleaner.git 2 remote: https://github.com/bmabey/database_cleaner.git
3 - revision: f89c34300e114be99532f14c115b2799a3380ac6  
4 - ref: f89c34300e114be99532f14c115b2799a3380ac6 3 + revision: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
  4 + ref: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
5 specs: 5 specs:
6 database_cleaner (0.9.1) 6 database_cleaner (0.9.1)
7 7
@@ -23,8 +23,8 @@ GIT @@ -23,8 +23,8 @@ GIT
23 23
24 GIT 24 GIT
25 remote: https://github.com/gitlabhq/grit.git 25 remote: https://github.com/gitlabhq/grit.git
26 - revision: 7f35cb98ff17d534a07e3ce6ec3d580f67402837  
27 - ref: 7f35cb98ff17d534a07e3ce6ec3d580f67402837 26 + revision: 9e98418ce2d654485b967003726aa2706a10060b
  27 + ref: 9e98418ce2d654485b967003726aa2706a10060b
28 specs: 28 specs:
29 grit (2.5.0) 29 grit (2.5.0)
30 diff-lcs (~> 1.1) 30 diff-lcs (~> 1.1)
@@ -54,18 +54,6 @@ GIT @@ -54,18 +54,6 @@ GIT
54 specs: 54 specs:
55 raphael-rails (2.1.0) 55 raphael-rails (2.1.0)
56 56
57 -GIT  
58 - remote: https://github.com/jonleighton/poltergeist.git  
59 - revision: 5c2e092001074a8cf09f332d3714e9ba150bc8ca  
60 - ref: 5c2e092001074a8cf09f332d3714e9ba150bc8ca  
61 - specs:  
62 - poltergeist (1.0.2)  
63 - capybara (~> 1.1)  
64 - childprocess (~> 0.3)  
65 - faye-websocket (~> 0.4, >= 0.4.4)  
66 - http_parser.rb (~> 0.5.3)  
67 - multi_json (~> 1.0)  
68 -  
69 GEM 57 GEM
70 remote: http://rubygems.org/ 58 remote: http://rubygems.org/
71 specs: 59 specs:
@@ -110,13 +98,13 @@ GEM @@ -110,13 +98,13 @@ GEM
110 bootstrap-sass (2.2.1.1) 98 bootstrap-sass (2.2.1.1)
111 sass (~> 3.2) 99 sass (~> 3.2)
112 builder (3.0.4) 100 builder (3.0.4)
113 - capybara (1.1.3) 101 + capybara (2.0.2)
114 mime-types (>= 1.16) 102 mime-types (>= 1.16)
115 nokogiri (>= 1.3.3) 103 nokogiri (>= 1.3.3)
116 rack (>= 1.0.0) 104 rack (>= 1.0.0)
117 rack-test (>= 0.5.4) 105 rack-test (>= 0.5.4)
118 selenium-webdriver (~> 2.0) 106 selenium-webdriver (~> 2.0)
119 - xpath (~> 0.1.4) 107 + xpath (~> 1.0.0)
120 carrierwave (0.7.1) 108 carrierwave (0.7.1)
121 activemodel (>= 3.2.0) 109 activemodel (>= 3.2.0)
122 activesupport (>= 3.2.0) 110 activesupport (>= 3.2.0)
@@ -124,8 +112,8 @@ GEM @@ -124,8 +112,8 @@ GEM
124 facter (>= 1.6.12) 112 facter (>= 1.6.12)
125 timers (>= 1.0.0) 113 timers (>= 1.0.0)
126 charlock_holmes (0.6.9) 114 charlock_holmes (0.6.9)
127 - childprocess (0.3.6)  
128 - ffi (~> 1.0, >= 1.0.6) 115 + childprocess (0.3.8)
  116 + ffi (~> 1.0, >= 1.0.11)
129 chosen-rails (0.9.8) 117 chosen-rails (0.9.8)
130 railties (~> 3.0) 118 railties (~> 3.0)
131 thor (~> 0.14) 119 thor (~> 0.14)
@@ -169,10 +157,10 @@ GEM @@ -169,10 +157,10 @@ GEM
169 railties (>= 3.0.0) 157 railties (>= 3.0.0)
170 faraday (0.8.4) 158 faraday (0.8.4)
171 multipart-post (~> 1.1) 159 multipart-post (~> 1.1)
172 - faye-websocket (0.4.6) 160 + faye-websocket (0.4.7)
173 eventmachine (>= 0.12.0) 161 eventmachine (>= 0.12.0)
174 ffaker (1.15.0) 162 ffaker (1.15.0)
175 - ffi (1.1.5) 163 + ffi (1.4.0)
176 font-awesome-sass-rails (3.0.0.1) 164 font-awesome-sass-rails (3.0.0.1)
177 railties (>= 3.1.1) 165 railties (>= 3.1.1)
178 sass-rails (>= 3.1.1) 166 sass-rails (>= 3.1.1)
@@ -249,8 +237,6 @@ GEM @@ -249,8 +237,6 @@ GEM
249 letter_opener (1.0.0) 237 letter_opener (1.0.0)
250 launchy (>= 2.0.4) 238 launchy (>= 2.0.4)
251 libv8 (3.3.10.4) 239 libv8 (3.3.10.4)
252 - libwebsocket (0.1.6)  
253 - websocket  
254 listen (0.5.3) 240 listen (0.5.3)
255 lumberjack (1.0.2) 241 lumberjack (1.0.2)
256 mail (2.4.4) 242 mail (2.4.4)
@@ -261,12 +247,12 @@ GEM @@ -261,12 +247,12 @@ GEM
261 mime-types (1.21) 247 mime-types (1.21)
262 modernizr (2.6.2) 248 modernizr (2.6.2)
263 sprockets (~> 2.0) 249 sprockets (~> 2.0)
264 - multi_json (1.5.1) 250 + multi_json (1.6.1)
265 multi_xml (0.5.1) 251 multi_xml (0.5.1)
266 multipart-post (1.1.5) 252 multipart-post (1.1.5)
267 mysql2 (0.3.11) 253 mysql2 (0.3.11)
268 net-ldap (0.2.2) 254 net-ldap (0.2.2)
269 - nokogiri (1.5.5) 255 + nokogiri (1.5.6)
270 oauth (0.4.7) 256 oauth (0.4.7)
271 oauth2 (0.8.0) 257 oauth2 (0.8.0)
272 faraday (~> 0.8) 258 faraday (~> 0.8)
@@ -294,6 +280,10 @@ GEM @@ -294,6 +280,10 @@ GEM
294 omniauth-oauth (~> 1.0) 280 omniauth-oauth (~> 1.0)
295 orm_adapter (0.4.0) 281 orm_adapter (0.4.0)
296 pg (0.14.1) 282 pg (0.14.1)
  283 + poltergeist (1.1.0)
  284 + capybara (~> 2.0, >= 2.0.1)
  285 + faye-websocket (~> 0.4, >= 0.4.4)
  286 + http_parser.rb (~> 0.5.3)
297 polyglot (0.3.3) 287 polyglot (0.3.3)
298 posix-spawn (0.3.6) 288 posix-spawn (0.3.6)
299 progressbar (0.12.0) 289 progressbar (0.12.0)
@@ -364,7 +354,7 @@ GEM @@ -364,7 +354,7 @@ GEM
364 rspec-expectations (2.12.0) 354 rspec-expectations (2.12.0)
365 diff-lcs (~> 1.1.3) 355 diff-lcs (~> 1.1.3)
366 rspec-mocks (2.12.0) 356 rspec-mocks (2.12.0)
367 - rspec-rails (2.12.0) 357 + rspec-rails (2.12.2)
368 actionpack (>= 3.0) 358 actionpack (>= 3.0)
369 activesupport (>= 3.0) 359 activesupport (>= 3.0)
370 railties (>= 3.0) 360 railties (>= 3.0)
@@ -384,16 +374,16 @@ GEM @@ -384,16 +374,16 @@ GEM
384 seed-fu (2.2.0) 374 seed-fu (2.2.0)
385 activerecord (~> 3.1) 375 activerecord (~> 3.1)
386 activesupport (~> 3.1) 376 activesupport (~> 3.1)
387 - selenium-webdriver (2.26.0) 377 + selenium-webdriver (2.30.0)
388 childprocess (>= 0.2.5) 378 childprocess (>= 0.2.5)
389 - libwebsocket (~> 0.1.3)  
390 multi_json (~> 1.0) 379 multi_json (~> 1.0)
391 rubyzip 380 rubyzip
  381 + websocket (~> 1.0.4)
392 settingslogic (2.0.8) 382 settingslogic (2.0.8)
393 sexp_processor (4.1.3) 383 sexp_processor (4.1.3)
394 shoulda-matchers (1.3.0) 384 shoulda-matchers (1.3.0)
395 activesupport (>= 3.0.0) 385 activesupport (>= 3.0.0)
396 - sidekiq (2.6.4) 386 + sidekiq (2.7.3)
397 celluloid (~> 0.12.0) 387 celluloid (~> 0.12.0)
398 connection_pool (~> 1.0) 388 connection_pool (~> 1.0)
399 multi_json (~> 1) 389 multi_json (~> 1)
@@ -412,11 +402,11 @@ GEM @@ -412,11 +402,11 @@ GEM
412 temple (~> 0.5.5) 402 temple (~> 0.5.5)
413 tilt (~> 1.3.3) 403 tilt (~> 1.3.3)
414 slop (3.3.3) 404 slop (3.3.3)
415 - spinach (0.5.2) 405 + spinach (0.7.0)
416 colorize 406 colorize
417 gherkin-ruby (~> 0.2.0) 407 gherkin-ruby (~> 0.2.0)
418 - spinach-rails (0.1.8)  
419 - capybara (~> 1) 408 + spinach-rails (0.2.0)
  409 + capybara (~> 2.0.0)
420 railties (>= 3) 410 railties (>= 3)
421 spinach (>= 0.4) 411 spinach (>= 0.4)
422 sprockets (2.2.2) 412 sprockets (2.2.2)
@@ -436,7 +426,7 @@ GEM @@ -436,7 +426,7 @@ GEM
436 rack (>= 1.0.0) 426 rack (>= 1.0.0)
437 thor (0.17.0) 427 thor (0.17.0)
438 tilt (1.3.3) 428 tilt (1.3.3)
439 - timers (1.0.2) 429 + timers (1.1.0)
440 treetop (1.4.12) 430 treetop (1.4.12)
441 polyglot 431 polyglot
442 polyglot (>= 0.3.1) 432 polyglot (>= 0.3.1)
@@ -455,8 +445,8 @@ GEM @@ -455,8 +445,8 @@ GEM
455 webmock (1.9.0) 445 webmock (1.9.0)
456 addressable (>= 2.2.7) 446 addressable (>= 2.2.7)
457 crack (>= 0.1.7) 447 crack (>= 0.1.7)
458 - websocket (1.0.2)  
459 - xpath (0.1.4) 448 + websocket (1.0.7)
  449 + xpath (1.0.0)
460 nokogiri (~> 1.3) 450 nokogiri (~> 1.3)
461 yajl-ruby (1.1.0) 451 yajl-ruby (1.1.0)
462 452
@@ -470,7 +460,7 @@ DEPENDENCIES @@ -470,7 +460,7 @@ DEPENDENCIES
470 better_errors 460 better_errors
471 binding_of_caller 461 binding_of_caller
472 bootstrap-sass (= 2.2.1.1) 462 bootstrap-sass (= 2.2.1.1)
473 - capybara 463 + capybara (= 2.0.2)
474 carrierwave (~> 0.7.1) 464 carrierwave (~> 0.7.1)
475 chosen-rails (= 0.9.8) 465 chosen-rails (= 0.9.8)
476 coffee-rails (~> 3.2.2) 466 coffee-rails (~> 3.2.2)
@@ -512,7 +502,7 @@ DEPENDENCIES @@ -512,7 +502,7 @@ DEPENDENCIES
512 omniauth-google-oauth2 502 omniauth-google-oauth2
513 omniauth-twitter 503 omniauth-twitter
514 pg 504 pg
515 - poltergeist! 505 + poltergeist (= 1.1.0)
516 pry 506 pry
517 pygments.rb! 507 pygments.rb!
518 quiet_assets (~> 1.0.1) 508 quiet_assets (~> 1.0.1)
@@ -524,18 +514,18 @@ DEPENDENCIES @@ -524,18 +514,18 @@ DEPENDENCIES
524 rb-fsevent 514 rb-fsevent
525 rb-inotify 515 rb-inotify
526 redcarpet (~> 2.2.2) 516 redcarpet (~> 2.2.2)
527 - rspec-rails 517 + rspec-rails (= 2.12.2)
528 sass-rails (~> 3.2.5) 518 sass-rails (~> 3.2.5)
529 sdoc 519 sdoc
530 seed-fu 520 seed-fu
531 settingslogic 521 settingslogic
532 shoulda-matchers (= 1.3.0) 522 shoulda-matchers (= 1.3.0)
533 - sidekiq (= 2.6.4) 523 + sidekiq (= 2.7.3)
534 simplecov 524 simplecov
535 sinatra 525 sinatra
536 six 526 six
537 slim 527 slim
538 - spinach-rails 528 + spinach-rails (= 0.2.0)
539 stamp 529 stamp
540 state_machine 530 state_machine
541 test_after_commit 531 test_after_commit
app/assets/javascripts/merge_requests.js.coffee
@@ -31,7 +31,7 @@ class MergeRequest @@ -31,7 +31,7 @@ class MergeRequest
31 31
32 if this.$('.automerge_widget').length and @opts.check_enable 32 if this.$('.automerge_widget').length and @opts.check_enable
33 $.get @opts.url_to_automerge_check, (data) => 33 $.get @opts.url_to_automerge_check, (data) =>
34 - this.showState( data.state ) 34 + this.showState( data.merge_status )
35 , 'json' 35 , 'json'
36 36
37 if @opts.ci_enable 37 if @opts.ci_enable
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
@@ -24,6 +24,14 @@ @@ -24,6 +24,14 @@
24 background-image: -o-linear-gradient($from, $to); 24 background-image: -o-linear-gradient($from, $to);
25 } 25 }
26 26
  27 +@mixin transition($transition) {
  28 + -webkit-transition: $transition;
  29 + -moz-transition: $transition;
  30 + -ms-transition: $transition;
  31 + -o-transition: $transition;
  32 + transition: $transition;
  33 +}
  34 +
27 /** 35 /**
28 * Prefilled mixins 36 * Prefilled mixins
29 * Mixins with fixed values 37 * Mixins with fixed values
app/assets/stylesheets/sections/header.scss
@@ -90,6 +90,7 @@ header { @@ -90,6 +90,7 @@ header {
90 @include border-radius(3px); 90 @include border-radius(3px);
91 border: 1px solid #c6c6c6; 91 border: 1px solid #c6c6c6;
92 box-shadow: none; 92 box-shadow: none;
  93 + @include transition(all 0.15s ease-in 0s);
93 &:focus { 94 &:focus {
94 @extend .span3; 95 @extend .span3;
95 } 96 }
app/contexts/merge_requests_load_context.rb
@@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext @@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext
14 end 14 end
15 15
16 merge_requests = merge_requests.page(params[:page]).per(20) 16 merge_requests = merge_requests.page(params[:page]).per(20)
17 - merge_requests = merge_requests.includes(:author, :project).order("state, created_at desc") 17 + merge_requests = merge_requests.includes(:author, :project).order("created_at desc")
18 18
19 # Filter by specific assignee_id (or lack thereof)? 19 # Filter by specific assignee_id (or lack thereof)?
20 if params[:assignee_id].present? 20 if params[:assignee_id].present?
app/contexts/test_hook_context.rb
1 class TestHookContext < BaseContext 1 class TestHookContext < BaseContext
2 def execute 2 def execute
3 hook = project.hooks.find(params[:id]) 3 hook = project.hooks.find(params[:id])
4 - commits = project.repository.commits(project.default_branch, nil, 3)  
5 - data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user) 4 + data = GitPushService.new.sample_data(project, current_user)
6 hook.execute(data) 5 hook.execute(data)
7 end 6 end
8 end 7 end
app/controllers/merge_requests_controller.rb
  1 +require 'gitlab/satellite/satellite'
  2 +
1 class MergeRequestsController < ProjectResourceController 3 class MergeRequestsController < ProjectResourceController
2 before_filter :module_enabled 4 before_filter :module_enabled
3 before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status] 5 before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status]
@@ -73,7 +75,7 @@ class MergeRequestsController &lt; ProjectResourceController @@ -73,7 +75,7 @@ class MergeRequestsController &lt; ProjectResourceController
73 if @merge_request.unchecked? 75 if @merge_request.unchecked?
74 @merge_request.check_if_can_be_merged 76 @merge_request.check_if_can_be_merged
75 end 77 end
76 - render json: {merge_status: @merge_request.human_merge_status} 78 + render json: {merge_status: @merge_request.merge_status_name}
77 rescue Gitlab::SatelliteNotExistError 79 rescue Gitlab::SatelliteNotExistError
78 render json: {merge_status: :no_satellite} 80 render json: {merge_status: :no_satellite}
79 end 81 end
app/controllers/profiles_controller.rb
1 class ProfilesController < ApplicationController 1 class ProfilesController < ApplicationController
  2 + include ActionView::Helpers::SanitizeHelper
  3 +
2 before_filter :user 4 before_filter :user
3 layout 'profile' 5 layout 'profile'
4 6
@@ -12,7 +14,7 @@ class ProfilesController &lt; ApplicationController @@ -12,7 +14,7 @@ class ProfilesController &lt; ApplicationController
12 end 14 end
13 15
14 def update 16 def update
15 - if @user.update_attributes(params[:user]) 17 + if @user.update_attributes(user_attributes)
16 flash[:notice] = "Profile was successfully updated" 18 flash[:notice] = "Profile was successfully updated"
17 else 19 else
18 flash[:alert] = "Failed to update profile" 20 flash[:alert] = "Failed to update profile"
@@ -65,4 +67,17 @@ class ProfilesController &lt; ApplicationController @@ -65,4 +67,17 @@ class ProfilesController &lt; ApplicationController
65 def user 67 def user
66 @user = current_user 68 @user = current_user
67 end 69 end
  70 +
  71 + def user_attributes
  72 + user_attributes = params[:user]
  73 +
  74 + # Sanitize user input because we dont have strict
  75 + # validation for this fields
  76 + %w(name skype linkedin twitter bio).each do |attr|
  77 + value = user_attributes[attr]
  78 + user_attributes[attr] = sanitize(value) if value.present?
  79 + end
  80 +
  81 + user_attributes
  82 + end
68 end 83 end
app/helpers/application_helper.rb
@@ -72,7 +72,7 @@ module ApplicationHelper @@ -72,7 +72,7 @@ module ApplicationHelper
72 end 72 end
73 73
74 def search_autocomplete_source 74 def search_autocomplete_source
75 - projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } } 75 + projects = current_user.authorized_projects.map { |p| { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } }
76 groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } } 76 groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } }
77 teams = current_user.authorized_teams.map { |team| { label: "team: #{simple_sanitize(team.name)}", url: team_path(team) } } 77 teams = current_user.authorized_teams.map { |team| { label: "team: #{simple_sanitize(team.name)}", url: team_path(team) } }
78 78
@@ -98,15 +98,15 @@ module ApplicationHelper @@ -98,15 +98,15 @@ module ApplicationHelper
98 project_nav = [] 98 project_nav = []
99 if @project && @project.repository && @project.repository.root_ref 99 if @project && @project.repository && @project.repository.root_ref
100 project_nav = [ 100 project_nav = [
101 - { label: "#{@project.name_with_namespace} - Issues", url: project_issues_path(@project) },  
102 - { label: "#{@project.name_with_namespace} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },  
103 - { label: "#{@project.name_with_namespace} - Merge Requests", url: project_merge_requests_path(@project) },  
104 - { label: "#{@project.name_with_namespace} - Milestones", url: project_milestones_path(@project) },  
105 - { label: "#{@project.name_with_namespace} - Snippets", url: project_snippets_path(@project) },  
106 - { label: "#{@project.name_with_namespace} - Team", url: project_team_index_path(@project) },  
107 - { label: "#{@project.name_with_namespace} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },  
108 - { label: "#{@project.name_with_namespace} - Wall", url: wall_project_path(@project) },  
109 - { label: "#{@project.name_with_namespace} - Wiki", url: project_wikis_path(@project) }, 101 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
  102 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
  103 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Merge Requests", url: project_merge_requests_path(@project) },
  104 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Milestones", url: project_milestones_path(@project) },
  105 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Snippets", url: project_snippets_path(@project) },
  106 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Team", url: project_team_index_path(@project) },
  107 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
  108 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Wall", url: wall_project_path(@project) },
  109 + { label: "#{simple_sanitize(@project.name_with_namespace)} - Wiki", url: project_wikis_path(@project) },
110 ] 110 ]
111 end 111 end
112 112
app/helpers/commits_helper.rb
@@ -57,6 +57,31 @@ module CommitsHelper @@ -57,6 +57,31 @@ module CommitsHelper
57 end 57 end
58 end 58 end
59 59
  60 + def each_diff_line_near(diff, index, expected_line_code)
  61 + max_number_of_lines = 16
  62 +
  63 + prev_match_line = nil
  64 + prev_lines = []
  65 +
  66 + each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
  67 + line = [full_line, type, line_code, line_new, line_old]
  68 + if line_code != expected_line_code
  69 + if type == "match"
  70 + prev_lines.clear
  71 + prev_match_line = line
  72 + else
  73 + prev_lines.push(line)
  74 + prev_lines.shift if prev_lines.length >= max_number_of_lines
  75 + end
  76 + else
  77 + yield(prev_match_line) if !prev_match_line.nil?
  78 + prev_lines.each { |ln| yield(ln) }
  79 + yield(line)
  80 + break
  81 + end
  82 + end
  83 + end
  84 +
60 def image_diff_class(diff) 85 def image_diff_class(diff)
61 if diff.deleted_file 86 if diff.deleted_file
62 "deleted" 87 "deleted"
app/models/key.rb
@@ -21,7 +21,6 @@ class Key &lt; ActiveRecord::Base @@ -21,7 +21,6 @@ class Key &lt; ActiveRecord::Base
21 attr_accessible :key, :title 21 attr_accessible :key, :title
22 22
23 before_validation :strip_white_space 23 before_validation :strip_white_space
24 - before_save :set_identifier  
25 24
26 validates :title, presence: true, length: { within: 0..255 } 25 validates :title, presence: true, length: { within: 0..255 }
27 validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true 26 validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true
@@ -48,14 +47,6 @@ class Key &lt; ActiveRecord::Base @@ -48,14 +47,6 @@ class Key &lt; ActiveRecord::Base
48 errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0 47 errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0
49 end 48 end
50 49
51 - def set_identifier  
52 - if is_deploy_key  
53 - self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}"  
54 - else  
55 - self.identifier = "#{user.identifier}_#{Time.now.to_i}"  
56 - end  
57 - end  
58 -  
59 def is_deploy_key 50 def is_deploy_key
60 !!project_id 51 !!project_id
61 end 52 end
app/models/merge_request.rb
@@ -24,6 +24,8 @@ require Rails.root.join(&quot;lib/static_model&quot;) @@ -24,6 +24,8 @@ require Rails.root.join(&quot;lib/static_model&quot;)
24 class MergeRequest < ActiveRecord::Base 24 class MergeRequest < ActiveRecord::Base
25 include Issuable 25 include Issuable
26 26
  27 + BROKEN_DIFF = "--broken-diff"
  28 +
27 attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id, 29 attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
28 :author_id_of_changes, :state_event 30 :author_id_of_changes, :state_event
29 31
@@ -51,47 +53,41 @@ class MergeRequest &lt; ActiveRecord::Base @@ -51,47 +53,41 @@ class MergeRequest &lt; ActiveRecord::Base
51 state :merged 53 state :merged
52 end 54 end
53 55
54 - BROKEN_DIFF = "--broken-diff" 56 + state_machine :merge_status, initial: :unchecked do
  57 + event :mark_as_unchecked do
  58 + transition [:can_be_merged, :cannot_be_merged] => :unchecked
  59 + end
  60 +
  61 + event :mark_as_mergeable do
  62 + transition unchecked: :can_be_merged
  63 + end
  64 +
  65 + event :mark_as_unmergeable do
  66 + transition unchecked: :cannot_be_merged
  67 + end
  68 +
  69 + state :unchecked
55 70
56 - UNCHECKED = 1  
57 - CAN_BE_MERGED = 2  
58 - CANNOT_BE_MERGED = 3 71 + state :can_be_merged
  72 +
  73 + state :cannot_be_merged
  74 + end
59 75
60 serialize :st_commits 76 serialize :st_commits
61 serialize :st_diffs 77 serialize :st_diffs
62 78
63 validates :source_branch, presence: true 79 validates :source_branch, presence: true
64 validates :target_branch, presence: true 80 validates :target_branch, presence: true
65 - validate :validate_branches 81 + validate :validate_branches
66 82
67 scope :merged, -> { with_state(:merged) } 83 scope :merged, -> { with_state(:merged) }
  84 + scope :by_branch, ->(branch_name) { where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) }
  85 + scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
  86 + scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
68 87
69 - class << self  
70 - def find_all_by_branch(branch_name)  
71 - where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)  
72 - end  
73 -  
74 - def cared(user)  
75 - where('assignee_id = :user OR author_id = :user', user: user.id)  
76 - end  
77 -  
78 - def find_all_by_branch(branch_name)  
79 - where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)  
80 - end  
81 -  
82 - def find_all_by_milestone(milestone)  
83 - where("milestone_id = :milestone_id", milestone_id: milestone)  
84 - end  
85 - end  
86 -  
87 - def human_merge_status  
88 - merge_statuses = {  
89 - CAN_BE_MERGED => "can_be_merged",  
90 - CANNOT_BE_MERGED => "cannot_be_merged",  
91 - UNCHECKED => "unchecked"  
92 - }  
93 - merge_statuses[self.merge_status]  
94 - end 88 + # Closed scope for merge request should return
  89 + # both merged and closed mr's
  90 + scope :closed, -> { with_states(:closed, :merged) }
95 91
96 def validate_branches 92 def validate_branches
97 if target_branch == source_branch 93 if target_branch == source_branch
@@ -104,26 +100,12 @@ class MergeRequest &lt; ActiveRecord::Base @@ -104,26 +100,12 @@ class MergeRequest &lt; ActiveRecord::Base
104 self.reloaded_diffs 100 self.reloaded_diffs
105 end 101 end
106 102
107 - def unchecked?  
108 - merge_status == UNCHECKED  
109 - end  
110 -  
111 - def mark_as_unchecked  
112 - self.merge_status = UNCHECKED  
113 - self.save  
114 - end  
115 -  
116 - def can_be_merged?  
117 - merge_status == CAN_BE_MERGED  
118 - end  
119 -  
120 def check_if_can_be_merged 103 def check_if_can_be_merged
121 - self.merge_status = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?  
122 - CAN_BE_MERGED  
123 - else  
124 - CANNOT_BE_MERGED  
125 - end  
126 - self.save 104 + if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
  105 + mark_as_mergeable
  106 + else
  107 + mark_as_unmergeable
  108 + end
127 end 109 end
128 110
129 def diffs 111 def diffs
@@ -178,11 +160,6 @@ class MergeRequest &lt; ActiveRecord::Base @@ -178,11 +160,6 @@ class MergeRequest &lt; ActiveRecord::Base
178 commits.any? && opened? 160 commits.any? && opened?
179 end 161 end
180 162
181 - def mark_as_unmergable  
182 - self.merge_status = CANNOT_BE_MERGED  
183 - self.save  
184 - end  
185 -  
186 def reloaded_commits 163 def reloaded_commits
187 if opened? && unmerged_commits.any? 164 if opened? && unmerged_commits.any?
188 self.st_commits = unmerged_commits 165 self.st_commits = unmerged_commits
@@ -217,7 +194,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -217,7 +194,7 @@ class MergeRequest &lt; ActiveRecord::Base
217 true 194 true
218 end 195 end
219 rescue 196 rescue
220 - self.mark_as_unmergable 197 + mark_as_unmergeable
221 false 198 false
222 end 199 end
223 200
app/models/project.rb
@@ -247,32 +247,6 @@ class Project &lt; ActiveRecord::Base @@ -247,32 +247,6 @@ class Project &lt; ActiveRecord::Base
247 users_projects.find_by_user_id(user_id) 247 users_projects.find_by_user_id(user_id)
248 end 248 end
249 249
250 - def transfer(new_namespace)  
251 - Project.transaction do  
252 - old_namespace = namespace  
253 - self.namespace = new_namespace  
254 -  
255 - old_dir = old_namespace.try(:path) || ''  
256 - new_dir = new_namespace.try(:path) || ''  
257 -  
258 - old_repo = if old_dir.present?  
259 - File.join(old_dir, self.path)  
260 - else  
261 - self.path  
262 - end  
263 -  
264 - if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?  
265 - raise TransferError.new("Project with same path in target namespace already exists")  
266 - end  
267 -  
268 - Gitlab::ProjectMover.new(self, old_dir, new_dir).execute  
269 -  
270 - save!  
271 - end  
272 - rescue Gitlab::ProjectMover::ProjectMoveError => ex  
273 - raise Project::TransferError.new(ex.message)  
274 - end  
275 -  
276 def name_with_namespace 250 def name_with_namespace
277 @name_with_namespace ||= begin 251 @name_with_namespace ||= begin
278 if namespace 252 if namespace
@@ -295,51 +269,8 @@ class Project &lt; ActiveRecord::Base @@ -295,51 +269,8 @@ class Project &lt; ActiveRecord::Base
295 end 269 end
296 end 270 end
297 271
298 - # This method will be called after each post receive and only if the provided  
299 - # user is present in GitLab.  
300 - #  
301 - # All callbacks for post receive should be placed here.  
302 - def trigger_post_receive(oldrev, newrev, ref, user)  
303 - data = post_receive_data(oldrev, newrev, ref, user)  
304 -  
305 - # Create satellite  
306 - self.satellite.create unless self.satellite.exists?  
307 -  
308 - # Create push event  
309 - self.observe_push(data)  
310 -  
311 - if push_to_branch? ref, oldrev  
312 - # Close merged MR  
313 - self.update_merge_requests(oldrev, newrev, ref, user)  
314 -  
315 - # Execute web hooks  
316 - self.execute_hooks(data.dup)  
317 -  
318 - # Execute project services  
319 - self.execute_services(data.dup)  
320 - end  
321 -  
322 - # Discover the default branch, but only if it hasn't already been set to  
323 - # something else  
324 - if repository && default_branch.nil?  
325 - update_attributes(default_branch: self.repository.discover_default_branch)  
326 - end  
327 - end  
328 -  
329 - def push_to_branch? ref, oldrev  
330 - ref_parts = ref.split('/')  
331 -  
332 - # Return if this is not a push to a branch (e.g. new commits)  
333 - !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")  
334 - end  
335 -  
336 - def observe_push(data)  
337 - Event.create(  
338 - project: self,  
339 - action: Event::PUSHED,  
340 - data: data,  
341 - author_id: data[:user_id]  
342 - ) 272 + def transfer(new_namespace)
  273 + ProjectTransferService.new.transfer(self, new_namespace)
343 end 274 end
344 275
345 def execute_hooks(data) 276 def execute_hooks(data)
@@ -354,68 +285,12 @@ class Project &lt; ActiveRecord::Base @@ -354,68 +285,12 @@ class Project &lt; ActiveRecord::Base
354 end 285 end
355 end 286 end
356 287
357 - # Produce a hash of post-receive data  
358 - #  
359 - # data = {  
360 - # before: String,  
361 - # after: String,  
362 - # ref: String,  
363 - # user_id: String,  
364 - # user_name: String,  
365 - # repository: {  
366 - # name: String,  
367 - # url: String,  
368 - # description: String,  
369 - # homepage: String,  
370 - # },  
371 - # commits: Array,  
372 - # total_commits_count: Fixnum  
373 - # }  
374 - #  
375 - def post_receive_data(oldrev, newrev, ref, user)  
376 -  
377 - push_commits = repository.commits_between(oldrev, newrev)  
378 -  
379 - # Total commits count  
380 - push_commits_count = push_commits.size  
381 -  
382 - # Get latest 20 commits ASC  
383 - push_commits_limited = push_commits.last(20)  
384 -  
385 - # Hash to be passed as post_receive_data  
386 - data = {  
387 - before: oldrev,  
388 - after: newrev,  
389 - ref: ref,  
390 - user_id: user.id,  
391 - user_name: user.name,  
392 - repository: {  
393 - name: name,  
394 - url: url_to_repo,  
395 - description: description,  
396 - homepage: web_url,  
397 - },  
398 - commits: [],  
399 - total_commits_count: push_commits_count  
400 - }  
401 -  
402 - # For perfomance purposes maximum 20 latest commits  
403 - # will be passed as post receive hook data.  
404 - #  
405 - push_commits_limited.each do |commit|  
406 - data[:commits] << {  
407 - id: commit.id,  
408 - message: commit.safe_message,  
409 - timestamp: commit.date.xmlschema,  
410 - url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",  
411 - author: {  
412 - name: commit.author_name,  
413 - email: commit.author_email  
414 - }  
415 - } 288 + def discover_default_branch
  289 + # Discover the default branch, but only if it hasn't already been set to
  290 + # something else
  291 + if repository && default_branch.nil?
  292 + update_attributes(default_branch: self.repository.discover_default_branch)
416 end 293 end
417 -  
418 - data  
419 end 294 end
420 295
421 def update_merge_requests(oldrev, newrev, ref, user) 296 def update_merge_requests(oldrev, newrev, ref, user)
@@ -424,7 +299,7 @@ class Project &lt; ActiveRecord::Base @@ -424,7 +299,7 @@ class Project &lt; ActiveRecord::Base
424 c_ids = self.repository.commits_between(oldrev, newrev).map(&:id) 299 c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
425 300
426 # Update code for merge requests 301 # Update code for merge requests
427 - mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all 302 + mrs = self.merge_requests.opened.by_branch(branch_name).all
428 mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } 303 mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
429 304
430 # Close merge requests 305 # Close merge requests
@@ -446,6 +321,10 @@ class Project &lt; ActiveRecord::Base @@ -446,6 +321,10 @@ class Project &lt; ActiveRecord::Base
446 !repository || repository.empty? 321 !repository || repository.empty?
447 end 322 end
448 323
  324 + def ensure_satellite_exists
  325 + self.satellite.create unless self.satellite.exists?
  326 + end
  327 +
449 def satellite 328 def satellite
450 @satellite ||= Gitlab::Satellite::Satellite.new(self) 329 @satellite ||= Gitlab::Satellite::Satellite.new(self)
451 end 330 end
app/models/project_team.rb
@@ -66,28 +66,6 @@ class ProjectTeam @@ -66,28 +66,6 @@ class ProjectTeam
66 members.masters.map(&:user) 66 members.masters.map(&:user)
67 end 67 end
68 68
69 - def repository_readers  
70 - repository_members[UsersProject::REPORTER]  
71 - end  
72 -  
73 - def repository_writers  
74 - repository_members[UsersProject::DEVELOPER]  
75 - end  
76 -  
77 - def repository_masters  
78 - repository_members[UsersProject::MASTER]  
79 - end  
80 -  
81 - def repository_members  
82 - keys = Hash.new {|h,k| h[k] = [] }  
83 - UsersProject.select("keys.identifier, project_access").  
84 - joins(user: :keys).where(project_id: project.id).  
85 - each {|row| keys[row.project_access] << [row.identifier] }  
86 -  
87 - keys[UsersProject::REPORTER] += project.deploy_keys.pluck(:identifier)  
88 - keys  
89 - end  
90 -  
91 def import(source_project) 69 def import(source_project)
92 target_project = project 70 target_project = project
93 71
app/models/system_hook.rb
@@ -12,13 +12,4 @@ @@ -12,13 +12,4 @@
12 # 12 #
13 13
14 class SystemHook < WebHook 14 class SystemHook < WebHook
15 - def self.all_hooks_fire(data)  
16 - SystemHook.all.each do |sh|  
17 - sh.async_execute data  
18 - end  
19 - end  
20 -  
21 - def async_execute(data)  
22 - Sidekiq::Client.enqueue(SystemHookWorker, id, data)  
23 - end  
24 end 15 end
app/models/user.rb
@@ -70,6 +70,7 @@ class User &lt; ActiveRecord::Base @@ -70,6 +70,7 @@ class User &lt; ActiveRecord::Base
70 has_many :team_projects, through: :user_team_project_relationships 70 has_many :team_projects, through: :user_team_project_relationships
71 71
72 validates :name, presence: true 72 validates :name, presence: true
  73 + validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
73 validates :bio, length: { within: 0..255 } 74 validates :bio, length: { within: 0..255 }
74 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider} 75 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
75 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0} 76 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
@@ -215,17 +216,6 @@ class User &lt; ActiveRecord::Base @@ -215,17 +216,6 @@ class User &lt; ActiveRecord::Base
215 UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id) 216 UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
216 end 217 end
217 218
218 - # Returns a string for use as a Gitolite user identifier  
219 - #  
220 - # Note that Gitolite 2.x requires the following pattern for users:  
221 - #  
222 - # ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$  
223 - def identifier  
224 - # Replace non-word chars with underscores, then make sure it starts with  
225 - # valid chars  
226 - email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')  
227 - end  
228 -  
229 def is_admin? 219 def is_admin?
230 admin 220 admin
231 end 221 end
app/models/web_hook.rb
@@ -28,10 +28,14 @@ class WebHook &lt; ActiveRecord::Base @@ -28,10 +28,14 @@ class WebHook &lt; ActiveRecord::Base
28 WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }) 28 WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
29 else 29 else
30 post_url = url.gsub("#{parsed_url.userinfo}@", "") 30 post_url = url.gsub("#{parsed_url.userinfo}@", "")
  31 + auth = {
  32 + username: URI.decode(parsed_url.user),
  33 + password: URI.decode(parsed_url.password),
  34 + }
31 WebHook.post(post_url, 35 WebHook.post(post_url,
32 body: data.to_json, 36 body: data.to_json,
33 headers: {"Content-Type" => "application/json"}, 37 headers: {"Content-Type" => "application/json"},
34 - basic_auth: {username: parsed_url.user, password: parsed_url.password}) 38 + basic_auth: auth)
35 end 39 end
36 end 40 end
37 41
app/observers/activity_observer.rb
@@ -21,22 +21,22 @@ class ActivityObserver &lt; ActiveRecord::Observer @@ -21,22 +21,22 @@ class ActivityObserver &lt; ActiveRecord::Observer
21 end 21 end
22 22
23 def after_close(record, transition) 23 def after_close(record, transition)
24 - Event.create(  
25 - project: record.project,  
26 - target_id: record.id,  
27 - target_type: record.class.name,  
28 - action: Event::CLOSED,  
29 - author_id: record.author_id_of_changes  
30 - ) 24 + Event.create(
  25 + project: record.project,
  26 + target_id: record.id,
  27 + target_type: record.class.name,
  28 + action: Event::CLOSED,
  29 + author_id: record.author_id_of_changes
  30 + )
31 end 31 end
32 32
33 def after_reopen(record, transition) 33 def after_reopen(record, transition)
34 - Event.create(  
35 - project: record.project,  
36 - target_id: record.id,  
37 - target_type: record.class.name,  
38 - action: Event::REOPENED,  
39 - author_id: record.author_id_of_changes  
40 - ) 34 + Event.create(
  35 + project: record.project,
  36 + target_id: record.id,
  37 + target_type: record.class.name,
  38 + action: Event::REOPENED,
  39 + author_id: record.author_id_of_changes
  40 + )
41 end 41 end
42 end 42 end
app/observers/note_observer.rb
1 class NoteObserver < ActiveRecord::Observer 1 class NoteObserver < ActiveRecord::Observer
2 -  
3 def after_create(note) 2 def after_create(note)
4 send_notify_mails(note) 3 send_notify_mails(note)
5 end 4 end
app/observers/system_hook_observer.rb
1 class SystemHookObserver < ActiveRecord::Observer 1 class SystemHookObserver < ActiveRecord::Observer
2 observe :user, :project, :users_project 2 observe :user, :project, :users_project
3 -  
4 - def after_create(model)  
5 - if model.kind_of? Project  
6 - SystemHook.all_hooks_fire({  
7 - event_name: "project_create",  
8 - name: model.name,  
9 - path: model.path,  
10 - project_id: model.id,  
11 - owner_name: model.owner.name,  
12 - owner_email: model.owner.email,  
13 - created_at: model.created_at  
14 - })  
15 - elsif model.kind_of? User  
16 - SystemHook.all_hooks_fire({  
17 - event_name: "user_create",  
18 - name: model.name,  
19 - email: model.email,  
20 - created_at: model.created_at  
21 - })  
22 -  
23 - elsif model.kind_of? UsersProject  
24 - SystemHook.all_hooks_fire({  
25 - event_name: "user_add_to_team",  
26 - project_name: model.project.name,  
27 - project_path: model.project.path,  
28 - project_id: model.project_id,  
29 - user_name: model.user.name,  
30 - user_email: model.user.email,  
31 - project_access: model.repo_access_human,  
32 - created_at: model.created_at  
33 - })  
34 3
35 - end 4 + def after_create(model)
  5 + SystemHooksService.execute_hooks_for(model, :create)
36 end 6 end
37 7
38 def after_destroy(model) 8 def after_destroy(model)
39 - if model.kind_of? Project  
40 - SystemHook.all_hooks_fire({  
41 - event_name: "project_destroy",  
42 - name: model.name,  
43 - path: model.path,  
44 - project_id: model.id,  
45 - owner_name: model.owner.name,  
46 - owner_email: model.owner.email,  
47 - })  
48 - elsif model.kind_of? User  
49 - SystemHook.all_hooks_fire({  
50 - event_name: "user_destroy",  
51 - name: model.name,  
52 - email: model.email  
53 - })  
54 -  
55 - elsif model.kind_of? UsersProject  
56 - SystemHook.all_hooks_fire({  
57 - event_name: "user_remove_from_team",  
58 - project_name: model.project.name,  
59 - project_path: model.project.path,  
60 - project_id: model.project_id,  
61 - user_name: model.user.name,  
62 - user_email: model.user.email,  
63 - project_access: model.repo_access_human  
64 - })  
65 - end 9 + SystemHooksService.execute_hooks_for(model, :destroy)
66 end 10 end
67 end 11 end
app/services/git_push_service.rb 0 → 100644
@@ -0,0 +1,124 @@ @@ -0,0 +1,124 @@
  1 +class GitPushService
  2 + attr_accessor :project, :user, :push_data
  3 +
  4 + # This method will be called after each git update
  5 + # and only if the provided user and project is present in GitLab.
  6 + #
  7 + # All callbacks for post receive action should be placed here.
  8 + #
  9 + # Now this method do next:
  10 + # 1. Ensure project satellite exists
  11 + # 2. Update merge requests
  12 + # 3. Execute project web hooks
  13 + # 4. Execute project services
  14 + # 5. Create Push Event
  15 + #
  16 + def execute(project, user, oldrev, newrev, ref)
  17 + @project, @user = project, user
  18 +
  19 + # Collect data for this git push
  20 + @push_data = post_receive_data(oldrev, newrev, ref)
  21 +
  22 + project.ensure_satellite_exists
  23 + project.discover_default_branch
  24 +
  25 + if push_to_branch?(ref, oldrev)
  26 + project.update_merge_requests(oldrev, newrev, ref, @user)
  27 + project.execute_hooks(@push_data.dup)
  28 + project.execute_services(@push_data.dup)
  29 + end
  30 +
  31 + create_push_event
  32 + end
  33 +
  34 + # This method provide a sample data
  35 + # generated with post_receive_data method
  36 + # for given project
  37 + #
  38 + def sample_data(project, user)
  39 + @project, @user = project, user
  40 + commits = project.repository.commits(project.default_branch, nil, 3)
  41 + post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}")
  42 + end
  43 +
  44 + protected
  45 +
  46 + def create_push_event
  47 + Event.create(
  48 + project: project,
  49 + action: Event::PUSHED,
  50 + data: push_data,
  51 + author_id: push_data[:user_id]
  52 + )
  53 + end
  54 +
  55 + # Produce a hash of post-receive data
  56 + #
  57 + # data = {
  58 + # before: String,
  59 + # after: String,
  60 + # ref: String,
  61 + # user_id: String,
  62 + # user_name: String,
  63 + # repository: {
  64 + # name: String,
  65 + # url: String,
  66 + # description: String,
  67 + # homepage: String,
  68 + # },
  69 + # commits: Array,
  70 + # total_commits_count: Fixnum
  71 + # }
  72 + #
  73 + def post_receive_data(oldrev, newrev, ref)
  74 + push_commits = project.repository.commits_between(oldrev, newrev)
  75 +
  76 + # Total commits count
  77 + push_commits_count = push_commits.size
  78 +
  79 + # Get latest 20 commits ASC
  80 + push_commits_limited = push_commits.last(20)
  81 +
  82 + # Hash to be passed as post_receive_data
  83 + data = {
  84 + before: oldrev,
  85 + after: newrev,
  86 + ref: ref,
  87 + user_id: user.id,
  88 + user_name: user.name,
  89 + repository: {
  90 + name: project.name,
  91 + url: project.url_to_repo,
  92 + description: project.description,
  93 + homepage: project.web_url,
  94 + },
  95 + commits: [],
  96 + total_commits_count: push_commits_count
  97 + }
  98 +
  99 + # For perfomance purposes maximum 20 latest commits
  100 + # will be passed as post receive hook data.
  101 + #
  102 + push_commits_limited.each do |commit|
  103 + data[:commits] << {
  104 + id: commit.id,
  105 + message: commit.safe_message,
  106 + timestamp: commit.date.xmlschema,
  107 + url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{commit.id}",
  108 + author: {
  109 + name: commit.author_name,
  110 + email: commit.author_email
  111 + }
  112 + }
  113 + end
  114 +
  115 + data
  116 + end
  117 +
  118 + def push_to_branch? ref, oldrev
  119 + ref_parts = ref.split('/')
  120 +
  121 + # Return if this is not a push to a branch (e.g. new commits)
  122 + !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")
  123 + end
  124 +end
app/services/project_transfer_service.rb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +# ProjectTransferService class
  2 +#
  3 +# Used for transfer project to another namespace
  4 +#
  5 +class ProjectTransferService
  6 + attr_accessor :project
  7 +
  8 + def transfer(project, new_namespace)
  9 + Project.transaction do
  10 + old_namespace = project.namespace
  11 + project.namespace = new_namespace
  12 +
  13 + old_dir = old_namespace.try(:path) || ''
  14 + new_dir = new_namespace.try(:path) || ''
  15 +
  16 + old_repo = if old_dir.present?
  17 + File.join(old_dir, project.path)
  18 + else
  19 + project.path
  20 + end
  21 +
  22 + if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
  23 + raise TransferError.new("Project with same path in target namespace already exists")
  24 + end
  25 +
  26 + Gitlab::ProjectMover.new(project, old_dir, new_dir).execute
  27 +
  28 + save!
  29 + end
  30 + rescue Gitlab::ProjectMover::ProjectMoveError => ex
  31 + raise Project::TransferError.new(ex.message)
  32 + end
  33 +end
  34 +
app/services/system_hooks_service.rb 0 → 100644
@@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
  1 +class SystemHooksService
  2 + def self.execute_hooks_for(model, event)
  3 + execute_hooks(build_event_data(model, event))
  4 + end
  5 +
  6 + private
  7 +
  8 + def self.execute_hooks(data)
  9 + SystemHook.all.each do |sh|
  10 + async_execute_hook sh, data
  11 + end
  12 + end
  13 +
  14 + def self.async_execute_hook(hook, data)
  15 + Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data)
  16 + end
  17 +
  18 + def self.build_event_data(model, event)
  19 + data = {
  20 + event_name: build_event_name(model, event),
  21 + created_at: model.created_at
  22 + }
  23 +
  24 + case model
  25 + when Project
  26 + data.merge!({
  27 + name: model.name,
  28 + path: model.path,
  29 + project_id: model.id,
  30 + owner_name: model.owner.name,
  31 + owner_email: model.owner.email
  32 + })
  33 + when User
  34 + data.merge!({
  35 + name: model.name,
  36 + email: model.email
  37 + })
  38 + when UsersProject
  39 + data.merge!({
  40 + project_name: model.project.name,
  41 + project_path: model.project.path,
  42 + project_id: model.project_id,
  43 + user_name: model.user.name,
  44 + user_email: model.user.email,
  45 + project_access: model.repo_access_human
  46 + })
  47 + end
  48 + end
  49 +
  50 + def self.build_event_name(model, event)
  51 + case model
  52 + when UsersProject
  53 + return "user_add_to_team" if event == :create
  54 + return "user_remove_from_team" if event == :destroy
  55 + else
  56 + "#{model.class.name.downcase}_#{event.to_s}"
  57 + end
  58 + end
  59 +end
app/views/dashboard/issues.html.haml
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 = link_to_project project 17 = link_to_project project
18 %ul.well-list.issues_table 18 %ul.well-list.issues_table
19 - group[1].each do |issue| 19 - group[1].each do |issue|
20 - = render(partial: 'issues/show', locals: {issue: issue}) 20 + = render issue
21 %hr 21 %hr
22 = paginate @issues, theme: "gitlab" 22 = paginate @issues, theme: "gitlab"
23 - else 23 - else
app/views/groups/issues.html.haml
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 = link_to_project project 16 = link_to_project project
17 %ul.well-list.issues_table 17 %ul.well-list.issues_table
18 - group[1].each do |issue| 18 - group[1].each do |issue|
19 - = render(partial: 'issues/show', locals: {issue: issue}) 19 + = render issue
20 %hr 20 %hr
21 = paginate @issues, theme: "gitlab" 21 = paginate @issues, theme: "gitlab"
22 - else 22 - else
app/views/issues/_issue.html.haml 0 → 100644
@@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
  1 +%li{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) }
  2 + - if controller.controller_name == 'issues'
  3 + .issue_check
  4 + = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
  5 + .pull-right
  6 + - if issue.notes.any?
  7 + %span.btn.btn-small.disabled.grouped
  8 + %i.icon-comment
  9 + = issue.notes.count
  10 + - if can? current_user, :modify_issue, issue
  11 + - if issue.closed?
  12 + = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true
  13 + - else
  14 + = link_to 'Close', project_issue_path(issue.project, issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true
  15 + = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do
  16 + %i.icon-edit
  17 + Edit
  18 +
  19 + - if issue.assignee
  20 + = image_tag gravatar_icon(issue.assignee_email), class: "avatar"
  21 + - else
  22 + = image_tag "no_avatar.png", class: "avatar"
  23 +
  24 + %p= link_to_gfm truncate(issue.title, length: 100), project_issue_path(issue.project, issue), class: "row_title"
  25 +
  26 + %span.update-author
  27 + %span.cdark= "##{issue.id}"
  28 + - if issue.assignee
  29 + assigned to #{issue.assignee_name}
  30 + - else
  31 + &nbsp;
  32 +
  33 + - if issue.votes_count > 0
  34 + = render 'votes/votes_inline', votable: issue
  35 + %span
  36 + - issue.labels.each do |label|
  37 + %span.label
  38 + %i.icon-tag
  39 + = label.name
app/views/issues/_issues.html.haml
1 -- @issues.each do |issue|  
2 - = render(partial: 'issues/show', locals: {issue: issue}) 1 += render @issues
3 2
4 - if @issues.present? 3 - if @issues.present?
5 %li.bottom 4 %li.bottom
app/views/issues/_show.html.haml
@@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
1 -%li{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) }  
2 - - if controller.controller_name == 'issues'  
3 - .issue_check  
4 - = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)  
5 - .pull-right  
6 - - if issue.notes.any?  
7 - %span.btn.btn-small.disabled.grouped  
8 - %i.icon-comment  
9 - = issue.notes.count  
10 - - if can? current_user, :modify_issue, issue  
11 - - if issue.closed?  
12 - = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true  
13 - - else  
14 - = link_to 'Close', project_issue_path(issue.project, issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true  
15 - = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do  
16 - %i.icon-edit  
17 - Edit  
18 -  
19 - - if issue.assignee  
20 - = image_tag gravatar_icon(issue.assignee_email), class: "avatar"  
21 - - else  
22 - = image_tag "no_avatar.png", class: "avatar"  
23 -  
24 - %p= link_to_gfm truncate(issue.title, length: 100), project_issue_path(issue.project, issue), class: "row_title"  
25 -  
26 - %span.update-author  
27 - %span.cdark= "##{issue.id}"  
28 - - if issue.assignee  
29 - assigned to #{issue.assignee_name}  
30 - - else  
31 - &nbsp;  
32 -  
33 - - if issue.votes_count > 0  
34 - = render 'votes/votes_inline', votable: issue  
35 - %span  
36 - - issue.labels.each do |label|  
37 - %span.label  
38 - %i.icon-tag  
39 - = label.name  
app/views/issues/show.html.haml
@@ -6,12 +6,12 @@ @@ -6,12 +6,12 @@
6 = @issue.created_at.stamp("Aug 21, 2011") 6 = @issue.created_at.stamp("Aug 21, 2011")
7 7
8 %span.pull-right 8 %span.pull-right
9 - - if can?(current_user, :admin_project, @project) || @issue.author == current_user 9 + - if can?(current_user, :modify_issue, @issue)
10 - if @issue.closed? 10 - if @issue.closed?
11 = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue" 11 = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
12 - else 12 - else
13 = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" 13 = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
14 - - if can?(current_user, :admin_project, @project) || @issue.author == current_user 14 +
15 = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do 15 = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
16 %i.icon-edit 16 %i.icon-edit
17 Edit 17 Edit
@@ -55,5 +55,11 @@ @@ -55,5 +55,11 @@
55 = preserve do 55 = preserve do
56 = markdown @issue.description 56 = markdown @issue.description
57 57
  58 +- content_for :note_actions do
  59 + - if can?(current_user, :modify_issue, @issue)
  60 + - if @issue.closed?
  61 + = link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
  62 + - else
  63 + = link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
58 64
59 .voting_notes#notes= render "notes/notes_with_form" 65 .voting_notes#notes= render "notes/notes_with_form"
app/views/merge_requests/_show.html.haml
@@ -29,10 +29,10 @@ @@ -29,10 +29,10 @@
29 $(function(){ 29 $(function(){
30 merge_request = new MergeRequest({ 30 merge_request = new MergeRequest({
31 url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}", 31 url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
32 - check_enable: #{@merge_request.merge_status == MergeRequest::UNCHECKED ? "true" : "false"}, 32 + check_enable: #{@merge_request.unchecked? ? "true" : "false"},
33 url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}", 33 url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
34 ci_enable: #{@project.gitlab_ci? ? "true" : "false"}, 34 ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
35 - current_status: "#{@merge_request.human_merge_status}", 35 + current_status: "#{@merge_request.merge_status_name}",
36 action: "#{controller.action_name}" 36 action: "#{controller.action_name}"
37 }); 37 });
38 }); 38 });
app/views/notes/_discussion_diff.html.haml
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 %br/ 9 %br/
10 .content 10 .content
11 %table 11 %table
12 - - each_diff_line(diff, note.diff_file_index) do |line, type, line_code, line_new, line_old| 12 + - each_diff_line_near(diff, note.diff_file_index, note.line_code) do |line, type, line_code, line_new, line_old|
13 %tr.line_holder{ id: line_code } 13 %tr.line_holder{ id: line_code }
14 - if type == "match" 14 - if type == "match"
15 %td.old_line= "..." 15 %td.old_line= "..."
@@ -22,4 +22,3 @@ @@ -22,4 +22,3 @@
22 22
23 - if line_code == note.line_code 23 - if line_code == note.line_code
24 = render "notes/diff_notes_with_reply", notes: discussion_notes 24 = render "notes/diff_notes_with_reply", notes: discussion_notes
25 - - break # cut off diff after notes  
app/views/notes/_form.html.haml
@@ -22,6 +22,8 @@ @@ -22,6 +22,8 @@
22 .note-form-actions 22 .note-form-actions
23 .buttons 23 .buttons
24 = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button" 24 = f.submit 'Add Comment', class: "btn comment-btn grouped js-comment-button"
  25 + = yield(:note_actions)
  26 +
25 %a.btn.grouped.js-close-discussion-note-form Cancel 27 %a.btn.grouped.js-close-discussion-note-form Cancel
26 28
27 .note-form-option 29 .note-form-option
app/views/profiles/account.html.haml
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 9
10 10
11 11
12 -%fieldset 12 +%fieldset.update-token
13 %legend 13 %legend
14 Private token 14 Private token
15 %span.cred.pull-right 15 %span.cred.pull-right
@@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
29 %span You don`t have one yet. Click generate to fix it. 29 %span You don`t have one yet. Click generate to fix it.
30 = f.submit 'Generate', class: "btn success btn-build-token" 30 = f.submit 'Generate', class: "btn success btn-build-token"
31 31
32 -%fieldset 32 +%fieldset.update-password
33 %legend Password 33 %legend Password
34 = form_for @user, url: update_password_profile_path, method: :put do |f| 34 = form_for @user, url: update_password_profile_path, method: :put do |f|
35 .padded 35 .padded
app/views/snippets/show.html.haml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 = @snippet.title 4 = @snippet.title
5 %small= @snippet.file_name 5 %small= @snippet.file_name
6 - if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user 6 - if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
7 - = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right" 7 + = link_to "Edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small pull-right", title: 'Edit Snippet'
8 8
9 %br 9 %br
10 %div= render 'blob' 10 %div= render 'blob'
app/views/teams/issues.html.haml
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 = link_to_project @project 16 = link_to_project @project
17 %ul.well-list.issues_table 17 %ul.well-list.issues_table
18 - group[1].each do |issue| 18 - group[1].each do |issue|
19 - = render(partial: 'issues/show', locals: {issue: issue}) 19 + = render issue
20 %hr 20 %hr
21 = paginate @issues, theme: "gitlab" 21 = paginate @issues, theme: "gitlab"
22 - else 22 - else
app/views/teams/members/_show.html.haml
@@ -26,5 +26,5 @@ @@ -26,5 +26,5 @@
26 - elsif user.blocked 26 - elsif user.blocked
27 %span.btn.disabled.blocked Blocked 27 %span.btn.disabled.blocked Blocked
28 - elsif allow_admin 28 - elsif allow_admin
29 - = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do 29 + = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove", title: "Remove from team" do
30 %i.icon-minus.icon-white 30 %i.icon-minus.icon-white
app/workers/post_receive.rb
@@ -42,6 +42,6 @@ class PostReceive @@ -42,6 +42,6 @@ class PostReceive
42 return false 42 return false
43 end 43 end
44 44
45 - project.trigger_post_receive(oldrev, newrev, ref, user) 45 + GitPushService.new.execute(project, user, oldrev, newrev, ref)
46 end 46 end
47 end 47 end
db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
1 class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration 1 class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
2 def up 2 def up
3 MergeRequest.transaction do 3 MergeRequest.transaction do
4 - MergeRequest.where("closed = 1 AND merged = 1").update_all("state = 'merged'")  
5 - MergeRequest.where("closed = 1 AND merged = 0").update_all("state = 'closed'")  
6 - MergeRequest.where("closed = 0").update_all("state = 'opened'") 4 + MergeRequest.where(closed: true, merged: true).update_all("state = 'merged'")
  5 + MergeRequest.where(closed: true, merged: true).update_all("state = 'closed'")
  6 + MergeRequest.where(closed: false).update_all("state = 'opened'")
7 end 7 end
8 end 8 end
9 9
10 def down 10 def down
11 MergeRequest.transaction do 11 MergeRequest.transaction do
12 - MergeRequest.where(state: :closed).update_all("closed = 1")  
13 - MergeRequest.where(state: :merged).update_all("closed = 1, merged = 1") 12 + MergeRequest.where(state: :closed).update_all(closed: true)
  13 + MergeRequest.where(state: :merged).update_all(closed: true, merged: true)
14 end 14 end
15 end 15 end
16 end 16 end
db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddNewMergeStatusToMergeRequest < ActiveRecord::Migration
  2 + def change
  3 + add_column :merge_requests, :new_merge_status, :string
  4 + end
  5 +end
db/migrate/20130220125544_convert_merge_status_in_merge_request.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +class ConvertMergeStatusInMergeRequest < ActiveRecord::Migration
  2 + def up
  3 + MergeRequest.transaction do
  4 + MergeRequest.where(merge_status: 1).update_all("new_merge_status = 'unchecked'")
  5 + MergeRequest.where(merge_status: 2).update_all("new_merge_status = 'can_be_merged'")
  6 + MergeRequest.where(merge_status: 3).update_all("new_merge_status = 'cannot_be_merged'")
  7 + end
  8 + end
  9 +
  10 + def down
  11 + MergeRequest.transaction do
  12 + MergeRequest.where(new_merge_status: :unchecked).update_all("merge_status = 1")
  13 + MergeRequest.where(new_merge_status: :can_be_merged).update_all("merge_status = 2")
  14 + MergeRequest.where(new_merge_status: :cannot_be_merged).update_all("merge_status = 3")
  15 + end
  16 + end
  17 +end
db/migrate/20130220125545_remove_merge_status_from_merge_request.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class RemoveMergeStatusFromMergeRequest < ActiveRecord::Migration
  2 + def up
  3 + remove_column :merge_requests, :merge_status
  4 + end
  5 +
  6 + def down
  7 + add_column :merge_requests, :merge_status, :integer
  8 + end
  9 +end
db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class RenameNewMergeStatusToMergeStatusInMilestone < ActiveRecord::Migration
  2 + def change
  3 + rename_column :merge_requests, :new_merge_status, :merge_status
  4 + end
  5 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20130218141554) do 14 +ActiveRecord::Schema.define(:version => 20130220133245) do
15 15
16 create_table "events", :force => true do |t| 16 create_table "events", :force => true do |t|
17 t.string "target_type" 17 t.string "target_type"
@@ -68,19 +68,19 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do @@ -68,19 +68,19 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do
68 add_index "keys", ["user_id"], :name => "index_keys_on_user_id" 68 add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
69 69
70 create_table "merge_requests", :force => true do |t| 70 create_table "merge_requests", :force => true do |t|
71 - t.string "target_branch", :null => false  
72 - t.string "source_branch", :null => false  
73 - t.integer "project_id", :null => false 71 + t.string "target_branch", :null => false
  72 + t.string "source_branch", :null => false
  73 + t.integer "project_id", :null => false
74 t.integer "author_id" 74 t.integer "author_id"
75 t.integer "assignee_id" 75 t.integer "assignee_id"
76 t.string "title" 76 t.string "title"
77 - t.datetime "created_at", :null => false  
78 - t.datetime "updated_at", :null => false 77 + t.datetime "created_at", :null => false
  78 + t.datetime "updated_at", :null => false
79 t.text "st_commits", :limit => 2147483647 79 t.text "st_commits", :limit => 2147483647
80 t.text "st_diffs", :limit => 2147483647 80 t.text "st_diffs", :limit => 2147483647
81 - t.integer "merge_status", :default => 1, :null => false  
82 t.integer "milestone_id" 81 t.integer "milestone_id"
83 t.string "state" 82 t.string "state"
  83 + t.string "merge_status"
84 end 84 end
85 85
86 add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id" 86 add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
@@ -106,11 +106,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do @@ -106,11 +106,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do
106 add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id" 106 add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id"
107 107
108 create_table "namespaces", :force => true do |t| 108 create_table "namespaces", :force => true do |t|
109 - t.string "name", :null => false  
110 - t.string "path", :null => false  
111 - t.integer "owner_id", :null => false  
112 - t.datetime "created_at", :null => false  
113 - t.datetime "updated_at", :null => false 109 + t.string "name", :null => false
  110 + t.string "path", :null => false
  111 + t.integer "owner_id", :null => false
  112 + t.datetime "created_at", :null => false
  113 + t.datetime "updated_at", :null => false
114 t.string "type" 114 t.string "type"
115 end 115 end
116 116
@@ -142,16 +142,16 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do @@ -142,16 +142,16 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do
142 t.string "name" 142 t.string "name"
143 t.string "path" 143 t.string "path"
144 t.text "description" 144 t.text "description"
145 - t.datetime "created_at", :null => false  
146 - t.datetime "updated_at", :null => false 145 + t.datetime "created_at", :null => false
  146 + t.datetime "updated_at", :null => false
147 t.integer "creator_id" 147 t.integer "creator_id"
148 t.string "default_branch" 148 t.string "default_branch"
149 - t.boolean "issues_enabled", :default => true, :null => false  
150 - t.boolean "wall_enabled", :default => true, :null => false  
151 - t.boolean "merge_requests_enabled", :default => true, :null => false  
152 - t.boolean "wiki_enabled", :default => true, :null => false 149 + t.boolean "issues_enabled", :default => true, :null => false
  150 + t.boolean "wall_enabled", :default => true, :null => false
  151 + t.boolean "merge_requests_enabled", :default => true, :null => false
  152 + t.boolean "wiki_enabled", :default => true, :null => false
153 t.integer "namespace_id" 153 t.integer "namespace_id"
154 - t.boolean "public", :default => false, :null => false 154 + t.boolean "public", :default => false, :null => false
155 end 155 end
156 156
157 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" 157 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
@@ -230,8 +230,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do @@ -230,8 +230,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130218141554) do
230 t.string "name" 230 t.string "name"
231 t.string "path" 231 t.string "path"
232 t.integer "owner_id" 232 t.integer "owner_id"
233 - t.datetime "created_at", :null => false  
234 - t.datetime "updated_at", :null => false 233 + t.datetime "created_at", :null => false
  234 + t.datetime "updated_at", :null => false
235 end 235 end
236 236
237 create_table "users", :force => true do |t| 237 create_table "users", :force => true do |t|
doc/raketasks/backup_restore.md
@@ -31,7 +31,6 @@ Dumping database tables: @@ -31,7 +31,6 @@ Dumping database tables:
31 - Dumping table wikis... [DONE] 31 - Dumping table wikis... [DONE]
32 Dumping repositories: 32 Dumping repositories:
33 - Dumping repository abcd... [DONE] 33 - Dumping repository abcd... [DONE]
34 -- Dumping repository gitolite-admin.git... [DONE]  
35 Creating backup archive: $TIMESTAMP_gitlab_backup.tar [DONE] 34 Creating backup archive: $TIMESTAMP_gitlab_backup.tar [DONE]
36 Deleting tmp directories...[DONE] 35 Deleting tmp directories...[DONE]
37 Deleting old backups... [SKIPPING] 36 Deleting old backups... [SKIPPING]
@@ -77,6 +76,5 @@ Restoring database tables: @@ -77,6 +76,5 @@ Restoring database tables:
77 - Loading fixture wikis...[SKIPPING] 76 - Loading fixture wikis...[SKIPPING]
78 Restoring repositories: 77 Restoring repositories:
79 - Restoring repository abcd... [DONE] 78 - Restoring repository abcd... [DONE]
80 -- Restoring repository gitolite-admin.git... [DONE]  
81 Deleting tmp directories...[DONE] 79 Deleting tmp directories...[DONE]
82 ``` 80 ```
doc/raketasks/cleanup.md
1 -### Remove grabage from gitolite config and filesystem. Important! Data loss!  
2 -  
3 -Remove projects from gitolite config if they dont exist in GitLab database  
4 -  
5 -```  
6 -bundle exec rake gitlab:cleanup:config RAILS_ENV=production  
7 -``` 1 +### Remove grabage from filesystem. Important! Data loss!
8 2
9 Remove namespaces(dirs) from /home/git/repositories if they dont exist in GitLab database 3 Remove namespaces(dirs) from /home/git/repositories if they dont exist in GitLab database
10 4
doc/raketasks/features.md
@@ -17,12 +17,12 @@ bundle exec rake gitlab:enable_namespaces RAILS_ENV=production @@ -17,12 +17,12 @@ bundle exec rake gitlab:enable_namespaces RAILS_ENV=production
17 ``` 17 ```
18 18
19 19
20 -### Enable auto merge 20 +### Rebuild project satellites
21 21
22 -This command will enable the auto merge feature. After this you will be able to **merge a merge request** via GitLab and use the **online editor**. 22 +This command will build missing satellites for projects. After this you will be able to **merge a merge request** via GitLab and use the **online editor**.
23 23
24 ``` 24 ```
25 -bundle exec rake gitlab:enable_automerge RAILS_ENV=production 25 +bundle exec rake gitlab:satellites:create RAILS_ENV=production
26 ``` 26 ```
27 27
28 Example output: 28 Example output:
doc/raketasks/maintenance.md
@@ -31,12 +31,10 @@ SSH Clone URL: git@localhost:some-project.git @@ -31,12 +31,10 @@ SSH Clone URL: git@localhost:some-project.git
31 Using LDAP: no 31 Using LDAP: no
32 Using Omniauth: no 32 Using Omniauth: no
33 33
34 -Gitolite information  
35 -Version: v3.04-4-g4524f01  
36 -Admin URI: git@localhost:gitolite-admin  
37 -Admin Key: gitlab 34 +GitLab Shell
  35 +Version: 1.0.4
38 Repositories: /home/git/repositories/ 36 Repositories: /home/git/repositories/
39 -Hooks: /home/git/.gitolite/hooks/ 37 +Hooks: /home/git/gitlab-shell/hooks/
40 Git: /usr/bin/git 38 Git: /usr/bin/git
41 ``` 39 ```
42 40
@@ -46,8 +44,8 @@ Git: /usr/bin/git @@ -46,8 +44,8 @@ Git: /usr/bin/git
46 Runs the following rake tasks: 44 Runs the following rake tasks:
47 45
48 * gitlab:env:check 46 * gitlab:env:check
49 -* gitlab:gitolite:check  
50 -* gitlab:resque:check 47 +* gitlab:gitlab_shell:check
  48 +* gitlab:sidekiq:check
51 * gitlab:app:check 49 * gitlab:app:check
52 50
53 It will check that each component was setup according to the installation guide and suggest fixes for issues found. 51 It will check that each component was setup according to the installation guide and suggest fixes for issues found.
@@ -74,16 +72,12 @@ Checking Environment ... Finished @@ -74,16 +72,12 @@ Checking Environment ... Finished
74 Checking Gitolite ... 72 Checking Gitolite ...
75 73
76 Using recommended version ... yes 74 Using recommended version ... yes
77 -Repo umask is 0007 in .gitolite.rc? ... yes  
78 -Allow all Git config keys in .gitolite.rc ... yes  
79 Config directory exists? ... yes 75 Config directory exists? ... yes
80 Config directory owned by git:git? ... yes 76 Config directory owned by git:git? ... yes
81 Config directory access is drwxr-x---? ... yes 77 Config directory access is drwxr-x---? ... yes
82 Repo base directory exists? ... yes 78 Repo base directory exists? ... yes
83 Repo base owned by git:git? ... yes 79 Repo base owned by git:git? ... yes
84 Repo base access is drwxrws---? ... yes 80 Repo base access is drwxrws---? ... yes
85 -Can clone gitolite-admin? ... yes  
86 -Can commit to gitolite-admin? ... yes  
87 post-receive hook exists? ... yes 81 post-receive hook exists? ... yes
88 post-receive hook up-to-date? ... yes 82 post-receive hook up-to-date? ... yes
89 post-receive hooks in repos are links: ... 83 post-receive hooks in repos are links: ...
@@ -135,24 +129,6 @@ If necessary, remove the `tmp/repo_satellites` directory and rerun the command b @@ -135,24 +129,6 @@ If necessary, remove the `tmp/repo_satellites` directory and rerun the command b
135 bundle exec rake gitlab:satellites:create RAILS_ENV=production 129 bundle exec rake gitlab:satellites:create RAILS_ENV=production
136 ``` 130 ```
137 131
138 -  
139 -### Rebuild each key at gitolite config  
140 -  
141 -This will send all users ssh public keys to gitolite and grant them access (based on their permission) to their projects.  
142 -  
143 -```  
144 -bundle exec rake gitlab:gitolite:update_keys RAILS_ENV=production  
145 -```  
146 -  
147 -  
148 -### Rebuild each project at gitolite config  
149 -  
150 -This makes sure that all projects are present in gitolite and can be accessed.  
151 -  
152 -```  
153 -bundle exec rake gitlab:gitolite:update_repos RAILS_ENV=production  
154 -```  
155 -  
156 ### Import bare repositories into GitLab project instance 132 ### Import bare repositories into GitLab project instance
157 133
158 Notes: 134 Notes:
features/project/source/browse_files.feature
@@ -12,7 +12,7 @@ Feature: Project Browse files @@ -12,7 +12,7 @@ Feature: Project Browse files
12 Then I should see files from repository for "8470d70" 12 Then I should see files from repository for "8470d70"
13 13
14 Scenario: I browse file content 14 Scenario: I browse file content
15 - Given I click on "Gemfile" file in repo 15 + Given I click on "Gemfile.lock" file in repo
16 Then I should see it content 16 Then I should see it content
17 17
18 Scenario: I browse raw file 18 Scenario: I browse raw file
@@ -22,6 +22,6 @@ Feature: Project Browse files @@ -22,6 +22,6 @@ Feature: Project Browse files
22 22
23 @javascript 23 @javascript
24 Scenario: I can edit file 24 Scenario: I can edit file
25 - Given I click on "Gemfile" file in repo 25 + Given I click on "Gemfile.lock" file in repo
26 And I click button "edit" 26 And I click button "edit"
27 Then I can edit code 27 Then I can edit code
features/project/source/git_blame.feature
@@ -5,6 +5,6 @@ Feature: Project Browse git repo @@ -5,6 +5,6 @@ Feature: Project Browse git repo
5 Given I visit project source page 5 Given I visit project source page
6 6
7 Scenario: I blame file 7 Scenario: I blame file
8 - Given I click on "Gemfile" file in repo 8 + Given I click on "Gemfile.lock" file in repo
9 And I click blame button 9 And I click blame button
10 Then I should see git file blame 10 Then I should see git file blame
features/steps/admin/admin_teams.rb
@@ -9,7 +9,7 @@ class AdminTeams &lt; Spinach::FeatureSteps @@ -9,7 +9,7 @@ class AdminTeams &lt; Spinach::FeatureSteps
9 end 9 end
10 10
11 And 'Create gitlab user "John"' do 11 And 'Create gitlab user "John"' do
12 - @user = create(:user, :name => "John") 12 + @user = create(:user, name: "John")
13 end 13 end
14 14
15 And 'I click new team link' do 15 And 'I click new team link' do
@@ -50,8 +50,8 @@ class AdminTeams &lt; Spinach::FeatureSteps @@ -50,8 +50,8 @@ class AdminTeams &lt; Spinach::FeatureSteps
50 When 'I select user "John" from user list as "Developer"' do 50 When 'I select user "John" from user list as "Developer"' do
51 @user ||= User.find_by_name("John") 51 @user ||= User.find_by_name("John")
52 within "#team_members" do 52 within "#team_members" do
53 - select @user.name, :from => "user_ids"  
54 - select "Developer", :from => "default_project_access" 53 + select "#{@user.name} (#{@user.email})", from: "user_ids"
  54 + select "Developer", from: "default_project_access"
55 end 55 end
56 end 56 end
57 57
@@ -89,8 +89,8 @@ class AdminTeams &lt; Spinach::FeatureSteps @@ -89,8 +89,8 @@ class AdminTeams &lt; Spinach::FeatureSteps
89 When 'I select project "Shop" with max access "Reporter"' do 89 When 'I select project "Shop" with max access "Reporter"' do
90 @project ||= Project.find_by_name("Shop") 90 @project ||= Project.find_by_name("Shop")
91 within "#assign_projects" do 91 within "#assign_projects" do
92 - select @project.name, :from => "project_ids"  
93 - select "Reporter", :from => "greatest_project_access" 92 + select @project.name, from: "project_ids"
  93 + select "Reporter", from: "greatest_project_access"
94 end 94 end
95 95
96 end 96 end
@@ -127,8 +127,8 @@ class AdminTeams &lt; Spinach::FeatureSteps @@ -127,8 +127,8 @@ class AdminTeams &lt; Spinach::FeatureSteps
127 When 'I select user "Jimm" ub team members list as "Master"' do 127 When 'I select user "Jimm" ub team members list as "Master"' do
128 user = User.find_by_name("Jimm") 128 user = User.find_by_name("Jimm")
129 within "#team_members" do 129 within "#team_members" do
130 - select user.name, :from => "user_ids"  
131 - select "Developer", :from => "default_project_access" 130 + select "#{user.name} (#{user.email})", from: "user_ids"
  131 + select "Developer", from: "default_project_access"
132 end 132 end
133 end 133 end
134 134
features/steps/profile/profile.rb
@@ -23,15 +23,19 @@ class Profile &lt; Spinach::FeatureSteps @@ -23,15 +23,19 @@ class Profile &lt; Spinach::FeatureSteps
23 end 23 end
24 24
25 Then 'I change my password' do 25 Then 'I change my password' do
26 - fill_in "user_password", :with => "222333"  
27 - fill_in "user_password_confirmation", :with => "222333"  
28 - click_button "Save" 26 + within '.update-password' do
  27 + fill_in "user_password", :with => "222333"
  28 + fill_in "user_password_confirmation", :with => "222333"
  29 + click_button "Save"
  30 + end
29 end 31 end
30 32
31 When 'I unsuccessfully change my password' do 33 When 'I unsuccessfully change my password' do
32 - fill_in "user_password", with: "password"  
33 - fill_in "user_password_confirmation", with: "confirmation"  
34 - click_button "Save" 34 + within '.update-password' do
  35 + fill_in "user_password", with: "password"
  36 + fill_in "user_password_confirmation", with: "confirmation"
  37 + click_button "Save"
  38 + end
35 end 39 end
36 40
37 Then "I should see a password error message" do 41 Then "I should see a password error message" do
@@ -43,8 +47,10 @@ class Profile &lt; Spinach::FeatureSteps @@ -43,8 +47,10 @@ class Profile &lt; Spinach::FeatureSteps
43 end 47 end
44 48
45 Then 'I reset my token' do 49 Then 'I reset my token' do
46 - @old_token = @user.private_token  
47 - click_button "Reset" 50 + within '.update-token' do
  51 + @old_token = @user.private_token
  52 + click_button "Reset"
  53 + end
48 end 54 end
49 55
50 And 'I should see new token' do 56 And 'I should see new token' do
features/steps/project/project_browse_files.rb
@@ -16,12 +16,12 @@ class ProjectBrowseFiles &lt; Spinach::FeatureSteps @@ -16,12 +16,12 @@ class ProjectBrowseFiles &lt; Spinach::FeatureSteps
16 page.should have_content "Gemfile" 16 page.should have_content "Gemfile"
17 end 17 end
18 18
19 - Given 'I click on "Gemfile" file in repo' do  
20 - click_link "Gemfile" 19 + Given 'I click on "Gemfile.lock" file in repo' do
  20 + click_link "Gemfile.lock"
21 end 21 end
22 22
23 Then 'I should see it content' do 23 Then 'I should see it content' do
24 - page.should have_content "rubygems.org" 24 + page.should have_content "DEPENDENCIES"
25 end 25 end
26 26
27 And 'I click link "raw"' do 27 And 'I click link "raw"' do
features/steps/project/project_browse_git_repo.rb
@@ -3,8 +3,8 @@ class ProjectBrowseGitRepo &lt; Spinach::FeatureSteps @@ -3,8 +3,8 @@ class ProjectBrowseGitRepo &lt; Spinach::FeatureSteps
3 include SharedProject 3 include SharedProject
4 include SharedPaths 4 include SharedPaths
5 5
6 - Given 'I click on "Gemfile" file in repo' do  
7 - click_link "Gemfile" 6 + Given 'I click on "Gemfile.lock" file in repo' do
  7 + click_link "Gemfile.lock"
8 end 8 end
9 9
10 And 'I click blame button' do 10 And 'I click blame button' do
@@ -12,7 +12,7 @@ class ProjectBrowseGitRepo &lt; Spinach::FeatureSteps @@ -12,7 +12,7 @@ class ProjectBrowseGitRepo &lt; Spinach::FeatureSteps
12 end 12 end
13 13
14 Then 'I should see git file blame' do 14 Then 'I should see git file blame' do
15 - page.should have_content "rubygems.org" 15 + page.should have_content "DEPENDENCIES"
16 page.should have_content "Dmitriy Zaporozhets" 16 page.should have_content "Dmitriy Zaporozhets"
17 page.should have_content "Moving to rails 3.2" 17 page.should have_content "Moving to rails 3.2"
18 end 18 end
features/steps/project/project_team_management.rb
@@ -53,7 +53,7 @@ class ProjectTeamManagement &lt; Spinach::FeatureSteps @@ -53,7 +53,7 @@ class ProjectTeamManagement &lt; Spinach::FeatureSteps
53 end 53 end
54 54
55 Given 'I click link "Sam"' do 55 Given 'I click link "Sam"' do
56 - click_link "Sam" 56 + first(:link, "Sam").click
57 end 57 end
58 58
59 Then 'I should see "Sam" team profile' do 59 Then 'I should see "Sam" team profile' do
features/steps/shared/diff_note.rb
@@ -22,7 +22,7 @@ module SharedDiffNote @@ -22,7 +22,7 @@ module SharedDiffNote
22 22
23 Given 'I leave a diff comment like "Typo, please fix"' do 23 Given 'I leave a diff comment like "Typo, please fix"' do
24 find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click") 24 find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click")
25 - within(".file") do 25 + within(".file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do
26 fill_in "note[note]", with: "Typo, please fix" 26 fill_in "note[note]", with: "Typo, please fix"
27 #click_button("Add Comment") 27 #click_button("Add Comment")
28 find(".js-comment-button").trigger("click") 28 find(".js-comment-button").trigger("click")
@@ -32,7 +32,7 @@ module SharedDiffNote @@ -32,7 +32,7 @@ module SharedDiffNote
32 32
33 Given 'I preview a diff comment text like "Should fix it :smile:"' do 33 Given 'I preview a diff comment text like "Should fix it :smile:"' do
34 find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click") 34 find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click")
35 - within(".file") do 35 + within(".file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_29_14']") do
36 fill_in "note[note]", with: "Should fix it :smile:" 36 fill_in "note[note]", with: "Should fix it :smile:"
37 find(".js-note-preview-button").trigger("click") 37 find(".js-note-preview-button").trigger("click")
38 end 38 end
@@ -40,7 +40,7 @@ module SharedDiffNote @@ -40,7 +40,7 @@ module SharedDiffNote
40 40
41 Given 'I preview another diff comment text like "DRY this up"' do 41 Given 'I preview another diff comment text like "DRY this up"' do
42 find("#586fb7c4e1add2d4d24e27566ed7064680098646_57_41.line_holder .js-add-diff-note-button").trigger("click") 42 find("#586fb7c4e1add2d4d24e27566ed7064680098646_57_41.line_holder .js-add-diff-note-button").trigger("click")
43 - within(".file") do 43 + within(".file form[rel$='586fb7c4e1add2d4d24e27566ed7064680098646_57_41']") do
44 fill_in "note[note]", with: "DRY this up" 44 fill_in "note[note]", with: "DRY this up"
45 find(".js-note-preview-button").trigger("click") 45 find(".js-note-preview-button").trigger("click")
46 end 46 end
features/steps/shared/paths.rb
@@ -173,12 +173,10 @@ module SharedPaths @@ -173,12 +173,10 @@ module SharedPaths
173 # ---------------------------------------- 173 # ----------------------------------------
174 174
175 And 'I visit project "Shop" page' do 175 And 'I visit project "Shop" page' do
176 - project = Project.find_by_name("Shop")  
177 visit project_path(project) 176 visit project_path(project)
178 end 177 end
179 178
180 When 'I visit edit project "Shop" page' do 179 When 'I visit edit project "Shop" page' do
181 - project = Project.find_by_name("Shop")  
182 visit edit_project_path(project) 180 visit edit_project_path(project)
183 end 181 end
184 182
@@ -219,7 +217,7 @@ module SharedPaths @@ -219,7 +217,7 @@ module SharedPaths
219 end 217 end
220 218
221 And 'I visit project "Shop" issues page' do 219 And 'I visit project "Shop" issues page' do
222 - visit project_issues_path(Project.find_by_name("Shop")) 220 + visit project_issues_path(project)
223 end 221 end
224 222
225 Given 'I visit issue page "Release 0.4"' do 223 Given 'I visit issue page "Release 0.4"' do
@@ -228,7 +226,7 @@ module SharedPaths @@ -228,7 +226,7 @@ module SharedPaths
228 end 226 end
229 227
230 Given 'I visit project "Shop" labels page' do 228 Given 'I visit project "Shop" labels page' do
231 - visit project_labels_path(Project.find_by_name("Shop")) 229 + visit project_labels_path(project)
232 end 230 end
233 231
234 Given 'I visit merge request page "Bug NS-04"' do 232 Given 'I visit merge request page "Bug NS-04"' do
@@ -242,20 +240,18 @@ module SharedPaths @@ -242,20 +240,18 @@ module SharedPaths
242 end 240 end
243 241
244 And 'I visit project "Shop" merge requests page' do 242 And 'I visit project "Shop" merge requests page' do
245 - visit project_merge_requests_path(Project.find_by_name("Shop")) 243 + visit project_merge_requests_path(project)
246 end 244 end
247 245
248 Given 'I visit project "Shop" milestones page' do 246 Given 'I visit project "Shop" milestones page' do
249 - @project = Project.find_by_name("Shop")  
250 - visit project_milestones_path(@project) 247 + visit project_milestones_path(project)
251 end 248 end
252 249
253 Then 'I visit project "Shop" team page' do 250 Then 'I visit project "Shop" team page' do
254 - visit project_team_index_path(Project.find_by_name("Shop")) 251 + visit project_team_index_path(project)
255 end 252 end
256 253
257 Then 'I visit project "Shop" wall page' do 254 Then 'I visit project "Shop" wall page' do
258 - project = Project.find_by_name("Shop")  
259 visit wall_project_path(project) 255 visit wall_project_path(project)
260 end 256 end
261 257
@@ -266,4 +262,8 @@ module SharedPaths @@ -266,4 +262,8 @@ module SharedPaths
266 def root_ref 262 def root_ref
267 @project.repository.root_ref 263 @project.repository.root_ref
268 end 264 end
  265 +
  266 + def project
  267 + project = Project.find_by_name!("Shop")
  268 + end
269 end 269 end
features/steps/userteams/userteams.rb
@@ -177,8 +177,8 @@ class Userteams &lt; Spinach::FeatureSteps @@ -177,8 +177,8 @@ class Userteams &lt; Spinach::FeatureSteps
177 And 'I select user "John" from list with role "Reporter"' do 177 And 'I select user "John" from list with role "Reporter"' do
178 user = User.find_by_name("John") 178 user = User.find_by_name("John")
179 within "#team_members" do 179 within "#team_members" do
180 - select user.name, :from => "user_ids"  
181 - select "Reporter", :from => "default_project_access" 180 + select "#{user.name} (#{user.email})", from: "user_ids"
  181 + select "Reporter", from: "default_project_access"
182 end 182 end
183 click_button "Add" 183 click_button "Add"
184 end 184 end
@@ -213,8 +213,8 @@ class Userteams &lt; Spinach::FeatureSteps @@ -213,8 +213,8 @@ class Userteams &lt; Spinach::FeatureSteps
213 213
214 When 'I submit form with selected project and max access' do 214 When 'I submit form with selected project and max access' do
215 within "#assign_projects" do 215 within "#assign_projects" do
216 - select @project.name_with_namespace, :from => "project_ids"  
217 - select "Reporter", :from => "greatest_project_access" 216 + select @project.name_with_namespace, from: "project_ids"
  217 + select "Reporter", from: "greatest_project_access"
218 end 218 end
219 click_button "Add" 219 click_button "Add"
220 end 220 end
lib/api/internal.rb
@@ -5,6 +5,12 @@ module Gitlab @@ -5,6 +5,12 @@ module Gitlab
5 # 5 #
6 # Check if ssh key has access to project code 6 # Check if ssh key has access to project code
7 # 7 #
  8 + # Params:
  9 + # key_id - SSH Key id
  10 + # project - project path with namespace
  11 + # action - git action (git-upload-pack or git-receive-pack)
  12 + # ref - branch name
  13 + #
8 get "/allowed" do 14 get "/allowed" do
9 key = Key.find(params[:key_id]) 15 key = Key.find(params[:key_id])
10 project = Project.find_with_namespace(params[:project]) 16 project = Project.find_with_namespace(params[:project])
lib/tasks/gitlab/check.rake
@@ -255,7 +255,6 @@ namespace :gitlab do @@ -255,7 +255,6 @@ namespace :gitlab do
255 warn_user_is_not_gitlab 255 warn_user_is_not_gitlab
256 start_checking "Environment" 256 start_checking "Environment"
257 257
258 - check_issue_1059_shell_profile_error  
259 check_gitlab_git_config 258 check_gitlab_git_config
260 check_python2_exists 259 check_python2_exists
261 check_python2_version 260 check_python2_version
@@ -294,30 +293,6 @@ namespace :gitlab do @@ -294,30 +293,6 @@ namespace :gitlab do
294 end 293 end
295 end 294 end
296 295
297 - # see https://github.com/gitlabhq/gitlabhq/issues/1059  
298 - def check_issue_1059_shell_profile_error  
299 - gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user  
300 - print "Has no \"-e\" in ~#{gitlab_shell_ssh_user}/.profile ... "  
301 -  
302 - profile_file = File.join(gitlab_shell_user_home, ".profile")  
303 -  
304 - unless File.read(profile_file) =~ /^-e PATH/  
305 - puts "yes".green  
306 - else  
307 - puts "no".red  
308 - try_fixing_it(  
309 - "Open #{profile_file}",  
310 - "Find the line starting with \"-e PATH\"",  
311 - "Remove \"-e \" so the line starts with PATH"  
312 - )  
313 - for_more_information(  
314 - see_installation_guide_section("Gitlab Shell"),  
315 - "https://github.com/gitlabhq/gitlabhq/issues/1059"  
316 - )  
317 - fix_and_rerun  
318 - end  
319 - end  
320 -  
321 def check_python2_exists 296 def check_python2_exists
322 print "Has python2? ... " 297 print "Has python2? ... "
323 298
spec/features/admin/admin_hooks_spec.rb 0 → 100644
@@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Admin::Hooks" do
  4 + before do
  5 + @project = create(:project)
  6 + login_as :admin
  7 +
  8 + @system_hook = create(:system_hook)
  9 +
  10 + end
  11 +
  12 + describe "GET /admin/hooks" do
  13 + it "should be ok" do
  14 + visit admin_root_path
  15 + within ".main_menu" do
  16 + click_on "Hooks"
  17 + end
  18 + current_path.should == admin_hooks_path
  19 + end
  20 +
  21 + it "should have hooks list" do
  22 + visit admin_hooks_path
  23 + page.should have_content(@system_hook.url)
  24 + end
  25 + end
  26 +
  27 + describe "New Hook" do
  28 + before do
  29 + @url = Faker::Internet.uri("http")
  30 + visit admin_hooks_path
  31 + fill_in "hook_url", with: @url
  32 + expect { click_button "Add System Hook" }.to change(SystemHook, :count).by(1)
  33 + end
  34 +
  35 + it "should open new hook popup" do
  36 + page.current_path.should == admin_hooks_path
  37 + page.should have_content(@url)
  38 + end
  39 + end
  40 +
  41 + describe "Test" do
  42 + before do
  43 + WebMock.stub_request(:post, @system_hook.url)
  44 + visit admin_hooks_path
  45 + click_link "Test Hook"
  46 + end
  47 +
  48 + it { page.current_path.should == admin_hooks_path }
  49 + end
  50 +
  51 +end
spec/features/admin/admin_projects_spec.rb 0 → 100644
@@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Admin::Projects" do
  4 + before do
  5 + @project = create(:project)
  6 + login_as :admin
  7 + end
  8 +
  9 + describe "GET /admin/projects" do
  10 + before do
  11 + visit admin_projects_path
  12 + end
  13 +
  14 + it "should be ok" do
  15 + current_path.should == admin_projects_path
  16 + end
  17 +
  18 + it "should have projects list" do
  19 + page.should have_content(@project.name)
  20 + end
  21 + end
  22 +
  23 + describe "GET /admin/projects/:id" do
  24 + before do
  25 + visit admin_projects_path
  26 + click_link "#{@project.name}"
  27 + end
  28 +
  29 + it "should have project info" do
  30 + page.should have_content(@project.path)
  31 + page.should have_content(@project.name)
  32 + end
  33 + end
  34 +
  35 + describe "GET /admin/projects/:id/edit" do
  36 + before do
  37 + visit admin_projects_path
  38 + click_link "edit_project_#{@project.id}"
  39 + end
  40 +
  41 + it "should have project edit page" do
  42 + page.should have_content("Edit project")
  43 + page.should have_button("Save Project")
  44 + end
  45 +
  46 + describe "Update project" do
  47 + before do
  48 + fill_in "project_name", with: "Big Bang"
  49 + click_button "Save Project"
  50 + @project.reload
  51 + end
  52 +
  53 + it "should show page with new data" do
  54 + page.should have_content("Big Bang")
  55 + end
  56 +
  57 + it "should change project entry" do
  58 + @project.name.should == "Big Bang"
  59 + end
  60 + end
  61 + end
  62 +
  63 + describe "Add new team member" do
  64 + before do
  65 + @new_user = create(:user)
  66 + visit admin_project_path(@project)
  67 + end
  68 +
  69 + it "should create new user" do
  70 + select @new_user.name, from: "user_ids"
  71 + expect { click_button "Add" }.to change { UsersProject.count }.by(1)
  72 + page.should have_content @new_user.name
  73 + current_path.should == admin_project_path(@project)
  74 + end
  75 + end
  76 +end
spec/features/admin/admin_users_spec.rb 0 → 100644
@@ -0,0 +1,134 @@ @@ -0,0 +1,134 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Admin::Users" do
  4 + before { login_as :admin }
  5 +
  6 + describe "GET /admin/users" do
  7 + before do
  8 + visit admin_users_path
  9 + end
  10 +
  11 + it "should be ok" do
  12 + current_path.should == admin_users_path
  13 + end
  14 +
  15 + it "should have users list" do
  16 + page.should have_content(@user.email)
  17 + page.should have_content(@user.name)
  18 + end
  19 + end
  20 +
  21 + describe "GET /admin/users/new" do
  22 + before do
  23 + @password = "123ABC"
  24 + visit new_admin_user_path
  25 + fill_in "user_name", with: "Big Bang"
  26 + fill_in "user_username", with: "bang"
  27 + fill_in "user_email", with: "bigbang@mail.com"
  28 + fill_in "user_password", with: @password
  29 + fill_in "user_password_confirmation", with: @password
  30 + end
  31 +
  32 + it "should create new user" do
  33 + expect { click_button "Save" }.to change {User.count}.by(1)
  34 + end
  35 +
  36 + it "should create user with valid data" do
  37 + click_button "Save"
  38 + user = User.last
  39 + user.name.should == "Big Bang"
  40 + user.email.should == "bigbang@mail.com"
  41 + end
  42 +
  43 + it "should call send mail" do
  44 + Notify.should_receive(:new_user_email)
  45 +
  46 + User.observers.enable :user_observer do
  47 + click_button "Save"
  48 + end
  49 + end
  50 +
  51 + it "should send valid email to user with email & password" do
  52 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)
  53 + User.observers.enable :user_observer do
  54 + click_button "Save"
  55 + user = User.last
  56 + email = ActionMailer::Base.deliveries.last
  57 + email.subject.should have_content("Account was created")
  58 + email.body.should have_content(user.email)
  59 + email.body.should have_content(@password)
  60 + end
  61 + end
  62 +
  63 + it "should send valid email to user with email without password when signup is enabled" do
  64 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
  65 + User.observers.enable :user_observer do
  66 + click_button "Save"
  67 + user = User.last
  68 + email = ActionMailer::Base.deliveries.last
  69 + email.subject.should have_content("Account was created")
  70 + email.body.should have_content(user.email)
  71 + email.body.should_not have_content(@password)
  72 + end
  73 + end
  74 + end
  75 +
  76 + describe "GET /admin/users/:id" do
  77 + before do
  78 + visit admin_users_path
  79 + click_link "#{@user.name}"
  80 + end
  81 +
  82 + it "should have user info" do
  83 + page.should have_content(@user.email)
  84 + page.should have_content(@user.name)
  85 + end
  86 + end
  87 +
  88 + describe "GET /admin/users/:id/edit" do
  89 + before do
  90 + @simple_user = create(:user)
  91 + visit admin_users_path
  92 + click_link "edit_user_#{@simple_user.id}"
  93 + end
  94 +
  95 + it "should have user edit page" do
  96 + page.should have_content("Name")
  97 + page.should have_content("Password")
  98 + end
  99 +
  100 + describe "Update user" do
  101 + before do
  102 + fill_in "user_name", with: "Big Bang"
  103 + fill_in "user_email", with: "bigbang@mail.com"
  104 + check "user_admin"
  105 + click_button "Save"
  106 + end
  107 +
  108 + it "should show page with new data" do
  109 + page.should have_content("bigbang@mail.com")
  110 + page.should have_content("Big Bang")
  111 + end
  112 +
  113 + it "should change user entry" do
  114 + @simple_user.reload
  115 + @simple_user.name.should == "Big Bang"
  116 + @simple_user.is_admin?.should be_true
  117 + end
  118 + end
  119 + end
  120 +
  121 + describe "Add new project" do
  122 + before do
  123 + @new_project = create(:project)
  124 + visit admin_user_path(@user)
  125 + end
  126 +
  127 + it "should create new user" do
  128 + select @new_project.name, from: "project_ids"
  129 + expect { click_button "Add" }.to change { UsersProject.count }.by(1)
  130 + page.should have_content @new_project.name
  131 + current_path.should == admin_user_path(@user)
  132 + end
  133 + end
  134 +end
spec/features/admin/security_spec.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Admin::Projects" do
  4 + describe "GET /admin/projects" do
  5 + subject { admin_projects_path }
  6 +
  7 + it { should be_allowed_for :admin }
  8 + it { should be_denied_for :user }
  9 + it { should be_denied_for :visitor }
  10 + end
  11 +
  12 + describe "GET /admin/users" do
  13 + subject { admin_users_path }
  14 +
  15 + it { should be_allowed_for :admin }
  16 + it { should be_denied_for :user }
  17 + it { should be_denied_for :visitor }
  18 + end
  19 +
  20 + describe "GET /admin/hooks" do
  21 + subject { admin_hooks_path }
  22 +
  23 + it { should be_allowed_for :admin }
  24 + it { should be_denied_for :user }
  25 + it { should be_denied_for :visitor }
  26 + end
  27 +end
spec/features/atom/dashboard_issues_spec.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Dashboard Issues Feed" do
  4 + describe "GET /issues" do
  5 + let!(:user) { create(:user) }
  6 + let!(:project1) { create(:project) }
  7 + let!(:project2) { create(:project) }
  8 + let!(:issue1) { create(:issue, author: user, assignee: user, project: project1) }
  9 + let!(:issue2) { create(:issue, author: user, assignee: user, project: project2) }
  10 +
  11 + describe "atom feed" do
  12 + it "should render atom feed via private token" do
  13 + visit issues_dashboard_path(:atom, private_token: user.private_token)
  14 +
  15 + page.response_headers['Content-Type'].should have_content("application/atom+xml")
  16 + page.body.should have_selector("title", text: "#{user.name} issues")
  17 + page.body.should have_selector("author email", text: issue1.author_email)
  18 + page.body.should have_selector("entry summary", text: issue1.title)
  19 + page.body.should have_selector("author email", text: issue2.author_email)
  20 + page.body.should have_selector("entry summary", text: issue2.title)
  21 + end
  22 + end
  23 + end
  24 +end
spec/features/atom/dashboard_spec.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Dashboard Feed" do
  4 + describe "GET /" do
  5 + let!(:user) { create(:user) }
  6 +
  7 + context "projects atom feed via private token" do
  8 + it "should render projects atom feed" do
  9 + visit dashboard_path(:atom, private_token: user.private_token)
  10 + page.body.should have_selector("feed title")
  11 + end
  12 + end
  13 + end
  14 +end
spec/features/atom/issues_spec.rb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Issues Feed" do
  4 + describe "GET /issues" do
  5 + let!(:user) { create(:user) }
  6 + let!(:project) { create(:project, namespace: user.namespace) }
  7 + let!(:issue) { create(:issue, author: user, project: project) }
  8 +
  9 + before { project.team << [user, :developer] }
  10 +
  11 + context "when authenticated" do
  12 + it "should render atom feed" do
  13 + login_with user
  14 + visit project_issues_path(project, :atom)
  15 +
  16 + page.response_headers['Content-Type'].should have_content("application/atom+xml")
  17 + page.body.should have_selector("title", text: "#{project.name} issues")
  18 + page.body.should have_selector("author email", text: issue.author_email)
  19 + page.body.should have_selector("entry summary", text: issue.title)
  20 + end
  21 + end
  22 +
  23 + context "when authenticated via private token" do
  24 + it "should render atom feed" do
  25 + visit project_issues_path(project, :atom, private_token: user.private_token)
  26 +
  27 + page.response_headers['Content-Type'].should have_content("application/atom+xml")
  28 + page.body.should have_selector("title", text: "#{project.name} issues")
  29 + page.body.should have_selector("author email", text: issue.author_email)
  30 + page.body.should have_selector("entry summary", text: issue.title)
  31 + end
  32 + end
  33 + end
  34 +end
spec/features/gitlab_flavored_markdown_spec.rb 0 → 100644
@@ -0,0 +1,231 @@ @@ -0,0 +1,231 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Gitlab Flavored Markdown" do
  4 + let(:project) { create(:project) }
  5 + let(:issue) { create(:issue, project: project) }
  6 + let(:merge_request) { create(:merge_request, project: project) }
  7 + let(:fred) do
  8 + u = create(:user, name: "fred")
  9 + project.team << [u, :master]
  10 + u
  11 + end
  12 +
  13 + before do
  14 + # add test branch
  15 + @branch_name = "gfm-test"
  16 + r = project.repo
  17 + i = r.index
  18 + # add test file
  19 + @test_file = "gfm_test_file"
  20 + i.add(@test_file, "foo\nbar\n")
  21 + # add commit with gfm
  22 + i.commit("fix ##{issue.id}\n\nask @#{fred.username} for details", head: @branch_name)
  23 +
  24 + # add test tag
  25 + @tag_name = "gfm-test-tag"
  26 + r.git.native(:tag, {}, @tag_name, commit.id)
  27 + end
  28 +
  29 + after do
  30 + # delete test branch and tag
  31 + project.repo.git.native(:branch, {D: true}, @branch_name)
  32 + project.repo.git.native(:tag, {d: true}, @tag_name)
  33 + project.repo.gc_auto
  34 + end
  35 +
  36 + let(:commit) { project.repository.commits(@branch_name).first }
  37 +
  38 + before do
  39 + login_as :user
  40 + project.team << [@user, :developer]
  41 + end
  42 +
  43 + describe "for commits" do
  44 + it "should render title in commits#index" do
  45 + visit project_commits_path(project, @branch_name, limit: 1)
  46 +
  47 + page.should have_link("##{issue.id}")
  48 + end
  49 +
  50 + it "should render title in commits#show" do
  51 + visit project_commit_path(project, commit)
  52 +
  53 + page.should have_link("##{issue.id}")
  54 + end
  55 +
  56 + it "should render description in commits#show" do
  57 + visit project_commit_path(project, commit)
  58 +
  59 + page.should have_link("@#{fred.username}")
  60 + end
  61 +
  62 + it "should render title in refs#tree", js: true do
  63 + visit project_tree_path(project, @branch_name)
  64 +
  65 + within(".tree_commit") do
  66 + page.should have_link("##{issue.id}")
  67 + end
  68 + end
  69 +
  70 + # @wip
  71 + #it "should render title in refs#blame" do
  72 + #visit project_blame_path(project, File.join(@branch_name, @test_file))
  73 +
  74 + #within(".blame_commit") do
  75 + #page.should have_link("##{issue.id}")
  76 + #end
  77 + #end
  78 +
  79 + it "should render title in repositories#branches" do
  80 + visit branches_project_repository_path(project)
  81 +
  82 + page.should have_link("##{issue.id}")
  83 + end
  84 + end
  85 +
  86 + describe "for issues" do
  87 + before do
  88 + @other_issue = create(:issue,
  89 + author: @user,
  90 + assignee: @user,
  91 + project: project)
  92 + @issue = create(:issue,
  93 + author: @user,
  94 + assignee: @user,
  95 + project: project,
  96 + title: "fix ##{@other_issue.id}",
  97 + description: "ask @#{fred.username} for details")
  98 + end
  99 +
  100 + it "should render subject in issues#index" do
  101 + visit project_issues_path(project)
  102 +
  103 + page.should have_link("##{@other_issue.id}")
  104 + end
  105 +
  106 + it "should render subject in issues#show" do
  107 + visit project_issue_path(project, @issue)
  108 +
  109 + page.should have_link("##{@other_issue.id}")
  110 + end
  111 +
  112 + it "should render details in issues#show" do
  113 + visit project_issue_path(project, @issue)
  114 +
  115 + page.should have_link("@#{fred.username}")
  116 + end
  117 + end
  118 +
  119 +
  120 + describe "for merge requests" do
  121 + before do
  122 + @merge_request = create(:merge_request,
  123 + project: project,
  124 + title: "fix ##{issue.id}")
  125 + end
  126 +
  127 + it "should render title in merge_requests#index" do
  128 + visit project_merge_requests_path(project)
  129 +
  130 + page.should have_link("##{issue.id}")
  131 + end
  132 +
  133 + it "should render title in merge_requests#show" do
  134 + visit project_merge_request_path(project, @merge_request)
  135 +
  136 + page.should have_link("##{issue.id}")
  137 + end
  138 + end
  139 +
  140 +
  141 + describe "for milestones" do
  142 + before do
  143 + @milestone = create(:milestone,
  144 + project: project,
  145 + title: "fix ##{issue.id}",
  146 + description: "ask @#{fred.username} for details")
  147 + end
  148 +
  149 + it "should render title in milestones#index" do
  150 + visit project_milestones_path(project)
  151 +
  152 + page.should have_link("##{issue.id}")
  153 + end
  154 +
  155 + it "should render title in milestones#show" do
  156 + visit project_milestone_path(project, @milestone)
  157 +
  158 + page.should have_link("##{issue.id}")
  159 + end
  160 +
  161 + it "should render description in milestones#show" do
  162 + visit project_milestone_path(project, @milestone)
  163 +
  164 + page.should have_link("@#{fred.username}")
  165 + end
  166 + end
  167 +
  168 +
  169 + describe "for notes" do
  170 + it "should render in commits#show", js: true do
  171 + visit project_commit_path(project, commit)
  172 + within ".new_note.js-main-target-form" do
  173 + fill_in "note_note", with: "see ##{issue.id}"
  174 + click_button "Add Comment"
  175 + end
  176 +
  177 + page.should have_link("##{issue.id}")
  178 + end
  179 +
  180 + it "should render in issue#show", js: true do
  181 + visit project_issue_path(project, issue)
  182 + within ".new_note.js-main-target-form" do
  183 + fill_in "note_note", with: "see ##{issue.id}"
  184 + click_button "Add Comment"
  185 + end
  186 +
  187 + page.should have_link("##{issue.id}")
  188 + end
  189 +
  190 + it "should render in merge_request#show", js: true do
  191 + visit project_merge_request_path(project, merge_request)
  192 + within ".new_note.js-main-target-form" do
  193 + fill_in "note_note", with: "see ##{issue.id}"
  194 + click_button "Add Comment"
  195 + end
  196 +
  197 + page.should have_link("##{issue.id}")
  198 + end
  199 +
  200 + it "should render in projects#wall", js: true do
  201 + visit wall_project_path(project)
  202 + within ".new_note.js-main-target-form" do
  203 + fill_in "note_note", with: "see ##{issue.id}"
  204 + click_button "Add Comment"
  205 + end
  206 +
  207 + page.should have_link("##{issue.id}")
  208 + end
  209 + end
  210 +
  211 +
  212 + describe "for wikis" do
  213 + before do
  214 + visit project_wiki_path(project, :index)
  215 + fill_in "Title", with: "Circumvent ##{issue.id}"
  216 + fill_in "Content", with: "# Other pages\n\n* [Foo](foo)\n* [Bar](bar)\n\nAlso look at ##{issue.id} :-)"
  217 + click_on "Save"
  218 + end
  219 +
  220 + it "should NOT render title in wikis#show" do
  221 + within(".content h3") do # page title
  222 + page.should have_content("Circumvent ##{issue.id}")
  223 + page.should_not have_link("##{issue.id}")
  224 + end
  225 + end
  226 +
  227 + it "should render content in wikis#show" do
  228 + page.should have_link("##{issue.id}")
  229 + end
  230 + end
  231 +end
spec/features/issues_spec.rb 0 → 100644
@@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Issues" do
  4 + let(:project) { create(:project) }
  5 +
  6 + before do
  7 + login_as :user
  8 + user2 = create(:user)
  9 +
  10 + project.team << [[@user, user2], :developer]
  11 + end
  12 +
  13 + describe "Edit issue" do
  14 + let!(:issue) do
  15 + create(:issue,
  16 + author: @user,
  17 + assignee: @user,
  18 + project: project)
  19 + end
  20 +
  21 + before do
  22 + visit project_issues_path(project)
  23 + click_link "Edit"
  24 + end
  25 +
  26 + it "should open new issue popup" do
  27 + page.should have_content("Issue ##{issue.id}")
  28 + end
  29 +
  30 + describe "fill in" do
  31 + before do
  32 + fill_in "issue_title", with: "bug 345"
  33 + fill_in "issue_description", with: "bug description"
  34 + end
  35 +
  36 + it { expect { click_button "Save changes" }.to_not change {Issue.count} }
  37 +
  38 + it "should update issue fields" do
  39 + click_button "Save changes"
  40 +
  41 + page.should have_content @user.name
  42 + page.should have_content "bug 345"
  43 + page.should have_content project.name
  44 + end
  45 + end
  46 + end
  47 +
  48 + describe "Search issue", js: true do
  49 + before do
  50 + ['foobar', 'foobar2', 'gitlab'].each do |title|
  51 + create(:issue,
  52 + author: @user,
  53 + assignee: @user,
  54 + project: project,
  55 + title: title)
  56 + end
  57 + end
  58 +
  59 + it "should be able to search on different statuses" do
  60 + issue = Issue.first # with title 'foobar'
  61 + issue.close
  62 +
  63 + visit project_issues_path(project)
  64 + click_link 'Closed'
  65 + fill_in 'issue_search', with: 'foobar'
  66 +
  67 + page.should have_content 'foobar'
  68 + page.should_not have_content 'foobar2'
  69 + page.should_not have_content 'gitlab'
  70 + end
  71 +
  72 + it "should search for term and return the correct results" do
  73 + visit project_issues_path(project)
  74 + fill_in 'issue_search', with: 'foobar'
  75 +
  76 + page.should have_content 'foobar'
  77 + page.should have_content 'foobar2'
  78 + page.should_not have_content 'gitlab'
  79 + end
  80 + end
  81 +
  82 + describe "Filter issue" do
  83 + before do
  84 + ['foobar', 'barbaz', 'gitlab'].each do |title|
  85 + create(:issue,
  86 + author: @user,
  87 + assignee: @user,
  88 + project: project,
  89 + title: title)
  90 + end
  91 +
  92 + @issue = Issue.first # with title 'foobar'
  93 + @issue.milestone = create(:milestone, project: project)
  94 + @issue.assignee = nil
  95 + @issue.save
  96 + end
  97 +
  98 + let(:issue) { @issue }
  99 +
  100 + it "should allow filtering by issues with no specified milestone" do
  101 + visit project_issues_path(project, milestone_id: '0')
  102 +
  103 + page.should_not have_content 'foobar'
  104 + page.should have_content 'barbaz'
  105 + page.should have_content 'gitlab'
  106 + end
  107 +
  108 + it "should allow filtering by a specified milestone" do
  109 + visit project_issues_path(project, milestone_id: issue.milestone.id)
  110 +
  111 + page.should have_content 'foobar'
  112 + page.should_not have_content 'barbaz'
  113 + page.should_not have_content 'gitlab'
  114 + end
  115 +
  116 + it "should allow filtering by issues with no specified assignee" do
  117 + visit project_issues_path(project, assignee_id: '0')
  118 +
  119 + page.should have_content 'foobar'
  120 + page.should_not have_content 'barbaz'
  121 + page.should_not have_content 'gitlab'
  122 + end
  123 +
  124 + it "should allow filtering by a specified assignee" do
  125 + visit project_issues_path(project, assignee_id: @user.id)
  126 +
  127 + page.should_not have_content 'foobar'
  128 + page.should have_content 'barbaz'
  129 + page.should have_content 'gitlab'
  130 + end
  131 + end
  132 +end
spec/features/notes_on_merge_requests_spec.rb 0 → 100644
@@ -0,0 +1,236 @@ @@ -0,0 +1,236 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "On a merge request", js: true do
  4 + let!(:project) { create(:project) }
  5 + let!(:merge_request) { create(:merge_request, project: project) }
  6 +
  7 + before do
  8 + login_as :user
  9 + project.team << [@user, :master]
  10 +
  11 + visit project_merge_request_path(project, merge_request)
  12 + end
  13 +
  14 + subject { page }
  15 +
  16 + describe "the note form" do
  17 + # main target form creation
  18 + it { should have_css(".js-main-target-form", visible: true, count: 1) }
  19 +
  20 + # button initalization
  21 + it { find(".js-main-target-form input[type=submit]").value.should == "Add Comment" }
  22 + it { within(".js-main-target-form") { should_not have_link("Cancel") } }
  23 +
  24 + # notifiactions
  25 + it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } }
  26 + it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } }
  27 + it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } }
  28 +
  29 + describe "without text" do
  30 + it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }
  31 + end
  32 +
  33 + describe "with text" do
  34 + before do
  35 + within(".js-main-target-form") do
  36 + fill_in "note[note]", with: "This is awesome"
  37 + end
  38 + end
  39 +
  40 + it { within(".js-main-target-form") { should_not have_css(".js-comment-button[disabled]") } }
  41 +
  42 + it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: true) } }
  43 + end
  44 +
  45 + describe "with preview" do
  46 + before do
  47 + within(".js-main-target-form") do
  48 + fill_in "note[note]", with: "This is awesome"
  49 + find(".js-note-preview-button").trigger("click")
  50 + end
  51 + end
  52 +
  53 + it { within(".js-main-target-form") { should have_css(".js-note-preview", text: "This is awesome", visible: true) } }
  54 +
  55 + it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }
  56 + it { within(".js-main-target-form") { should have_css(".js-note-edit-button", visible: true) } }
  57 + end
  58 + end
  59 +
  60 + describe "when posting a note" do
  61 + before do
  62 + within(".js-main-target-form") do
  63 + fill_in "note[note]", with: "This is awsome!"
  64 + find(".js-note-preview-button").trigger("click")
  65 + click_button "Add Comment"
  66 + end
  67 + end
  68 +
  69 + # note added
  70 + it { should have_content("This is awsome!") }
  71 +
  72 + # reset form
  73 + it { within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } }
  74 +
  75 + # return from preview
  76 + it { within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) } }
  77 + it { within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } }
  78 +
  79 +
  80 + it "should be removable" do
  81 + find(".js-note-delete").trigger("click")
  82 +
  83 + should_not have_css(".note")
  84 + end
  85 + end
  86 +end
  87 +
  88 +
  89 +
  90 +describe "On a merge request diff", js: true, focus: true do
  91 + let!(:project) { create(:project) }
  92 + let!(:merge_request) { create(:merge_request_with_diffs, project: project) }
  93 +
  94 + before do
  95 + login_as :user
  96 + project.team << [@user, :master]
  97 +
  98 + visit diffs_project_merge_request_path(project, merge_request)
  99 +
  100 + within '.diffs-tab' do
  101 + click_link("Diff")
  102 + end
  103 + end
  104 +
  105 + subject { page }
  106 +
  107 + describe "when adding a note" do
  108 + before do
  109 + find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click")
  110 + end
  111 +
  112 + describe "the notes holder" do
  113 + it { should have_css("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") }
  114 +
  115 + it { within(".js-temp-notes-holder") { should have_css(".new_note") } }
  116 + end
  117 +
  118 + describe "the note form" do
  119 + # set up hidden fields correctly
  120 + it { within(".js-temp-notes-holder") { find("#note_noteable_type").value.should == "MergeRequest" } }
  121 + it { within(".js-temp-notes-holder") { find("#note_noteable_id").value.should == merge_request.id.to_s } }
  122 + it { within(".js-temp-notes-holder") { find("#note_commit_id").value.should == "" } }
  123 + it { within(".js-temp-notes-holder") { find("#note_line_code").value.should == "4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185" } }
  124 +
  125 + # buttons
  126 + it { should have_button("Add Comment") }
  127 + it { should have_css(".js-close-discussion-note-form", text: "Cancel") }
  128 +
  129 + # notification options
  130 + it { should have_checked_field("Notify team via email") }
  131 +
  132 + it "shouldn't add a second form for same row" do
  133 + find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click")
  134 +
  135 + should have_css("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder form", count: 1)
  136 + end
  137 +
  138 + it "should be removed when canceled" do
  139 + within(".file form[rel$='4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185']") do
  140 + find(".js-close-discussion-note-form").trigger("click")
  141 + end
  142 +
  143 + should have_no_css(".js-temp-notes-holder")
  144 + end
  145 + end
  146 + end
  147 +
  148 + describe "with muliple note forms" do
  149 + before do
  150 + find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click")
  151 + find("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder .js-add-diff-note-button").trigger("click")
  152 + end
  153 +
  154 + # has two line forms
  155 + it { should have_css(".js-temp-notes-holder", count: 2) }
  156 +
  157 + describe "previewing them separately" do
  158 + before do
  159 + # add two separate texts and trigger previews on both
  160 + within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") do
  161 + fill_in "note[note]", with: "One comment on line 185"
  162 + find(".js-note-preview-button").trigger("click")
  163 + end
  164 + within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") do
  165 + fill_in "note[note]", with: "Another comment on line 17"
  166 + find(".js-note-preview-button").trigger("click")
  167 + end
  168 + end
  169 +
  170 + # check if previews were rendered separately
  171 + it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") { should have_css(".js-note-preview", text: "One comment on line 185") } }
  172 + it { within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") { should have_css(".js-note-preview", text: "Another comment on line 17") } }
  173 + end
  174 +
  175 + describe "posting a note" do
  176 + before do
  177 + within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") do
  178 + fill_in "note[note]", with: "Another comment on line 17"
  179 + click_button("Add Comment")
  180 + end
  181 + end
  182 +
  183 + # removed form after submit
  184 + it { should have_no_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") }
  185 +
  186 + # added discussion
  187 + it { should have_content("Another comment on line 17") }
  188 + it { should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder") }
  189 + it { should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder .note", count: 1) }
  190 + it { should have_link("Reply") }
  191 +
  192 + it "should remove last note of a discussion" do
  193 + within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder") do
  194 + find(".js-note-delete").trigger("click")
  195 + end
  196 +
  197 + # removed whole discussion
  198 + should_not have_css(".note_holder")
  199 + should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + #342e16cbbd482ac2047dc679b2749d248cc1428f_18_18.line_holder")
  200 + end
  201 + end
  202 + end
  203 +
  204 + describe "when replying to a note" do
  205 + before do
  206 + # create first note
  207 + find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder .js-add-diff-note-button").trigger("click")
  208 + within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .js-temp-notes-holder") do
  209 + fill_in "note[note]", with: "One comment on line 184"
  210 + click_button("Add Comment")
  211 + end
  212 + # create second note
  213 + within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") do
  214 + find(".js-discussion-reply-button").trigger("click")
  215 + fill_in "note[note]", with: "An additional comment in reply"
  216 + click_button("Add Comment")
  217 + end
  218 + end
  219 +
  220 + # inserted note
  221 + it { should have_content("An additional comment in reply") }
  222 + it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_css(".note", count: 2) } }
  223 +
  224 + # removed form after reply
  225 + it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_no_css("form") } }
  226 + it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_link("Reply") } }
  227 + end
  228 +end
  229 +
  230 +
  231 +
  232 +describe "On merge request discussion", js: true do
  233 + describe "with merge request diff note"
  234 + describe "with commit note"
  235 + describe "with commit diff note"
  236 +end
spec/features/notes_on_wall_spec.rb 0 → 100644
@@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "On the project wall", js: true do
  4 + let!(:project) { create(:project) }
  5 + let!(:commit) { project.repository.commit("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }
  6 +
  7 + before do
  8 + login_as :user
  9 + project.team << [@user, :master]
  10 + visit wall_project_path(project)
  11 + end
  12 +
  13 + subject { page }
  14 +
  15 + describe "the note form" do
  16 + # main target form creation
  17 + it { should have_css(".js-main-target-form", visible: true, count: 1) }
  18 +
  19 + # button initalization
  20 + it { find(".js-main-target-form input[type=submit]").value.should == "Add Comment" }
  21 + it { within(".js-main-target-form") { should_not have_link("Cancel") } }
  22 +
  23 + # notifiactions
  24 + it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } }
  25 + it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } }
  26 + it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } }
  27 +
  28 + describe "without text" do
  29 + it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }
  30 + end
  31 +
  32 + describe "with text" do
  33 + before do
  34 + within(".js-main-target-form") do
  35 + fill_in "note[note]", with: "This is awesome"
  36 + end
  37 + end
  38 +
  39 + it { within(".js-main-target-form") { should_not have_css(".js-comment-button[disabled]") } }
  40 +
  41 + it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: true) } }
  42 + end
  43 +
  44 + describe "with preview" do
  45 + before do
  46 + within(".js-main-target-form") do
  47 + fill_in "note[note]", with: "This is awesome"
  48 + find(".js-note-preview-button").trigger("click")
  49 + end
  50 + end
  51 +
  52 + it { within(".js-main-target-form") { should have_css(".js-note-preview", text: "This is awesome", visible: true) } }
  53 +
  54 + it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }
  55 + it { within(".js-main-target-form") { should have_css(".js-note-edit-button", visible: true) } }
  56 + end
  57 + end
  58 +
  59 + describe "when posting a note" do
  60 + before do
  61 + within(".js-main-target-form") do
  62 + fill_in "note[note]", with: "This is awsome!"
  63 + find(".js-note-preview-button").trigger("click")
  64 + click_button "Add Comment"
  65 + end
  66 + end
  67 +
  68 + # note added
  69 + it { should have_content("This is awsome!") }
  70 +
  71 + # reset form
  72 + it { within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } }
  73 +
  74 + # return from preview
  75 + it { within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) } }
  76 + it { within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } }
  77 +
  78 +
  79 + it "should be removable" do
  80 + find(".js-note-delete").trigger("click")
  81 +
  82 + should_not have_css(".note")
  83 + end
  84 + end
  85 +end
spec/features/profile_spec.rb 0 → 100644
@@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Profile account page" do
  4 + let(:user) { create(:user) }
  5 +
  6 + before do
  7 + login_as :user
  8 + end
  9 +
  10 + describe "when signup is enabled" do
  11 + before do
  12 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
  13 + visit account_profile_path
  14 + end
  15 + it { page.should have_content("Remove account") }
  16 +
  17 + it "should delete the account", js: true do
  18 + expect { click_link "Delete account" }.to change {User.count}.by(-1)
  19 + current_path.should == new_user_session_path
  20 + end
  21 + end
  22 +
  23 + describe "when signup is enabled and user has a project" do
  24 + before do
  25 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
  26 + @project = create(:project, namespace: @user.namespace)
  27 + @project.team << [@user, :master]
  28 + visit account_profile_path
  29 + end
  30 + it { page.should have_content("Remove account") }
  31 +
  32 + it "should not allow user to delete the account" do
  33 + expect { click_link "Delete account" }.not_to change {User.count}.by(-1)
  34 + end
  35 + end
  36 +
  37 + describe "when signup is disabled" do
  38 + before do
  39 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)
  40 + visit account_profile_path
  41 + end
  42 +
  43 + it "should not have option to remove account" do
  44 + page.should_not have_content("Remove account")
  45 + current_path.should == account_profile_path
  46 + end
  47 + end
  48 +end
0 \ No newline at end of file 49 \ No newline at end of file
spec/features/projects_deploy_keys_spec.rb 0 → 100644
@@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Projects", "DeployKeys" do
  4 + let(:project) { create(:project) }
  5 +
  6 + before do
  7 + login_as :user
  8 + project.team << [@user, :master]
  9 + end
  10 +
  11 + describe "GET /keys" do
  12 + before do
  13 + @key = create(:key, project: project)
  14 + visit project_deploy_keys_path(project)
  15 + end
  16 +
  17 + subject { page }
  18 +
  19 + it { should have_content(@key.title) }
  20 +
  21 + describe "Destroy" do
  22 + before { visit project_deploy_key_path(project, @key) }
  23 +
  24 + it "should remove entry" do
  25 + expect {
  26 + click_link "Remove"
  27 + }.to change { project.deploy_keys.count }.by(-1)
  28 + end
  29 + end
  30 + end
  31 +
  32 + describe "New key" do
  33 + before do
  34 + visit project_deploy_keys_path(project)
  35 + click_link "New Deploy Key"
  36 + end
  37 +
  38 + it "should open new key popup" do
  39 + page.should have_content("New Deploy key")
  40 + end
  41 +
  42 + describe "fill in" do
  43 + before do
  44 + fill_in "key_title", with: "laptop"
  45 + fill_in "key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop"
  46 + end
  47 +
  48 + it { expect { click_button "Save" }.to change {Key.count}.by(1) }
  49 +
  50 + it "should add new key to table" do
  51 + click_button "Save"
  52 +
  53 + page.should have_content "laptop"
  54 + end
  55 + end
  56 + end
  57 +
  58 + describe "Show page" do
  59 + before do
  60 + @key = create(:key, project: project)
  61 + visit project_deploy_key_path(project, @key)
  62 + end
  63 +
  64 + it { page.should have_content @key.title }
  65 + it { page.should have_content @key.key[0..10] }
  66 + end
  67 +end
spec/features/projects_spec.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Projects" do
  4 + before { login_as :user }
  5 +
  6 + describe "DELETE /projects/:id" do
  7 + before do
  8 + @project = create(:project, namespace: @user.namespace)
  9 + @project.team << [@user, :master]
  10 + visit edit_project_path(@project)
  11 + end
  12 +
  13 + it "should be correct path" do
  14 + expect { click_link "Remove" }.to change {Project.count}.by(-1)
  15 + end
  16 + end
  17 +end
spec/features/search_spec.rb 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Search" do
  4 + before do
  5 + login_as :user
  6 + @project = create(:project)
  7 + @project.team << [@user, :reporter]
  8 + visit search_path
  9 +
  10 + within '.search-holder' do
  11 + fill_in "search", with: @project.name[0..3]
  12 + click_button "Search"
  13 + end
  14 + end
  15 +
  16 + it "should show project in search results" do
  17 + page.should have_content @project.name
  18 + end
  19 +end
  20 +
spec/features/security/profile_access_spec.rb 0 → 100644
@@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Users Security" do
  4 + describe "Project" do
  5 + before do
  6 + @u1 = create(:user)
  7 + end
  8 +
  9 + describe "GET /login" do
  10 + it { new_user_session_path.should_not be_404_for :visitor }
  11 + end
  12 +
  13 + describe "GET /keys" do
  14 + subject { keys_path }
  15 +
  16 + it { should be_allowed_for @u1 }
  17 + it { should be_allowed_for :admin }
  18 + it { should be_allowed_for :user }
  19 + it { should be_denied_for :visitor }
  20 + end
  21 +
  22 + describe "GET /profile" do
  23 + subject { profile_path }
  24 +
  25 + it { should be_allowed_for @u1 }
  26 + it { should be_allowed_for :admin }
  27 + it { should be_allowed_for :user }
  28 + it { should be_denied_for :visitor }
  29 + end
  30 +
  31 + describe "GET /profile/account" do
  32 + subject { account_profile_path }
  33 +
  34 + it { should be_allowed_for @u1 }
  35 + it { should be_allowed_for :admin }
  36 + it { should be_allowed_for :user }
  37 + it { should be_denied_for :visitor }
  38 + end
  39 +
  40 + describe "GET /profile/design" do
  41 + subject { design_profile_path }
  42 +
  43 + it { should be_allowed_for @u1 }
  44 + it { should be_allowed_for :admin }
  45 + it { should be_allowed_for :user }
  46 + it { should be_denied_for :visitor }
  47 + end
  48 + end
  49 +end
spec/features/security/project_access_spec.rb 0 → 100644
@@ -0,0 +1,243 @@ @@ -0,0 +1,243 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Application access" do
  4 + describe "GET /" do
  5 + it { root_path.should be_allowed_for :admin }
  6 + it { root_path.should be_allowed_for :user }
  7 + it { root_path.should be_denied_for :visitor }
  8 + end
  9 +
  10 + describe "GET /projects/new" do
  11 + it { new_project_path.should be_allowed_for :admin }
  12 + it { new_project_path.should be_allowed_for :user }
  13 + it { new_project_path.should be_denied_for :visitor }
  14 + end
  15 +
  16 + describe "Project" do
  17 + let(:project) { create(:project) }
  18 +
  19 + let(:master) { create(:user) }
  20 + let(:guest) { create(:user) }
  21 + let(:reporter) { create(:user) }
  22 +
  23 + before do
  24 + # full access
  25 + project.team << [master, :master]
  26 +
  27 + # readonly
  28 + project.team << [reporter, :reporter]
  29 + end
  30 +
  31 + describe "GET /project_code" do
  32 + subject { project_path(project) }
  33 +
  34 + it { should be_allowed_for master }
  35 + it { should be_allowed_for reporter }
  36 + it { should be_denied_for :admin }
  37 + it { should be_denied_for guest }
  38 + it { should be_denied_for :user }
  39 + it { should be_denied_for :visitor }
  40 + end
  41 +
  42 + describe "GET /project_code/tree/master" do
  43 + subject { project_tree_path(project, project.repository.root_ref) }
  44 +
  45 + it { should be_allowed_for master }
  46 + it { should be_allowed_for reporter }
  47 + it { should be_denied_for :admin }
  48 + it { should be_denied_for guest }
  49 + it { should be_denied_for :user }
  50 + it { should be_denied_for :visitor }
  51 + end
  52 +
  53 + describe "GET /project_code/commits/master" do
  54 + subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
  55 +
  56 + it { should be_allowed_for master }
  57 + it { should be_allowed_for reporter }
  58 + it { should be_denied_for :admin }
  59 + it { should be_denied_for guest }
  60 + it { should be_denied_for :user }
  61 + it { should be_denied_for :visitor }
  62 + end
  63 +
  64 + describe "GET /project_code/commit/:sha" do
  65 + subject { project_commit_path(project, project.repository.commit) }
  66 +
  67 + it { should be_allowed_for master }
  68 + it { should be_allowed_for reporter }
  69 + it { should be_denied_for :admin }
  70 + it { should be_denied_for guest }
  71 + it { should be_denied_for :user }
  72 + it { should be_denied_for :visitor }
  73 + end
  74 +
  75 + describe "GET /project_code/compare" do
  76 + subject { project_compare_index_path(project) }
  77 +
  78 + it { should be_allowed_for master }
  79 + it { should be_allowed_for reporter }
  80 + it { should be_denied_for :admin }
  81 + it { should be_denied_for guest }
  82 + it { should be_denied_for :user }
  83 + it { should be_denied_for :visitor }
  84 + end
  85 +
  86 + describe "GET /project_code/team" do
  87 + subject { project_team_index_path(project) }
  88 +
  89 + it { should be_allowed_for master }
  90 + it { should be_allowed_for reporter }
  91 + it { should be_denied_for :admin }
  92 + it { should be_denied_for guest }
  93 + it { should be_denied_for :user }
  94 + it { should be_denied_for :visitor }
  95 + end
  96 +
  97 + describe "GET /project_code/wall" do
  98 + subject { wall_project_path(project) }
  99 +
  100 + it { should be_allowed_for master }
  101 + it { should be_allowed_for reporter }
  102 + it { should be_denied_for :admin }
  103 + it { should be_denied_for guest }
  104 + it { should be_denied_for :user }
  105 + it { should be_denied_for :visitor }
  106 + end
  107 +
  108 + describe "GET /project_code/blob" do
  109 + before do
  110 + commit = project.repository.commit
  111 + path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name
  112 + @blob_path = project_blob_path(project, File.join(commit.id, path))
  113 + end
  114 +
  115 + it { @blob_path.should be_allowed_for master }
  116 + it { @blob_path.should be_allowed_for reporter }
  117 + it { @blob_path.should be_denied_for :admin }
  118 + it { @blob_path.should be_denied_for guest }
  119 + it { @blob_path.should be_denied_for :user }
  120 + it { @blob_path.should be_denied_for :visitor }
  121 + end
  122 +
  123 + describe "GET /project_code/edit" do
  124 + subject { edit_project_path(project) }
  125 +
  126 + it { should be_allowed_for master }
  127 + it { should be_denied_for reporter }
  128 + it { should be_denied_for :admin }
  129 + it { should be_denied_for guest }
  130 + it { should be_denied_for :user }
  131 + it { should be_denied_for :visitor }
  132 + end
  133 +
  134 + describe "GET /project_code/deploy_keys" do
  135 + subject { project_deploy_keys_path(project) }
  136 +
  137 + it { should be_allowed_for master }
  138 + it { should be_denied_for reporter }
  139 + it { should be_denied_for :admin }
  140 + it { should be_denied_for guest }
  141 + it { should be_denied_for :user }
  142 + it { should be_denied_for :visitor }
  143 + end
  144 +
  145 + describe "GET /project_code/issues" do
  146 + subject { project_issues_path(project) }
  147 +
  148 + it { should be_allowed_for master }
  149 + it { should be_allowed_for reporter }
  150 + it { should be_denied_for :admin }
  151 + it { should be_denied_for guest }
  152 + it { should be_denied_for :user }
  153 + it { should be_denied_for :visitor }
  154 + end
  155 +
  156 + describe "GET /project_code/snippets" do
  157 + subject { project_snippets_path(project) }
  158 +
  159 + it { should be_allowed_for master }
  160 + it { should be_allowed_for reporter }
  161 + it { should be_denied_for :admin }
  162 + it { should be_denied_for guest }
  163 + it { should be_denied_for :user }
  164 + it { should be_denied_for :visitor }
  165 + end
  166 +
  167 + describe "GET /project_code/merge_requests" do
  168 + subject { project_merge_requests_path(project) }
  169 +
  170 + it { should be_allowed_for master }
  171 + it { should be_allowed_for reporter }
  172 + it { should be_denied_for :admin }
  173 + it { should be_denied_for guest }
  174 + it { should be_denied_for :user }
  175 + it { should be_denied_for :visitor }
  176 + end
  177 +
  178 + describe "GET /project_code/repository" do
  179 + subject { project_repository_path(project) }
  180 +
  181 + it { should be_allowed_for master }
  182 + it { should be_allowed_for reporter }
  183 + it { should be_denied_for :admin }
  184 + it { should be_denied_for guest }
  185 + it { should be_denied_for :user }
  186 + it { should be_denied_for :visitor }
  187 + end
  188 +
  189 + describe "GET /project_code/repository/branches" do
  190 + subject { branches_project_repository_path(project) }
  191 +
  192 + before do
  193 + # Speed increase
  194 + Project.any_instance.stub(:branches).and_return([])
  195 + end
  196 +
  197 + it { should be_allowed_for master }
  198 + it { should be_allowed_for reporter }
  199 + it { should be_denied_for :admin }
  200 + it { should be_denied_for guest }
  201 + it { should be_denied_for :user }
  202 + it { should be_denied_for :visitor }
  203 + end
  204 +
  205 + describe "GET /project_code/repository/tags" do
  206 + subject { tags_project_repository_path(project) }
  207 +
  208 + before do
  209 + # Speed increase
  210 + Project.any_instance.stub(:tags).and_return([])
  211 + end
  212 +
  213 + it { should be_allowed_for master }
  214 + it { should be_allowed_for reporter }
  215 + it { should be_denied_for :admin }
  216 + it { should be_denied_for guest }
  217 + it { should be_denied_for :user }
  218 + it { should be_denied_for :visitor }
  219 + end
  220 +
  221 + describe "GET /project_code/hooks" do
  222 + subject { project_hooks_path(project) }
  223 +
  224 + it { should be_allowed_for master }
  225 + it { should be_allowed_for reporter }
  226 + it { should be_denied_for :admin }
  227 + it { should be_denied_for guest }
  228 + it { should be_denied_for :user }
  229 + it { should be_denied_for :visitor }
  230 + end
  231 +
  232 + describe "GET /project_code/files" do
  233 + subject { files_project_path(project) }
  234 +
  235 + it { should be_allowed_for master }
  236 + it { should be_allowed_for reporter }
  237 + it { should be_denied_for :admin }
  238 + it { should be_denied_for guest }
  239 + it { should be_denied_for :user }
  240 + it { should be_denied_for :visitor }
  241 + end
  242 + end
  243 +end
spec/features/snippets_spec.rb 0 → 100644
@@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "Snippets" do
  4 + let(:project) { create(:project) }
  5 +
  6 + before do
  7 + login_as :user
  8 + project.team << [@user, :developer]
  9 + end
  10 +
  11 + describe "GET /snippets" do
  12 + before do
  13 + @snippet = create(:snippet,
  14 + author: @user,
  15 + project: project)
  16 +
  17 + visit project_snippets_path(project)
  18 + end
  19 +
  20 + subject { page }
  21 +
  22 + it { should have_content(@snippet.title[0..10]) }
  23 + it { should have_content(@snippet.project.name) }
  24 +
  25 + describe "Destroy" do
  26 + before do
  27 + # admin access to remove snippet
  28 + @user.users_projects.destroy_all
  29 + project.team << [@user, :master]
  30 + visit edit_project_snippet_path(project, @snippet)
  31 + end
  32 +
  33 + it "should remove entry" do
  34 + expect {
  35 + click_link "destroy_snippet_#{@snippet.id}"
  36 + }.to change { Snippet.count }.by(-1)
  37 + end
  38 + end
  39 + end
  40 +
  41 + describe "New snippet" do
  42 + before do
  43 + visit project_snippets_path(project)
  44 + click_link "New Snippet"
  45 + end
  46 +
  47 + it "should open new snippet popup" do
  48 + page.current_path.should == new_project_snippet_path(project)
  49 + end
  50 +
  51 + describe "fill in", js: true do
  52 + before do
  53 + fill_in "snippet_title", with: "login function"
  54 + fill_in "snippet_file_name", with: "test.rb"
  55 + page.execute_script("editor.insert('def login; end');")
  56 + end
  57 +
  58 + it { expect { click_button "Save" }.to change {Snippet.count}.by(1) }
  59 +
  60 + it "should add new snippet to table" do
  61 + click_button "Save"
  62 + page.current_path.should == project_snippet_path(project, Snippet.last)
  63 + page.should have_content "login function"
  64 + page.should have_content "test.rb"
  65 + end
  66 + end
  67 + end
  68 +
  69 + describe "Edit snippet" do
  70 + before do
  71 + @snippet = create(:snippet,
  72 + author: @user,
  73 + project: project)
  74 + visit project_snippet_path(project, @snippet)
  75 + click_link "Edit Snippet"
  76 + end
  77 +
  78 + it "should open edit page" do
  79 + page.current_path.should == edit_project_snippet_path(project, @snippet)
  80 + end
  81 +
  82 + describe "fill in" do
  83 + before do
  84 + fill_in "snippet_title", with: "login function"
  85 + fill_in "snippet_file_name", with: "test.rb"
  86 + end
  87 +
  88 + it { expect { click_button "Save" }.to_not change {Snippet.count} }
  89 +
  90 + it "should update snippet fields" do
  91 + click_button "Save"
  92 +
  93 + page.current_path.should == project_snippet_path(project, @snippet)
  94 + page.should have_content "login function"
  95 + page.should have_content "test.rb"
  96 + end
  97 + end
  98 + end
  99 +end
spec/features/users_spec.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +require 'spec_helper'
  2 +
  3 +describe 'Users' do
  4 + describe "GET /users/sign_up" do
  5 + before do
  6 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
  7 + end
  8 +
  9 + it "should create a new user account" do
  10 + visit new_user_registration_path
  11 + fill_in "user_name", with: "Name Surname"
  12 + fill_in "user_username", with: "Great"
  13 + fill_in "user_email", with: "name@mail.com"
  14 + fill_in "user_password", with: "password1234"
  15 + fill_in "user_password_confirmation", with: "password1234"
  16 + expect { click_button "Sign up" }.to change {User.count}.by(1)
  17 + end
  18 + end
  19 +end
spec/lib/popen_spec.rb 0 → 100644
@@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
  1 +require 'spec_helper'
  2 +
  3 +describe 'Gitlab::Popen', no_db: true do
  4 + let (:path) { Rails.root.join('tmp').to_s }
  5 +
  6 + before do
  7 + @klass = Class.new(Object)
  8 + @klass.send(:include, Gitlab::Popen)
  9 + end
  10 +
  11 + context 'zero status' do
  12 + before do
  13 + @output, @status = @klass.new.popen('ls', path)
  14 + end
  15 +
  16 + it { @status.should be_zero }
  17 + it { @output.should include('cache') }
  18 + end
  19 +
  20 + context 'non-zero status' do
  21 + before do
  22 + @output, @status = @klass.new.popen('cat NOTHING', path)
  23 + end
  24 +
  25 + it { @status.should == 1 }
  26 + it { @output.should include('No such file or directory') }
  27 + end
  28 +end
  29 +
spec/models/merge_request_spec.rb
@@ -32,6 +32,12 @@ describe MergeRequest do @@ -32,6 +32,12 @@ describe MergeRequest do
32 it { should_not allow_mass_assignment_of(:project_id) } 32 it { should_not allow_mass_assignment_of(:project_id) }
33 end 33 end
34 34
  35 + describe "Respond to" do
  36 + it { should respond_to(:unchecked?) }
  37 + it { should respond_to(:can_be_merged?) }
  38 + it { should respond_to(:cannot_be_merged?) }
  39 + end
  40 +
35 describe 'modules' do 41 describe 'modules' do
36 it { should include_module(Issuable) } 42 it { should include_module(Issuable) }
37 end 43 end
spec/models/project_hooks_spec.rb
@@ -1,128 +0,0 @@ @@ -1,128 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Project, "Hooks" do  
4 - let(:project) { create(:project) }  
5 -  
6 - before do  
7 - @key = create(:key, user: project.owner)  
8 - @user = @key.user  
9 - @key_id = @key.identifier  
10 - end  
11 -  
12 - describe "Post Receive Event" do  
13 - it "should create push event" do  
14 - oldrev, newrev, ref = '00000000000000000000000000000000', 'newrev', 'refs/heads/master'  
15 - data = project.post_receive_data(oldrev, newrev, ref, @user)  
16 -  
17 - project.observe_push(data)  
18 - event = Event.last  
19 -  
20 - event.should_not be_nil  
21 - event.project.should == project  
22 - event.action.should == Event::PUSHED  
23 - event.data.should == data  
24 - end  
25 - end  
26 -  
27 - describe "Project hooks" do  
28 - context "with no web hooks" do  
29 - it "raises no errors" do  
30 - lambda {  
31 - project.execute_hooks({})  
32 - }.should_not raise_error  
33 - end  
34 - end  
35 -  
36 - context "with web hooks" do  
37 - before do  
38 - @project_hook = create(:project_hook)  
39 - @project_hook_2 = create(:project_hook)  
40 - project.hooks << [@project_hook, @project_hook_2]  
41 -  
42 - stub_request(:post, @project_hook.url)  
43 - stub_request(:post, @project_hook_2.url)  
44 - end  
45 -  
46 - it "executes multiple web hook" do  
47 - @project_hook.should_receive(:async_execute).once  
48 - @project_hook_2.should_receive(:async_execute).once  
49 -  
50 - project.trigger_post_receive('oldrev', 'newrev', 'refs/heads/master', @user)  
51 - end  
52 - end  
53 -  
54 - context "does not execute web hooks" do  
55 - before do  
56 - @project_hook = create(:project_hook)  
57 - project.hooks << [@project_hook]  
58 - end  
59 -  
60 - it "when pushing a branch for the first time" do  
61 - @project_hook.should_not_receive(:execute)  
62 - project.trigger_post_receive('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user)  
63 - end  
64 -  
65 - it "when pushing tags" do  
66 - @project_hook.should_not_receive(:execute)  
67 - project.trigger_post_receive('oldrev', 'newrev', 'refs/tags/v1.0.0', @user)  
68 - end  
69 - end  
70 -  
71 - context "when pushing new branches" do  
72 -  
73 - end  
74 -  
75 - context "when gathering commit data" do  
76 - before do  
77 - @oldrev, @newrev, @ref = project.repository.fresh_commits(2).last.sha,  
78 - project.repository.fresh_commits(2).first.sha, 'refs/heads/master'  
79 - @commit = project.repository.fresh_commits(2).first  
80 -  
81 - # Fill nil/empty attributes  
82 - project.description = "This is a description"  
83 -  
84 - @data = project.post_receive_data(@oldrev, @newrev, @ref, @user)  
85 - end  
86 -  
87 - subject { @data }  
88 -  
89 - it { should include(before: @oldrev) }  
90 - it { should include(after: @newrev) }  
91 - it { should include(ref: @ref) }  
92 - it { should include(user_id: project.owner.id) }  
93 - it { should include(user_name: project.owner.name) }  
94 -  
95 - context "with repository data" do  
96 - subject { @data[:repository] }  
97 -  
98 - it { should include(name: project.name) }  
99 - it { should include(url: project.url_to_repo) }  
100 - it { should include(description: project.description) }  
101 - it { should include(homepage: project.web_url) }  
102 - end  
103 -  
104 - context "with commits" do  
105 - subject { @data[:commits] }  
106 -  
107 - it { should be_an(Array) }  
108 - it { should have(1).element }  
109 -  
110 - context "the commit" do  
111 - subject { @data[:commits].first }  
112 -  
113 - it { should include(id: @commit.id) }  
114 - it { should include(message: @commit.safe_message) }  
115 - it { should include(timestamp: @commit.date.xmlschema) }  
116 - it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") }  
117 -  
118 - context "with a author" do  
119 - subject { @data[:commits].first[:author] }  
120 -  
121 - it { should include(name: @commit.author_name) }  
122 - it { should include(email: @commit.author_email) }  
123 - end  
124 - end  
125 - end  
126 - end  
127 - end  
128 -end  
spec/models/project_spec.rb
@@ -72,11 +72,8 @@ describe Project do @@ -72,11 +72,8 @@ describe Project do
72 it { should respond_to(:url_to_repo) } 72 it { should respond_to(:url_to_repo) }
73 it { should respond_to(:repo_exists?) } 73 it { should respond_to(:repo_exists?) }
74 it { should respond_to(:satellite) } 74 it { should respond_to(:satellite) }
75 - it { should respond_to(:observe_push) }  
76 it { should respond_to(:update_merge_requests) } 75 it { should respond_to(:update_merge_requests) }
77 it { should respond_to(:execute_hooks) } 76 it { should respond_to(:execute_hooks) }
78 - it { should respond_to(:post_receive_data) }  
79 - it { should respond_to(:trigger_post_receive) }  
80 it { should respond_to(:transfer) } 77 it { should respond_to(:transfer) }
81 it { should respond_to(:name_with_namespace) } 78 it { should respond_to(:name_with_namespace) }
82 it { should respond_to(:namespace_owner) } 79 it { should respond_to(:namespace_owner) }
spec/models/project_team_spec.rb
@@ -10,9 +10,6 @@ describe ProjectTeam do @@ -10,9 +10,6 @@ describe ProjectTeam do
10 it { should respond_to(:masters) } 10 it { should respond_to(:masters) }
11 it { should respond_to(:reporters) } 11 it { should respond_to(:reporters) }
12 it { should respond_to(:guests) } 12 it { should respond_to(:guests) }
13 - it { should respond_to(:repository_writers) }  
14 - it { should respond_to(:repository_masters) }  
15 - it { should respond_to(:repository_readers) }  
16 end 13 end
17 end 14 end
18 15
spec/models/user_spec.rb
@@ -69,28 +69,10 @@ describe User do @@ -69,28 +69,10 @@ describe User do
69 69
70 describe "Respond to" do 70 describe "Respond to" do
71 it { should respond_to(:is_admin?) } 71 it { should respond_to(:is_admin?) }
72 - it { should respond_to(:identifier) }  
73 it { should respond_to(:name) } 72 it { should respond_to(:name) }
74 it { should respond_to(:private_token) } 73 it { should respond_to(:private_token) }
75 end 74 end
76 75
77 - describe '#identifier' do  
78 - it "should return valid identifier" do  
79 - user = build(:user, email: "test@mail.com")  
80 - user.identifier.should == "test_mail_com"  
81 - end  
82 -  
83 - it "should return identifier without + sign" do  
84 - user = build(:user, email: "test+foo@mail.com")  
85 - user.identifier.should == "test_foo_mail_com"  
86 - end  
87 -  
88 - it "should conform to Gitolite's required identifier pattern" do  
89 - user = build(:user, email: "_test@example.com")  
90 - user.identifier.should == 'test_example_com'  
91 - end  
92 - end  
93 -  
94 describe '#generate_password' do 76 describe '#generate_password' do
95 it "should execute callback when force_random_password specified" do 77 it "should execute callback when force_random_password specified" do
96 user = build(:user, force_random_password: true) 78 user = build(:user, force_random_password: true)
spec/requests/admin/admin_hooks_spec.rb
@@ -1,51 +0,0 @@ @@ -1,51 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Admin::Hooks" do  
4 - before do  
5 - @project = create(:project)  
6 - login_as :admin  
7 -  
8 - @system_hook = create(:system_hook)  
9 -  
10 - end  
11 -  
12 - describe "GET /admin/hooks" do  
13 - it "should be ok" do  
14 - visit admin_root_path  
15 - within ".main_menu" do  
16 - click_on "Hooks"  
17 - end  
18 - current_path.should == admin_hooks_path  
19 - end  
20 -  
21 - it "should have hooks list" do  
22 - visit admin_hooks_path  
23 - page.should have_content(@system_hook.url)  
24 - end  
25 - end  
26 -  
27 - describe "New Hook" do  
28 - before do  
29 - @url = Faker::Internet.uri("http")  
30 - visit admin_hooks_path  
31 - fill_in "hook_url", with: @url  
32 - expect { click_button "Add System Hook" }.to change(SystemHook, :count).by(1)  
33 - end  
34 -  
35 - it "should open new hook popup" do  
36 - page.current_path.should == admin_hooks_path  
37 - page.should have_content(@url)  
38 - end  
39 - end  
40 -  
41 - describe "Test" do  
42 - before do  
43 - WebMock.stub_request(:post, @system_hook.url)  
44 - visit admin_hooks_path  
45 - click_link "Test Hook"  
46 - end  
47 -  
48 - it { page.current_path.should == admin_hooks_path }  
49 - end  
50 -  
51 -end  
spec/requests/admin/admin_projects_spec.rb
@@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Admin::Projects" do  
4 - before do  
5 - @project = create(:project)  
6 - login_as :admin  
7 - end  
8 -  
9 - describe "GET /admin/projects" do  
10 - before do  
11 - visit admin_projects_path  
12 - end  
13 -  
14 - it "should be ok" do  
15 - current_path.should == admin_projects_path  
16 - end  
17 -  
18 - it "should have projects list" do  
19 - page.should have_content(@project.name)  
20 - end  
21 - end  
22 -  
23 - describe "GET /admin/projects/:id" do  
24 - before do  
25 - visit admin_projects_path  
26 - click_link "#{@project.name}"  
27 - end  
28 -  
29 - it "should have project info" do  
30 - page.should have_content(@project.path)  
31 - page.should have_content(@project.name)  
32 - end  
33 - end  
34 -  
35 - describe "GET /admin/projects/:id/edit" do  
36 - before do  
37 - visit admin_projects_path  
38 - click_link "edit_project_#{@project.id}"  
39 - end  
40 -  
41 - it "should have project edit page" do  
42 - page.should have_content("Edit project")  
43 - page.should have_button("Save Project")  
44 - end  
45 -  
46 - describe "Update project" do  
47 - before do  
48 - fill_in "project_name", with: "Big Bang"  
49 - click_button "Save Project"  
50 - @project.reload  
51 - end  
52 -  
53 - it "should show page with new data" do  
54 - page.should have_content("Big Bang")  
55 - end  
56 -  
57 - it "should change project entry" do  
58 - @project.name.should == "Big Bang"  
59 - end  
60 - end  
61 - end  
62 -  
63 - describe "Add new team member" do  
64 - before do  
65 - @new_user = create(:user)  
66 - visit admin_project_path(@project)  
67 - end  
68 -  
69 - it "should create new user" do  
70 - select @new_user.name, from: "user_ids"  
71 - expect { click_button "Add" }.to change { UsersProject.count }.by(1)  
72 - page.should have_content @new_user.name  
73 - current_path.should == admin_project_path(@project)  
74 - end  
75 - end  
76 -end  
spec/requests/admin/admin_users_spec.rb
@@ -1,135 +0,0 @@ @@ -1,135 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Admin::Users" do  
4 - before { login_as :admin }  
5 -  
6 - describe "GET /admin/users" do  
7 - before do  
8 - visit admin_users_path  
9 - end  
10 -  
11 - it "should be ok" do  
12 - current_path.should == admin_users_path  
13 - end  
14 -  
15 - it "should have users list" do  
16 - page.should have_content(@user.email)  
17 - page.should have_content(@user.name)  
18 - end  
19 - end  
20 -  
21 - describe "GET /admin/users/new" do  
22 - before do  
23 - @password = "123ABC"  
24 - visit new_admin_user_path  
25 - fill_in "user_name", with: "Big Bang"  
26 - fill_in "user_username", with: "bang"  
27 - fill_in "user_email", with: "bigbang@mail.com"  
28 - fill_in "user_password", with: @password  
29 - fill_in "user_password_confirmation", with: @password  
30 - end  
31 -  
32 - it "should create new user" do  
33 - expect { click_button "Save" }.to change {User.count}.by(1)  
34 - end  
35 -  
36 - it "should create user with valid data" do  
37 - click_button "Save"  
38 - user = User.last  
39 - user.name.should == "Big Bang"  
40 - user.email.should == "bigbang@mail.com"  
41 - end  
42 -  
43 - it "should call send mail" do  
44 - Notify.should_receive(:new_user_email)  
45 -  
46 - User.observers.enable :user_observer do  
47 - click_button "Save"  
48 - end  
49 - end  
50 -  
51 - it "should send valid email to user with email & password" do  
52 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)  
53 - User.observers.enable :user_observer do  
54 - click_button "Save"  
55 - user = User.last  
56 - email = ActionMailer::Base.deliveries.last  
57 - email.subject.should have_content("Account was created")  
58 - email.body.should have_content(user.email)  
59 - email.body.should have_content(@password)  
60 - end  
61 - end  
62 -  
63 - it "should send valid email to user with email without password when signup is enabled" do  
64 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)  
65 - User.observers.enable :user_observer do  
66 - click_button "Save"  
67 - user = User.last  
68 - email = ActionMailer::Base.deliveries.last  
69 - email.subject.should have_content("Account was created")  
70 - email.body.should have_content(user.email)  
71 - email.body.should_not have_content(@password)  
72 - end  
73 - end  
74 - end  
75 -  
76 - describe "GET /admin/users/:id" do  
77 - before do  
78 - visit admin_users_path  
79 - click_link "#{@user.name}"  
80 - end  
81 -  
82 - it "should have user info" do  
83 - page.should have_content(@user.email)  
84 - page.should have_content(@user.name)  
85 - page.should have_content(@user.projects_limit)  
86 - end  
87 - end  
88 -  
89 - describe "GET /admin/users/:id/edit" do  
90 - before do  
91 - @simple_user = create(:user)  
92 - visit admin_users_path  
93 - click_link "edit_user_#{@simple_user.id}"  
94 - end  
95 -  
96 - it "should have user edit page" do  
97 - page.should have_content("Name")  
98 - page.should have_content("Password")  
99 - end  
100 -  
101 - describe "Update user" do  
102 - before do  
103 - fill_in "user_name", with: "Big Bang"  
104 - fill_in "user_email", with: "bigbang@mail.com"  
105 - check "user_admin"  
106 - click_button "Save"  
107 - end  
108 -  
109 - it "should show page with new data" do  
110 - page.should have_content("bigbang@mail.com")  
111 - page.should have_content("Big Bang")  
112 - end  
113 -  
114 - it "should change user entry" do  
115 - @simple_user.reload  
116 - @simple_user.name.should == "Big Bang"  
117 - @simple_user.is_admin?.should be_true  
118 - end  
119 - end  
120 - end  
121 -  
122 - describe "Add new project" do  
123 - before do  
124 - @new_project = create(:project)  
125 - visit admin_user_path(@user)  
126 - end  
127 -  
128 - it "should create new user" do  
129 - select @new_project.name, from: "project_ids"  
130 - expect { click_button "Add" }.to change { UsersProject.count }.by(1)  
131 - page.should have_content @new_project.name  
132 - current_path.should == admin_user_path(@user)  
133 - end  
134 - end  
135 -end  
spec/requests/admin/security_spec.rb
@@ -1,27 +0,0 @@ @@ -1,27 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Admin::Projects" do  
4 - describe "GET /admin/projects" do  
5 - subject { admin_projects_path }  
6 -  
7 - it { should be_allowed_for :admin }  
8 - it { should be_denied_for :user }  
9 - it { should be_denied_for :visitor }  
10 - end  
11 -  
12 - describe "GET /admin/users" do  
13 - subject { admin_users_path }  
14 -  
15 - it { should be_allowed_for :admin }  
16 - it { should be_denied_for :user }  
17 - it { should be_denied_for :visitor }  
18 - end  
19 -  
20 - describe "GET /admin/hooks" do  
21 - subject { admin_hooks_path }  
22 -  
23 - it { should be_allowed_for :admin }  
24 - it { should be_denied_for :user }  
25 - it { should be_denied_for :visitor }  
26 - end  
27 -end  
spec/requests/api/internal_spec.rb 0 → 100644
@@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::API do
  4 + include ApiHelpers
  5 +
  6 + let(:user) { create(:user) }
  7 + let(:key) { create(:key, user: user) }
  8 + let(:project) { create(:project) }
  9 +
  10 + describe "GET /internal/check", no_db: true do
  11 + it do
  12 + get api("/internal/check")
  13 +
  14 + response.status.should == 200
  15 + json_response['api_version'].should == Gitlab::API.version
  16 + end
  17 + end
  18 +
  19 + describe "GET /internal/discover" do
  20 + it do
  21 + get(api("/internal/discover"), key_id: key.id)
  22 +
  23 + response.status.should == 200
  24 +
  25 + json_response['email'].should == user.email
  26 + end
  27 + end
  28 +
  29 + describe "GET /internal/allowed" do
  30 + context "access granted" do
  31 + before do
  32 + project.team << [user, :developer]
  33 + end
  34 +
  35 + context "git pull" do
  36 + it do
  37 + get(
  38 + api("/internal/allowed"),
  39 + ref: 'master',
  40 + key_id: key.id,
  41 + project: project.path_with_namespace,
  42 + action: 'git-upload-pack'
  43 + )
  44 +
  45 + response.status.should == 200
  46 + response.body.should == 'true'
  47 + end
  48 + end
  49 +
  50 + context "git push" do
  51 + it do
  52 + get(
  53 + api("/internal/allowed"),
  54 + ref: 'master',
  55 + key_id: key.id,
  56 + project: project.path_with_namespace,
  57 + action: 'git-receive-pack'
  58 + )
  59 +
  60 + response.status.should == 200
  61 + response.body.should == 'true'
  62 + end
  63 + end
  64 + end
  65 +
  66 + context "access denied" do
  67 + before do
  68 + project.team << [user, :guest]
  69 + end
  70 +
  71 + context "git pull" do
  72 + it do
  73 + get(
  74 + api("/internal/allowed"),
  75 + ref: 'master',
  76 + key_id: key.id,
  77 + project: project.path_with_namespace,
  78 + action: 'git-upload-pack'
  79 + )
  80 +
  81 + response.status.should == 200
  82 + response.body.should == 'false'
  83 + end
  84 + end
  85 +
  86 + context "git push" do
  87 + it do
  88 + get(
  89 + api("/internal/allowed"),
  90 + ref: 'master',
  91 + key_id: key.id,
  92 + project: project.path_with_namespace,
  93 + action: 'git-receive-pack'
  94 + )
  95 +
  96 + response.status.should == 200
  97 + response.body.should == 'false'
  98 + end
  99 + end
  100 + end
  101 +
  102 + end
  103 +end
spec/requests/api/users_spec.rb
@@ -97,32 +97,27 @@ describe Gitlab::API do @@ -97,32 +97,27 @@ describe Gitlab::API do
97 end 97 end
98 98
99 describe "GET /users/sign_up" do 99 describe "GET /users/sign_up" do
100 - before do  
101 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)  
102 - end  
103 - it "should redirect to sign in page if signup is disabled" do  
104 - get "/users/sign_up"  
105 - response.status.should == 302  
106 - response.should redirect_to(new_user_session_path)  
107 - end  
108 - end 100 + context 'enabled' do
  101 + before do
  102 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
  103 + end
109 104
110 - describe "GET /users/sign_up" do  
111 - before do  
112 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)  
113 - end  
114 - it "should return sign up page if signup is enabled" do  
115 - get "/users/sign_up"  
116 - response.status.should == 200 105 + it "should return sign up page if signup is enabled" do
  106 + get "/users/sign_up"
  107 + response.status.should == 200
  108 + end
117 end 109 end
118 - it "should create a new user account" do  
119 - visit new_user_registration_path  
120 - fill_in "user_name", with: "Name Surname"  
121 - fill_in "user_username", with: "Great"  
122 - fill_in "user_email", with: "name@mail.com"  
123 - fill_in "user_password", with: "password1234"  
124 - fill_in "user_password_confirmation", with: "password1234"  
125 - expect { click_button "Sign up" }.to change {User.count}.by(1) 110 +
  111 + context 'disabled' do
  112 + before do
  113 + Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)
  114 + end
  115 +
  116 + it "should redirect to sign in page if signup is disabled" do
  117 + get "/users/sign_up"
  118 + response.status.should == 302
  119 + response.should redirect_to(new_user_session_path)
  120 + end
126 end 121 end
127 end 122 end
128 123
spec/requests/atom/dashboard_issues_spec.rb
@@ -1,24 +0,0 @@ @@ -1,24 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Dashboard Issues Feed" do  
4 - describe "GET /issues" do  
5 - let!(:user) { create(:user) }  
6 - let!(:project1) { create(:project) }  
7 - let!(:project2) { create(:project) }  
8 - let!(:issue1) { create(:issue, author: user, assignee: user, project: project1) }  
9 - let!(:issue2) { create(:issue, author: user, assignee: user, project: project2) }  
10 -  
11 - describe "atom feed" do  
12 - it "should render atom feed via private token" do  
13 - visit issues_dashboard_path(:atom, private_token: user.private_token)  
14 -  
15 - page.response_headers['Content-Type'].should have_content("application/atom+xml")  
16 - page.body.should have_selector("title", text: "#{user.name} issues")  
17 - page.body.should have_selector("author email", text: issue1.author_email)  
18 - page.body.should have_selector("entry summary", text: issue1.title)  
19 - page.body.should have_selector("author email", text: issue2.author_email)  
20 - page.body.should have_selector("entry summary", text: issue2.title)  
21 - end  
22 - end  
23 - end  
24 -end  
spec/requests/atom/dashboard_spec.rb
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Dashboard Feed" do  
4 - describe "GET /" do  
5 - let!(:user) { create(:user) }  
6 -  
7 - context "projects atom feed via private token" do  
8 - it "should render projects atom feed" do  
9 - visit dashboard_path(:atom, private_token: user.private_token)  
10 - page.body.should have_selector("feed title")  
11 - end  
12 - end  
13 - end  
14 -end  
spec/requests/atom/issues_spec.rb
@@ -1,34 +0,0 @@ @@ -1,34 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Issues Feed" do  
4 - describe "GET /issues" do  
5 - let!(:user) { create(:user) }  
6 - let!(:project) { create(:project, namespace: user.namespace) }  
7 - let!(:issue) { create(:issue, author: user, project: project) }  
8 -  
9 - before { project.team << [user, :developer] }  
10 -  
11 - context "when authenticated" do  
12 - it "should render atom feed" do  
13 - login_with user  
14 - visit project_issues_path(project, :atom)  
15 -  
16 - page.response_headers['Content-Type'].should have_content("application/atom+xml")  
17 - page.body.should have_selector("title", text: "#{project.name} issues")  
18 - page.body.should have_selector("author email", text: issue.author_email)  
19 - page.body.should have_selector("entry summary", text: issue.title)  
20 - end  
21 - end  
22 -  
23 - context "when authenticated via private token" do  
24 - it "should render atom feed" do  
25 - visit project_issues_path(project, :atom, private_token: user.private_token)  
26 -  
27 - page.response_headers['Content-Type'].should have_content("application/atom+xml")  
28 - page.body.should have_selector("title", text: "#{project.name} issues")  
29 - page.body.should have_selector("author email", text: issue.author_email)  
30 - page.body.should have_selector("entry summary", text: issue.title)  
31 - end  
32 - end  
33 - end  
34 -end  
spec/requests/gitlab_flavored_markdown_spec.rb
@@ -1,223 +0,0 @@ @@ -1,223 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Gitlab Flavored Markdown" do  
4 - let(:project) { create(:project) }  
5 - let(:issue) { create(:issue, project: project) }  
6 - let(:merge_request) { create(:merge_request, project: project) }  
7 - let(:fred) do  
8 - u = create(:user, name: "fred")  
9 - project.team << [u, :master]  
10 - u  
11 - end  
12 -  
13 - before do  
14 - # add test branch  
15 - @branch_name = "gfm-test"  
16 - r = project.repo  
17 - i = r.index  
18 - # add test file  
19 - @test_file = "gfm_test_file"  
20 - i.add(@test_file, "foo\nbar\n")  
21 - # add commit with gfm  
22 - i.commit("fix ##{issue.id}\n\nask @#{fred.username} for details", head: @branch_name)  
23 -  
24 - # add test tag  
25 - @tag_name = "gfm-test-tag"  
26 - r.git.native(:tag, {}, @tag_name, commit.id)  
27 - end  
28 -  
29 - after do  
30 - # delete test branch and tag  
31 - project.repo.git.native(:branch, {D: true}, @branch_name)  
32 - project.repo.git.native(:tag, {d: true}, @tag_name)  
33 - project.repo.gc_auto  
34 - end  
35 -  
36 - let(:commit) { project.repository.commits(@branch_name).first }  
37 -  
38 - before do  
39 - login_as :user  
40 - project.team << [@user, :developer]  
41 - end  
42 -  
43 - describe "for commits" do  
44 - it "should render title in commits#index" do  
45 - visit project_commits_path(project, @branch_name, limit: 1)  
46 -  
47 - page.should have_link("##{issue.id}")  
48 - end  
49 -  
50 - it "should render title in commits#show" do  
51 - visit project_commit_path(project, commit)  
52 -  
53 - page.should have_link("##{issue.id}")  
54 - end  
55 -  
56 - it "should render description in commits#show" do  
57 - visit project_commit_path(project, commit)  
58 -  
59 - page.should have_link("@#{fred.username}")  
60 - end  
61 -  
62 - it "should render title in refs#tree", js: true do  
63 - visit project_tree_path(project, @branch_name)  
64 -  
65 - within(".tree_commit") do  
66 - page.should have_link("##{issue.id}")  
67 - end  
68 - end  
69 -  
70 - # @wip  
71 - #it "should render title in refs#blame" do  
72 - #visit project_blame_path(project, File.join(@branch_name, @test_file))  
73 -  
74 - #within(".blame_commit") do  
75 - #page.should have_link("##{issue.id}")  
76 - #end  
77 - #end  
78 -  
79 - it "should render title in repositories#branches" do  
80 - visit branches_project_repository_path(project)  
81 -  
82 - page.should have_link("##{issue.id}")  
83 - end  
84 - end  
85 -  
86 - describe "for issues" do  
87 - before do  
88 - @other_issue = create(:issue,  
89 - author: @user,  
90 - assignee: @user,  
91 - project: project)  
92 - @issue = create(:issue,  
93 - author: @user,  
94 - assignee: @user,  
95 - project: project,  
96 - title: "fix ##{@other_issue.id}",  
97 - description: "ask @#{fred.username} for details")  
98 - end  
99 -  
100 - it "should render subject in issues#index" do  
101 - visit project_issues_path(project)  
102 -  
103 - page.should have_link("##{@other_issue.id}")  
104 - end  
105 -  
106 - it "should render subject in issues#show" do  
107 - visit project_issue_path(project, @issue)  
108 -  
109 - page.should have_link("##{@other_issue.id}")  
110 - end  
111 -  
112 - it "should render details in issues#show" do  
113 - visit project_issue_path(project, @issue)  
114 -  
115 - page.should have_link("@#{fred.username}")  
116 - end  
117 - end  
118 -  
119 -  
120 - describe "for merge requests" do  
121 - before do  
122 - @merge_request = create(:merge_request,  
123 - project: project,  
124 - title: "fix ##{issue.id}")  
125 - end  
126 -  
127 - it "should render title in merge_requests#index" do  
128 - visit project_merge_requests_path(project)  
129 -  
130 - page.should have_link("##{issue.id}")  
131 - end  
132 -  
133 - it "should render title in merge_requests#show" do  
134 - visit project_merge_request_path(project, @merge_request)  
135 -  
136 - page.should have_link("##{issue.id}")  
137 - end  
138 - end  
139 -  
140 -  
141 - describe "for milestones" do  
142 - before do  
143 - @milestone = create(:milestone,  
144 - project: project,  
145 - title: "fix ##{issue.id}",  
146 - description: "ask @#{fred.username} for details")  
147 - end  
148 -  
149 - it "should render title in milestones#index" do  
150 - visit project_milestones_path(project)  
151 -  
152 - page.should have_link("##{issue.id}")  
153 - end  
154 -  
155 - it "should render title in milestones#show" do  
156 - visit project_milestone_path(project, @milestone)  
157 -  
158 - page.should have_link("##{issue.id}")  
159 - end  
160 -  
161 - it "should render description in milestones#show" do  
162 - visit project_milestone_path(project, @milestone)  
163 -  
164 - page.should have_link("@#{fred.username}")  
165 - end  
166 - end  
167 -  
168 -  
169 - describe "for notes" do  
170 - it "should render in commits#show", js: true do  
171 - visit project_commit_path(project, commit)  
172 - fill_in "note_note", with: "see ##{issue.id}"  
173 - click_button "Add Comment"  
174 -  
175 - page.should have_link("##{issue.id}")  
176 - end  
177 -  
178 - it "should render in issue#show", js: true do  
179 - visit project_issue_path(project, issue)  
180 - fill_in "note_note", with: "see ##{issue.id}"  
181 - click_button "Add Comment"  
182 -  
183 - page.should have_link("##{issue.id}")  
184 - end  
185 -  
186 - it "should render in merge_request#show", js: true do  
187 - visit project_merge_request_path(project, merge_request)  
188 - fill_in "note_note", with: "see ##{issue.id}"  
189 - click_button "Add Comment"  
190 -  
191 - page.should have_link("##{issue.id}")  
192 - end  
193 -  
194 - it "should render in projects#wall", js: true do  
195 - visit wall_project_path(project)  
196 - fill_in "note_note", with: "see ##{issue.id}"  
197 - click_button "Add Comment"  
198 -  
199 - page.should have_link("##{issue.id}")  
200 - end  
201 - end  
202 -  
203 -  
204 - describe "for wikis" do  
205 - before do  
206 - visit project_wiki_path(project, :index)  
207 - fill_in "Title", with: "Circumvent ##{issue.id}"  
208 - fill_in "Content", with: "# Other pages\n\n* [Foo](foo)\n* [Bar](bar)\n\nAlso look at ##{issue.id} :-)"  
209 - click_on "Save"  
210 - end  
211 -  
212 - it "should NOT render title in wikis#show" do  
213 - within(".content h3") do # page title  
214 - page.should have_content("Circumvent ##{issue.id}")  
215 - page.should_not have_link("##{issue.id}")  
216 - end  
217 - end  
218 -  
219 - it "should render content in wikis#show" do  
220 - page.should have_link("##{issue.id}")  
221 - end  
222 - end  
223 -end  
spec/requests/issues_spec.rb
@@ -1,132 +0,0 @@ @@ -1,132 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "Issues" do  
4 - let(:project) { create(:project) }  
5 -  
6 - before do  
7 - login_as :user  
8 - user2 = create(:user)  
9 -  
10 - project.team << [[@user, user2], :developer]  
11 - end  
12 -  
13 - describe "Edit issue" do  
14 - let!(:issue) do  
15 - create(:issue,  
16 - author: @user,  
17 - assignee: @user,  
18 - project: project)  
19 - end  
20 -  
21 - before do  
22 - visit project_issues_path(project)  
23 - click_link "Edit"  
24 - end  
25 -  
26 - it "should open new issue popup" do  
27 - page.should have_content("Issue ##{issue.id}")  
28 - end  
29 -  
30 - describe "fill in" do  
31 - before do  
32 - fill_in "issue_title", with: "bug 345"  
33 - fill_in "issue_description", with: "bug description"  
34 - end  
35 -  
36 - it { expect { click_button "Save changes" }.to_not change {Issue.count} }  
37 -  
38 - it "should update issue fields" do  
39 - click_button "Save changes"  
40 -  
41 - page.should have_content @user.name  
42 - page.should have_content "bug 345"  
43 - page.should have_content project.name  
44 - end  
45 - end  
46 - end  
47 -  
48 - describe "Search issue", js: true do  
49 - before do  
50 - ['foobar', 'foobar2', 'gitlab'].each do |title|  
51 - create(:issue,  
52 - author: @user,  
53 - assignee: @user,  
54 - project: project,  
55 - title: title)  
56 - end  
57 - end  
58 -  
59 - it "should be able to search on different statuses" do  
60 - issue = Issue.first # with title 'foobar'  
61 - issue.close  
62 -  
63 - visit project_issues_path(project)  
64 - click_link 'Closed'  
65 - fill_in 'issue_search', with: 'foobar'  
66 -  
67 - page.should have_content 'foobar'  
68 - page.should_not have_content 'foobar2'  
69 - page.should_not have_content 'gitlab'  
70 - end  
71 -  
72 - it "should search for term and return the correct results" do  
73 - visit project_issues_path(project)  
74 - fill_in 'issue_search', with: 'foobar'  
75 -  
76 - page.should have_content 'foobar'  
77 - page.should have_content 'foobar2'  
78 - page.should_not have_content 'gitlab'  
79 - end  
80 - end  
81 -  
82 - describe "Filter issue" do  
83 - before do  
84 - ['foobar', 'barbaz', 'gitlab'].each do |title|  
85 - create(:issue,  
86 - author: @user,  
87 - assignee: @user,  
88 - project: project,  
89 - title: title)  
90 - end  
91 -  
92 - @issue = Issue.first # with title 'foobar'  
93 - @issue.milestone = create(:milestone, project: project)  
94 - @issue.assignee = nil  
95 - @issue.save  
96 - end  
97 -  
98 - let(:issue) { @issue }  
99 -  
100 - it "should allow filtering by issues with no specified milestone" do  
101 - visit project_issues_path(project, milestone_id: '0')  
102 -  
103 - page.should_not have_content 'foobar'  
104 - page.should have_content 'barbaz'  
105 - page.should have_content 'gitlab'  
106 - end  
107 -  
108 - it "should allow filtering by a specified milestone" do  
109 - visit project_issues_path(project, milestone_id: issue.milestone.id)  
110 -  
111 - page.should have_content 'foobar'  
112 - page.should_not have_content 'barbaz'  
113 - page.should_not have_content 'gitlab'  
114 - end  
115 -  
116 - it "should allow filtering by issues with no specified assignee" do  
117 - visit project_issues_path(project, assignee_id: '0')  
118 -  
119 - page.should have_content 'foobar'  
120 - page.should_not have_content 'barbaz'  
121 - page.should_not have_content 'gitlab'  
122 - end  
123 -  
124 - it "should allow filtering by a specified assignee" do  
125 - visit project_issues_path(project, assignee_id: @user.id)  
126 -  
127 - page.should_not have_content 'foobar'  
128 - page.should have_content 'barbaz'  
129 - page.should have_content 'gitlab'  
130 - end  
131 - end  
132 -end  
spec/requests/notes_on_merge_requests_spec.rb
@@ -1,232 +0,0 @@ @@ -1,232 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "On a merge request", js: true do  
4 - let!(:project) { create(:project) }  
5 - let!(:merge_request) { create(:merge_request, project: project) }  
6 -  
7 - before do  
8 - login_as :user  
9 - project.team << [@user, :master]  
10 -  
11 - visit project_merge_request_path(project, merge_request)  
12 - end  
13 -  
14 - subject { page }  
15 -  
16 - describe "the note form" do  
17 - # main target form creation  
18 - it { should have_css(".js-main-target-form", visible: true, count: 1) }  
19 -  
20 - # button initalization  
21 - it { within(".js-main-target-form") { should have_button("Add Comment") } }  
22 - it { within(".js-main-target-form") { should_not have_link("Cancel") } }  
23 -  
24 - # notifiactions  
25 - it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } }  
26 - it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } }  
27 - it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } }  
28 -  
29 - describe "without text" do  
30 - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }  
31 - end  
32 -  
33 - describe "with text" do  
34 - before do  
35 - within(".js-main-target-form") do  
36 - fill_in "note[note]", with: "This is awesome"  
37 - end  
38 - end  
39 -  
40 - it { within(".js-main-target-form") { should_not have_css(".js-comment-button[disabled]") } }  
41 -  
42 - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: true) } }  
43 - end  
44 -  
45 - describe "with preview" do  
46 - before do  
47 - within(".js-main-target-form") do  
48 - fill_in "note[note]", with: "This is awesome"  
49 - find(".js-note-preview-button").trigger("click")  
50 - end  
51 - end  
52 -  
53 - it { within(".js-main-target-form") { should have_css(".js-note-preview", text: "This is awesome", visible: true) } }  
54 -  
55 - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }  
56 - it { within(".js-main-target-form") { should have_css(".js-note-edit-button", visible: true) } }  
57 - end  
58 - end  
59 -  
60 - describe "when posting a note" do  
61 - before do  
62 - within(".js-main-target-form") do  
63 - fill_in "note[note]", with: "This is awsome!"  
64 - find(".js-note-preview-button").trigger("click")  
65 - click_button "Add Comment"  
66 - end  
67 - end  
68 -  
69 - # note added  
70 - it { within(".js-main-target-form") { should have_content("This is awsome!") } }  
71 -  
72 - # reset form  
73 - it { within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } }  
74 -  
75 - # return from preview  
76 - it { within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) } }  
77 - it { within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } }  
78 -  
79 -  
80 - it "should be removable" do  
81 - find(".js-note-delete").trigger("click")  
82 -  
83 - should_not have_css(".note")  
84 - end  
85 - end  
86 -end  
87 -  
88 -  
89 -  
90 -describe "On a merge request diff", js: true, focus: true do  
91 - let!(:project) { create(:project) }  
92 - let!(:merge_request) { create(:merge_request_with_diffs, project: project) }  
93 -  
94 - before do  
95 - login_as :user  
96 - project.team << [@user, :master]  
97 -  
98 - visit diffs_project_merge_request_path(project, merge_request)  
99 -  
100 - click_link("Diff")  
101 - end  
102 -  
103 - subject { page }  
104 -  
105 - describe "when adding a note" do  
106 - before do  
107 - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click")  
108 - end  
109 -  
110 - describe "the notes holder" do  
111 - it { should have_css("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") }  
112 -  
113 - it { within(".js-temp-notes-holder") { should have_css(".new_note") } }  
114 - end  
115 -  
116 - describe "the note form" do  
117 - # set up hidden fields correctly  
118 - it { within(".js-temp-notes-holder") { find("#note_noteable_type").value.should == "MergeRequest" } }  
119 - it { within(".js-temp-notes-holder") { find("#note_noteable_id").value.should == merge_request.id.to_s } }  
120 - it { within(".js-temp-notes-holder") { find("#note_commit_id").value.should == "" } }  
121 - it { within(".js-temp-notes-holder") { find("#note_line_code").value.should == "4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185" } }  
122 -  
123 - # buttons  
124 - it { should have_button("Add Comment") }  
125 - it { should have_css(".js-close-discussion-note-form", text: "Cancel") }  
126 -  
127 - # notification options  
128 - it { should have_checked_field("Notify team via email") }  
129 -  
130 - it "shouldn't add a second form for same row" do  
131 - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click")  
132 -  
133 - should have_css("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder form", count: 1)  
134 - end  
135 -  
136 - it "should be removed when canceled" do  
137 - find(".js-close-discussion-note-form").trigger("click")  
138 -  
139 - should have_no_css(".js-temp-notes-holder")  
140 - end  
141 - end  
142 - end  
143 -  
144 - describe "with muliple note forms" do  
145 - before do  
146 - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder .js-add-diff-note-button").trigger("click")  
147 - find("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder .js-add-diff-note-button").trigger("click")  
148 - end  
149 -  
150 - # has two line forms  
151 - it { should have_css(".js-temp-notes-holder", count: 2) }  
152 -  
153 - describe "previewing them separately" do  
154 - before do  
155 - # add two separate texts and trigger previews on both  
156 - within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") do  
157 - fill_in "note[note]", with: "One comment on line 185"  
158 - find(".js-note-preview-button").trigger("click")  
159 - end  
160 - within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") do  
161 - fill_in "note[note]", with: "Another comment on line 17"  
162 - find(".js-note-preview-button").trigger("click")  
163 - end  
164 - end  
165 -  
166 - # check if previews were rendered separately  
167 - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185.line_holder + .js-temp-notes-holder") { should have_css(".js-note-preview", text: "One comment on line 185") } }  
168 - it { within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") { should have_css(".js-note-preview", text: "Another comment on line 17") } }  
169 - end  
170 -  
171 - describe "posting a note" do  
172 - before do  
173 - within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") do  
174 - fill_in "note[note]", with: "Another comment on line 17"  
175 - click_button("Add Comment")  
176 - end  
177 - end  
178 -  
179 - # removed form after submit  
180 - it { should have_no_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .js-temp-notes-holder") }  
181 -  
182 - # added discussion  
183 - it { should have_content("Another comment on line 17") }  
184 - it { should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder") }  
185 - it { should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder .note", count: 1) }  
186 - it { should have_link("Reply") }  
187 -  
188 - it "should remove last note of a discussion" do  
189 - within("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + .notes_holder") do  
190 - find(".js-note-delete").trigger("click")  
191 - end  
192 -  
193 - # removed whole discussion  
194 - should_not have_css(".note_holder")  
195 - should have_css("#342e16cbbd482ac2047dc679b2749d248cc1428f_18_17.line_holder + #342e16cbbd482ac2047dc679b2749d248cc1428f_18_18.line_holder")  
196 - end  
197 - end  
198 - end  
199 -  
200 - describe "when replying to a note" do  
201 - before do  
202 - # create first note  
203 - find("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder .js-add-diff-note-button").trigger("click")  
204 - within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .js-temp-notes-holder") do  
205 - fill_in "note[note]", with: "One comment on line 184"  
206 - click_button("Add Comment")  
207 - end  
208 - # create second note  
209 - within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") do  
210 - find(".js-discussion-reply-button").trigger("click")  
211 - fill_in "note[note]", with: "An additional comment in reply"  
212 - click_button("Add Comment")  
213 - end  
214 - end  
215 -  
216 - # inserted note  
217 - it { should have_content("An additional comment in reply") }  
218 - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_css(".note", count: 2) } }  
219 -  
220 - # removed form after reply  
221 - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_no_css("form") } }  
222 - it { within("#4735dfc552ad7bf15ca468adc3cad9d05b624490_184_184.line_holder + .notes_holder") { should have_link("Reply") } }  
223 - end  
224 -end  
225 -  
226 -  
227 -  
228 -describe "On merge request discussion", js: true do  
229 - describe "with merge request diff note"  
230 - describe "with commit note"  
231 - describe "with commit diff note"  
232 -end  
spec/requests/notes_on_wall_spec.rb
@@ -1,85 +0,0 @@ @@ -1,85 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe "On the project wall", js: true do  
4 - let!(:project) { create(:project) }  
5 - let!(:commit) { project.repository.commit("bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a") }  
6 -  
7 - before do  
8 - login_as :user  
9 - project.team << [@user, :master]  
10 - visit wall_project_path(project)  
11 - end  
12 -  
13 - subject { page }  
14 -  
15 - describe "the note form" do  
16 - # main target form creation  
17 - it { should have_css(".js-main-target-form", visible: true, count: 1) }  
18 -  
19 - # button initalization  
20 - it { within(".js-main-target-form") { should have_button("Add Comment") } }  
21 - it { within(".js-main-target-form") { should_not have_link("Cancel") } }  
22 -  
23 - # notifiactions  
24 - it { within(".js-main-target-form") { should have_checked_field("Notify team via email") } }  
25 - it { within(".js-main-target-form") { should_not have_checked_field("Notify commit author") } }  
26 - it { within(".js-main-target-form") { should_not have_unchecked_field("Notify commit author") } }  
27 -  
28 - describe "without text" do  
29 - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }  
30 - end  
31 -  
32 - describe "with text" do  
33 - before do  
34 - within(".js-main-target-form") do  
35 - fill_in "note[note]", with: "This is awesome"  
36 - end  
37 - end  
38 -  
39 - it { within(".js-main-target-form") { should_not have_css(".js-comment-button[disabled]") } }  
40 -  
41 - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: true) } }  
42 - end  
43 -  
44 - describe "with preview" do  
45 - before do  
46 - within(".js-main-target-form") do  
47 - fill_in "note[note]", with: "This is awesome"  
48 - find(".js-note-preview-button").trigger("click")  
49 - end  
50 - end  
51 -  
52 - it { within(".js-main-target-form") { should have_css(".js-note-preview", text: "This is awesome", visible: true) } }  
53 -  
54 - it { within(".js-main-target-form") { should have_css(".js-note-preview-button", visible: false) } }  
55 - it { within(".js-main-target-form") { should have_css(".js-note-edit-button", visible: true) } }  
56 - end  
57 - end  
58 -  
59 - describe "when posting a note" do  
60 - before do  
61 - within(".js-main-target-form") do  
62 - fill_in "note[note]", with: "This is awsome!"  
63 - find(".js-note-preview-button").trigger("click")  
64 - click_button "Add Comment"  
65 - end  
66 - end  
67 -  
68 - # note added  
69 - it { within(".js-main-target-form") { should have_content("This is awsome!") } }  
70 -  
71 - # reset form  
72 - it { within(".js-main-target-form") { should have_no_field("note[note]", with: "This is awesome!") } }  
73 -  
74 - # return from preview  
75 - it { within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) } }  
76 - it { within(".js-main-target-form") { should have_css(".js-note-text", visible: true) } }  
77 -  
78 -  
79 - it "should be removable" do  
80 - find(".js-note-delete").trigger("click")  
81 -  
82 - should_not have_css(".note")  
83 - end  
84 - end  
85 -end