Commit ac4a09e9cca9018dbb8e52446078b91a4b87e410
Exists in
master
and in
4 other branches
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.
.travis.yml
CHANGELOG
| 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 |
Gemfile
| @@ -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 |
Gemfile.lock
| 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 < ProjectResourceController | @@ -73,7 +75,7 @@ class MergeRequestsController < 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 < ApplicationController | @@ -12,7 +14,7 @@ class ProfilesController < 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 < ApplicationController | @@ -65,4 +67,17 @@ class ProfilesController < 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 < ActiveRecord::Base | @@ -21,7 +21,6 @@ class Key < 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 < ActiveRecord::Base | @@ -48,14 +47,6 @@ class Key < 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("lib/static_model") | @@ -24,6 +24,8 @@ require Rails.root.join("lib/static_model") | ||
| 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 < ActiveRecord::Base | @@ -51,47 +53,41 @@ class MergeRequest < 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 < ActiveRecord::Base | @@ -104,26 +100,12 @@ class MergeRequest < 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 < ActiveRecord::Base | @@ -178,11 +160,6 @@ class MergeRequest < 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 < ActiveRecord::Base | @@ -217,7 +194,7 @@ class MergeRequest < 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 < ActiveRecord::Base | @@ -247,32 +247,6 @@ class Project < 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 < ActiveRecord::Base | @@ -295,51 +269,8 @@ class Project < 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 < ActiveRecord::Base | @@ -354,68 +285,12 @@ class Project < 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 < ActiveRecord::Base | @@ -424,7 +299,7 @@ class Project < 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 < ActiveRecord::Base | @@ -446,6 +321,10 @@ class Project < 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 < ActiveRecord::Base | @@ -70,6 +70,7 @@ class User < 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 < ActiveRecord::Base | @@ -215,17 +216,6 @@ class User < 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 < ActiveRecord::Base | @@ -28,10 +28,14 @@ class WebHook < 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 < ActiveRecord::Observer | @@ -21,22 +21,22 @@ class ActivityObserver < 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
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 |
| @@ -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 |
| @@ -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 | + |
| @@ -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 |
| @@ -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 | + | ||
| 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
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 | - | ||
| 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
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
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
db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb
0 → 100644
db/schema.rb
| @@ -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 => 20130218141554) do | @@ -68,19 +68,19 @@ ActiveRecord::Schema.define(:version => 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 => 20130218141554) do | @@ -106,11 +106,11 @@ ActiveRecord::Schema.define(:version => 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 => 20130218141554) do | @@ -142,16 +142,16 @@ ActiveRecord::Schema.define(:version => 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 => 20130218141554) do | @@ -230,8 +230,8 @@ ActiveRecord::Schema.define(:version => 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 < Spinach::FeatureSteps | @@ -9,7 +9,7 @@ class AdminTeams < 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 < Spinach::FeatureSteps | @@ -50,8 +50,8 @@ class AdminTeams < 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 < Spinach::FeatureSteps | @@ -89,8 +89,8 @@ class AdminTeams < 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 < Spinach::FeatureSteps | @@ -127,8 +127,8 @@ class AdminTeams < 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 < Spinach::FeatureSteps | @@ -23,15 +23,19 @@ class Profile < 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 < Spinach::FeatureSteps | @@ -43,8 +47,10 @@ class Profile < 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 < Spinach::FeatureSteps | @@ -16,12 +16,12 @@ class ProjectBrowseFiles < 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 < Spinach::FeatureSteps | @@ -3,8 +3,8 @@ class ProjectBrowseGitRepo < 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 < Spinach::FeatureSteps | @@ -12,7 +12,7 @@ class ProjectBrowseGitRepo < 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 < Spinach::FeatureSteps | @@ -53,7 +53,7 @@ class ProjectTeamManagement < 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 < Spinach::FeatureSteps | @@ -177,8 +177,8 @@ class Userteams < 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 < Spinach::FeatureSteps | @@ -213,8 +213,8 @@ class Userteams < 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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 | + |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |
| @@ -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 |