Commit 73e00301 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' into admin-edit-identities

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>

Conflicts:
	app/views/admin/users/show.html.haml
parents 3fe3cbf2 cc9b5c49
Please view this file on the master branch, on stable branches it's out of date.
v 7.13.0 (unreleased)
- Update maintenance documentation to explain no need to recompile asssets for omnibus installations (Stan Hu)
- Support commenting on diffs in side-by-side mode (Stan Hu)
- Fix JavaScript error when clicking on the comment button on a diff line that has a comment already (Stan Hu)
- Remove project visibility icons from dashboard projects list
......@@ -8,8 +9,12 @@ v 7.13.0 (unreleased)
- Allow users to customize their default Dashboard page.
- Update ssl_ciphers in Nginx example to remove DHE settings. This will deny forward secrecy for Android 2.3.7, Java 6 and OpenSSL 0.9.8
- Admin can edit and remove user identities
- Convert CRLF newlines to LF when committing using the web editor.
- API request /projects/:project_id/merge_requests?state=closed will return only closed merge requests without merged one. If you need ones that were merged - use state=merged.
v 7.12.0 (unreleased)
- Fix Error 500 when one user attempts to access a personal, internal snippet (Stan Hu)
- Disable changing of target branch in new merge request page when a branch has already been specified (Stan Hu)
- Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
- Update oauth button logos for Twitter and Google to recommended assets
- Fix hooks for web based events with external issue references (Daniel Gerhardt)
......@@ -48,8 +53,8 @@ v 7.12.0 (unreleased)
- Add validation to wiki page creation (only [a-zA-Z0-9/_-] are allowed) (Jeroen van Baarsen)
- Fix new/empty milestones showing 100% completion value (Jonah Bishop)
- Add a note when an Issue or Merge Request's title changes
- Consistently refer to MRs as either Accepted or Rejected.
- Add Accepted and Rejected tabs to MR lists.
- Consistently refer to MRs as either Merged or Closed.
- Add Merged tab to MR lists.
- Prefix EmailsOnPush email subject with `[Git]`.
- Group project contributions by both name and email.
- Clarify navigation labels for Project Settings and Group Settings.
......@@ -61,7 +66,7 @@ v 7.12.0 (unreleased)
- Allow to configure a URL to show after sign out
- Add an option to automatically sign-in with an Omniauth provider
- Better performance for web editor (switched from satellites to rugged)
- GitLab CI service sends .gitlab-ci.yaml in each push call
- GitLab CI service sends .gitlab-ci.yml in each push call
- When remove project - move repository and schedule it removal
- Improve group removing logic
- Trigger create-hooks on backup restore task
......
......@@ -167,15 +167,17 @@ If you add a dependency in GitLab (such as an operating system package) please c
This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
## Code of conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior can be
reported by emailing contact@gitlab.com
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
\ No newline at end of file
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
......@@ -34,7 +34,7 @@ gem "browser", '~> 0.8.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.2.3'
gem "gitlab_git", '~> 7.2.5'
# Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
......@@ -222,16 +222,16 @@ group :development do
end
group :development, :test do
gem 'awesome_print'
gem 'byebug'
gem 'pry-rails'
gem 'coveralls', require: false
gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails'
gem 'rspec-rails', '~> 3.3.0'
gem 'rubocop', '0.28.0', require: false
gem 'spinach-rails'
gem "rspec-rails", '2.99'
gem 'capybara', '~> 2.2.1'
gem 'capybara-screenshot', '~> 1.0.0'
gem "pry-rails"
gem "awesome_print"
gem "database_cleaner"
gem 'factory_girl_rails'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.3.0'
......@@ -239,8 +239,9 @@ group :development, :test do
# Generate Fake data
gem 'ffaker', '~> 2.0.0'
# PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.5.1'
gem 'capybara', '~> 2.3.0'
gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.6.0'
gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine'
......@@ -249,14 +250,12 @@ group :development, :test do
gem 'spring-commands-rspec', '~> 1.0.0'
gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem "byebug"
end
group :test do
gem 'simplecov', require: false
gem 'shoulda-matchers', '~> 2.8.0', require: false
gem 'email_spec'
gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0'
gem 'test_after_commit'
end
......
......@@ -82,7 +82,7 @@ GEM
columnize (~> 0.8)
debugger-linecache (~> 1.2)
cal-heatmap-rails (0.0.1)
capybara (2.2.1)
capybara (2.3.0)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
......@@ -125,7 +125,7 @@ GEM
d3_rails (3.5.5)
railties (>= 3.1.0)
daemons (1.1.9)
database_cleaner (1.3.0)
database_cleaner (1.4.1)
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
default_value_for (3.0.0)
......@@ -154,7 +154,7 @@ GEM
dotenv (0.9.0)
dropzonejs-rails (0.4.14)
rails (> 3.1)
email_spec (1.5.0)
email_spec (1.6.0)
launchy (~> 2.1)
mail (~> 2.2)
encryptor (1.3.0)
......@@ -266,7 +266,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
gitlab_git (7.2.3)
gitlab_git (7.2.5)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
......@@ -348,7 +348,7 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.9.2)
launchy (2.4.2)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.1.2)
launchy (~> 2.2)
......@@ -431,8 +431,8 @@ GEM
orm_adapter (0.5.0)
parser (2.2.0.2)
ast (>= 1.1, < 3.0)
pg (0.15.1)
poltergeist (1.5.1)
pg (0.18.2)
poltergeist (1.6.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
......@@ -449,7 +449,7 @@ GEM
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
racc (1.4.10)
rack (1.5.4)
rack (1.5.5)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.3.0)
......@@ -530,21 +530,23 @@ GEM
rqrcode (0.4.2)
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
rspec-collection_matchers (1.1.2)
rspec-expectations (>= 2.99.0.beta1)
rspec-core (2.99.2)
rspec-expectations (2.99.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.99.3)
rspec-rails (2.99.0)
actionpack (>= 3.0)
activemodel (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-collection_matchers
rspec-core (~> 2.99.0)
rspec-expectations (~> 2.99.0)
rspec-mocks (~> 2.99.0)
rspec-core (3.3.1)
rspec-support (~> 3.3.0)
rspec-expectations (3.3.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-mocks (3.3.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-rails (3.3.2)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.3.0)
rspec-expectations (~> 3.3.0)
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
rubocop (0.28.0)
astrolabe (~> 1.3)
parser (>= 2.2.0.pre.7, < 3.0)
......@@ -707,7 +709,9 @@ GEM
webmock (1.21.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
websocket-driver (0.3.3)
websocket-driver (0.5.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
wikicloth (0.8.1)
builder
expression_parser
......@@ -735,7 +739,7 @@ DEPENDENCIES
browser (~> 0.8.0)
byebug
cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1)
capybara (~> 2.3.0)
capybara-screenshot (~> 1.0.0)
carrierwave
charlock_holmes
......@@ -744,7 +748,7 @@ DEPENDENCIES
coveralls
creole (~> 0.3.6)
d3_rails (~> 3.5.5)
database_cleaner
database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0)
devise (= 3.2.4)
devise-async (= 0.9.0)
......@@ -752,7 +756,7 @@ DEPENDENCIES
diffy (~> 3.0.3)
doorkeeper (= 2.1.3)
dropzonejs-rails
email_spec
email_spec (~> 1.6.0)
enumerize
factory_girl_rails
ffaker (~> 2.0.0)
......@@ -765,7 +769,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.3)
gitlab_git (~> 7.2.5)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2)
......@@ -800,7 +804,7 @@ DEPENDENCIES
omniauth-twitter
org-ruby (= 0.9.12)
pg
poltergeist (~> 1.5.1)
poltergeist (~> 1.6.0)
pry-rails
quiet_assets (~> 1.0.1)
rack-attack (~> 4.3.0)
......@@ -815,7 +819,7 @@ DEPENDENCIES
request_store
rerun (~> 0.10.0)
rqrcode-rails3
rspec-rails (= 2.99)
rspec-rails (~> 3.3.0)
rubocop (= 0.28.0)
rugments (~> 1.0.0.beta7)
sanitize (~> 2.0)
......
app/assets/images/favicon.ico

32.2 KB | W: | H:

app/assets/images/favicon.ico

5.3 KB | W: | H:

app/assets/images/favicon.ico
app/assets/images/favicon.ico
app/assets/images/favicon.ico
app/assets/images/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="210px" height="210px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
<title>Slice 1</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
<g id="Page-1" sketch:type="MSShapeGroup">
<g id="Fill-1-+-Group-24">
<g id="Group-24">
<g id="Group">
<path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329"></path>
<path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26"></path>
<path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326"></path>
<path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329"></path>
<path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26"></path>
<path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326"></path>
<path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="546px" height="194px" viewBox="0 0 546 194" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
<title>Fill 1 + Group 24</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="Fill-1-+-Group-24" sketch:type="MSLayerGroup">
<g id="Group-24" sketch:type="MSShapeGroup">
<path d="M316.7906,65.3001 C301.5016,65.3001 292.0046,77.4461 292.0046,97.0001 C292.0046,116.5541 301.5016,128.7001 316.7906,128.7001 C322.5346,128.7001 327.8716,127.0711 332.2226,123.9881 L332.4336,123.8391 L332.4336,101.8711 L310.4336,101.8711 L310.4336,94.0711 L341.4336,94.0711 L341.4336,126.8061 C334.8706,133.1501 326.3546,136.5001 316.7906,136.5001 C296.2666,136.5001 283.0046,120.9951 283.0046,97.0001 C283.0046,73.0051 296.2666,57.5001 316.7906,57.5001 C326.7826,57.5001 335.2176,61.1481 341.2206,68.0561 L335.2246,73.0381 C330.6986,67.9041 324.4986,65.3001 316.7906,65.3001 L316.7906,65.3001 Z M489.8836,135.2501 L482.9356,135.2501 L480.6016,128.8021 L480.0486,129.2991 C479.9716,129.3681 472.2196,136.2501 462.4606,136.2501 C452.6096,136.2501 445.4606,129.6961 445.4606,120.6671 C445.4606,107.5951 456.7446,104.8511 466.2096,104.8511 C473.5836,104.8511 480.1886,106.5111 480.2546,106.5281 L480.8776,106.6871 L480.8776,105.1011 C480.8776,97.9861 476.4356,94.3781 467.6726,94.3781 C462.3646,94.3781 456.7556,95.6891 451.4236,98.1701 L447.8206,91.9581 C452.5266,88.8961 459.6726,85.3781 467.6726,85.3781 C481.5806,85.3781 489.8836,92.9341 489.8836,105.5891 L489.8836,135.2501 Z M470.6886,111.7771 C460.0716,111.7771 454.4606,114.8511 454.4606,120.6671 C454.4606,124.7281 457.5256,127.2501 462.4606,127.2501 C470.5906,127.2501 477.7276,123.9181 480.6626,121.9481 L480.8836,121.8001 L480.8836,112.6201 L480.4676,112.5491 C480.4226,112.5411 475.8766,111.7771 470.6886,111.7771 L470.6886,111.7771 Z M440.4576,127.4501 L440.4576,135.2501 L410.4606,135.2501 L410.4606,61.2501 L419.4606,61.2501 L419.4606,127.4501 L440.4576,127.4501 Z M520.9416,136.5001 C515.0966,136.5001 508.6886,135.6961 501.8926,134.1091 L501.8926,61.2501 L510.8926,61.2501 L510.8926,89.3131 L511.6656,88.8111 C511.7146,88.7791 516.7346,85.5711 523.6536,85.5711 C525.0336,85.5711 526.4146,85.7001 527.7486,85.9521 C539.0936,88.2761 545.8666,97.4301 545.8666,110.4391 C545.8666,125.7831 535.6176,136.5001 520.9416,136.5001 L520.9416,136.5001 Z M521.9426,94.3781 C518.3636,94.3781 514.6196,95.6031 511.1166,97.9191 L510.8926,98.0681 L510.8926,127.9021 L511.3196,127.9651 C514.6986,128.4601 517.9356,128.7121 520.9416,128.7121 C530.3176,128.7121 536.8666,121.1971 536.8666,110.4391 C536.8666,100.2321 531.4266,94.3781 521.9426,94.3781 L521.9426,94.3781 Z M398.4516,86.2501 L398.4516,94.0501 L383.4516,94.0501 L383.4516,116.9501 C383.4516,119.7551 384.5436,122.3921 386.5276,124.3741 C388.5096,126.3581 391.1466,127.4501 393.9516,127.4501 L398.4516,127.4501 L398.4516,135.2501 L393.9516,135.2501 C383.1996,135.2501 374.4516,126.5021 374.4516,115.7501 L374.4516,61.2501 L383.4516,61.2501 L383.4516,86.2501 L398.4516,86.2501 Z M353.4426,66.2501 L362.4426,66.2501 L362.4426,75.2501 L353.4426,75.2501 L353.4426,66.2501 Z M353.4426,86.2501 L362.4426,86.2501 L362.4426,135.2501 L353.4426,135.2501 L353.4426,86.2501 Z" id="Fill-2" fill="#8C929D"></path>
<g id="Group">
<path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329"></path>
<path id="Fill-6" fill="#FC6D26"></path>
<path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26"></path>
<path id="Fill-10" fill="#FC6D26"></path>
<path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326"></path>
<path id="Fill-14" fill="#FC6D26"></path>
<path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329"></path>
<path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26"></path>
<path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326"></path>
<path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -141,8 +141,7 @@ $ ->
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
$("abbr.timeago").timeago()
$('.js-timeago').timeago()
$('abbr.timeago, .js-timeago').timeago()
# Flash
if (flash = $(".flash-container")).length > 0
......
class @BlobView
constructor: ->
# handle multi-line select
handleMultiSelect = (e) ->
[ first_line, last_line ] = parseSelectedLines()
[ line_number ] = parseSelectedLines($(this).attr("id"))
hash = "L#{line_number}"
if e.shiftKey and not isNaN(first_line) and not isNaN(line_number)
if line_number < first_line
last_line = first_line
first_line = line_number
else
last_line = line_number
hash = if first_line == last_line then "L#{first_line}" else "L#{first_line}-#{last_line}"
setHash(hash)
e.preventDefault()
# See if there are lines selected
# "#L12" and "#L34-56" supported
highlightBlobLines = (e) ->
[ first_line, last_line ] = parseSelectedLines()
unless isNaN first_line
$("#tree-content-holder .highlight .line").removeClass("hll")
$("#LC#{line}").addClass("hll") for line in [first_line..last_line]
$.scrollTo("#L#{first_line}", offset: -50) unless e?
# parse selected lines from hash
# always return first and last line (initialized to NaN)
parseSelectedLines = (str) ->
first_line = NaN
last_line = NaN
hash = str || window.location.hash
if hash isnt ""
matches = hash.match(/\#?L(\d+)(\-(\d+))?/)
first_line = parseInt(matches?[1])
last_line = parseInt(matches?[3])
last_line = first_line if isNaN(last_line)
[ first_line, last_line ]
setHash = (hash) ->
hash = hash.replace(/^\#/, "")
nodes = $("#" + hash)
# if any nodes are using this id, they must be temporarily changed
# also, add a temporary div at the top of the screen to prevent scrolling
if nodes.length > 0
scroll_top = $(document).scrollTop()
nodes.attr("id", "")
tmp = $("<div></div>")
.css({ position: "absolute", visibility: "hidden", top: scroll_top + "px" })
.attr("id", hash)
.appendTo(document.body)
window.location.hash = hash
# restore the nodes
if nodes.length > 0
tmp.remove()
nodes.attr("id", hash)
# initialize multi-line select
$("#tree-content-holder .line-numbers a[id^=L]").on("click", handleMultiSelect)
# Highlight the correct lines on load
highlightBlobLines()
# Highlight the correct lines when the hash part of the URL changes
$(window).on("hashchange", highlightBlobLines)
......@@ -87,7 +87,7 @@ class Dispatcher
new TreeView()
shortcut_handler = new ShortcutsNavigation()
when 'projects:blob:show'
new BlobView()
new LineHighlighter()
shortcut_handler = new ShortcutsNavigation()
when 'projects:labels:new', 'projects:labels:edit'
new Labels()
......
# LineHighlighter
#
# Handles single- and multi-line selection and highlight for blob views.
#
#= require jquery.scrollTo
#
# ### Example Markup
#
# <div id="tree-content-holder">
# <div class="file-content">
# <div class="line-numbers">
# <a href="#L1" id="L1" data-line-number="1">1</a>
# <a href="#L2" id="L2" data-line-number="2">2</a>
# <a href="#L3" id="L3" data-line-number="3">3</a>
# <a href="#L4" id="L4" data-line-number="4">4</a>
# <a href="#L5" id="L5" data-line-number="5">5</a>
# </div>
# <pre class="code highlight">
# <code>
# <span id="LC1" class="line">...</span>
# <span id="LC2" class="line">...</span>
# <span id="LC3" class="line">...</span>
# <span id="LC4" class="line">...</span>
# <span id="LC5" class="line">...</span>
# </code>
# </pre>
# </div>
# </div>
#
class @LineHighlighter
# CSS class applied to highlighted lines
highlightClass: 'hll'
# Internal copy of location.hash so we're not dependent on `location` in tests
_hash: ''
# Initialize a LineHighlighter object
#
# hash - String URL hash for dependency injection in tests
constructor: (hash = location.hash) ->
@_hash = hash
@bindEvents()
unless hash == ''
range = @hashToRange(hash)
if range[0]
@highlightRange(range)
# Scroll to the first highlighted line on initial load
# Offset -50 for the sticky top bar, and another -100 for some context
$.scrollTo("#L#{range[0]}", offset: -150)
bindEvents: ->
$('#tree-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler
# While it may seem odd to bind to the mousedown event and then throw away
# the click event, there is a method to our madness.
#
# If not done this way, the line number anchor will sometimes keep its
# active state even when the event is cancelled, resulting in an ugly border
# around the link and/or a persisted underline text decoration.
$('#tree-content-holder').on 'click', 'a[data-line-number]', (event) ->
event.preventDefault()
clickHandler: (event) =>
event.preventDefault()
@clearHighlight()
lineNumber = $(event.target).data('line-number')
current = @hashToRange(@_hash)
unless current[0] && event.shiftKey
# If there's no current selection, or there is but Shift wasn't held,
# treat this like a single-line selection.
@setHash(lineNumber)
@highlightLine(lineNumber)
else if event.shiftKey
if lineNumber < current[0]
range = [lineNumber, current[0]]
else
range = [current[0], lineNumber]
@setHash(range[0], range[1])
@highlightRange(range)
# Unhighlight previously highlighted lines
clearHighlight: ->
$(".#{@highlightClass}").removeClass(@highlightClass)
# Convert a URL hash String into line numbers
#
# hash - Hash String
#
# Examples:
#
# hashToRange('#L5') # => [5, null]
# hashToRange('#L5-15') # => [5, 15]
# hashToRange('#foo') # => [null, null]
#
# Returns an Array
hashToRange: (hash) ->
matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/)
if matches && matches.length
first = parseInt(matches[1])
last = if matches[2] then parseInt(matches[2]) else null
[first, last]
else
[null, null]
# Highlight a single line
#
# lineNumber - Line number to highlight
highlightLine: (lineNumber) =>
$("#LC#{lineNumber}").addClass(@highlightClass)
# Highlight all lines within a range
#
# range - Array containing the starting and ending line numbers
highlightRange: (range) ->
if range[1]
for lineNumber in [range[0]..range[1]]
@highlightLine(lineNumber)
else
@highlightLine(range[0])
# Set the URL hash string
setHash: (firstLineNumber, lastLineNumber) =>
if lastLineNumber
hash = "#L#{firstLineNumber}-#{lastLineNumber}"
else
hash = "#L#{firstLineNumber}"
@_hash = hash
@__setLocationHash__(hash)
# Make the actual hash change in the browser
#
# This method is stubbed in tests.
__setLocationHash__: (value) ->
# We're using pushState instead of assigning location.hash directly to
# prevent the page from scrolling on the hashchange event
history.pushState({turbolinks: false, url: value}, document.title, value)
#= require jquery.waitforimages
#= require task_list
#= require merge_request_tabs
class @MergeRequest
# Initialize MergeRequest behavior
#
# Options:
# action - String, current controller action
# diffs_loaded - Boolean, have diffs been pre-rendered server-side?
# (default: true if `action` is 'diffs', otherwise false)
# commits_loaded - Boolean, have commits been pre-rendered server-side?
# (default: false)
#
constructor: (@opts) ->
@initContextWidget()
this.$el = $('.merge-request')
@diffs_loaded = @opts.diffs_loaded or @opts.action == 'diffs'
@commits_loaded = @opts.commits_loaded or false
this.bindEvents()
this.activateTabFromPath()
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
# `MergeRequests#new` has no tab-persisting or lazy-loading behavior
unless @opts.action == 'new'
new MergeRequestTabs(@opts)
# Prevent duplicate event bindings
@disableTaskList()
......@@ -52,83 +48,6 @@ class @MergeRequest
$(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
bindEvents: ->
this.$('.merge-request-tabs a[data-toggle="tab"]').on 'shown.bs.tab', (e) =>
$target = $(e.target)
tab_action = $target.data('action')
# Lazy-load diffs
if tab_action == 'diffs'
this.loadDiff() unless @diffs_loaded
$('.diff-header').trigger('sticky_kit:recalc')
# Skip tab-persisting behavior on MergeRequests#new
unless @opts.action == 'new'
@setCurrentAction(tab_action)
# Activate a tab based on the current URL path
#
# If the current action is 'show' or 'new' (i.e., initial page load),
# activates the first tab, otherwise activates the tab corresponding to the
# current action (diffs, commits).
activateTabFromPath: ->
if @opts.action == 'show' || @opts.action == 'new'
this.$('.merge-request-tabs a[data-toggle="tab"]:first').tab('show')
else
this.$(".merge-request-tabs a[data-action='#{@opts.action}']").tab('show')
# Replaces the current Merge Request-specific action in the URL with a new one
#
# If the action is "notes", the URL is reset to the standard
# `MergeRequests#show` route.
#
# Examples:
#
# location.pathname # => "/namespace/project/merge_requests/1"
# setCurrentAction('diffs')
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('notes')
# location.pathname # => "/namespace/project/merge_requests/1"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('commits')
# location.pathname # => "/namespace/project/merge_requests/1/commits"
setCurrentAction: (action) ->
# Normalize action, just to be safe
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = location.pathname.replace(/\/(commits|diffs)\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
new_state += "/#{action}"
# Ensure parameters and hash come along for the ride
new_state += location.search + location.hash
# Replace the current history state with the new one without breaking
# Turbolinks' history.
#
# See https://github.com/rails/turbolinks/issues/363
history.replaceState {turbolinks: true, url: new_state}, '', new_state
loadDiff: (event) ->
$.ajax
type: 'GET'
url: this.$('.merge-request-tabs .diffs-tab a').attr('href') + ".json"
beforeSend: =>
this.$('.mr-loading-status .loading').show()
complete: =>
@diffs_loaded = true
this.$('.mr-loading-status .loading').hide()
success: (data) =>
this.$(".diffs").html(data.html)
dataType: 'json'
showAllCommits: ->
this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide'
......
# MergeRequestTabs
#
# Handles persisting and restoring the current tab selection and lazily-loading
# content on the MergeRequests#show page.
#
# ### Example Markup
#
# <ul class="nav nav-tabs merge-request-tabs">
# <li class="notes-tab active">
# <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
# Discussion
# </a>
# </li>
# <li class="commits-tab">
# <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
# Commits
# </a>
# </li>
# <li class="diffs-tab">
# <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
# Diffs
# </a>
# </li>
# </ul>
#
# <div class="tab-content">
# <div class="notes tab-pane active" id="notes">
# Notes Content
# </div>
# <div class="commits tab-pane" id="commits">
# Commits Content
# </div>
# <div class="diffs tab-pane" id="diffs">
# Diffs Content
# </div>
# </div>
#
# <div class="mr-loading-status">
# <div class="loading">
# Loading Animation
# </div>
# </div>
#
class @MergeRequestTabs
diffsLoaded: false
commitsLoaded: false
constructor: (@opts = {}) ->
# Store the `location` object, allowing for easier stubbing in tests
@_location = location
@bindEvents()
@activateTab(@opts.action)
switch @opts.action
when 'commits' then @commitsLoaded = true
when 'diffs' then @diffsLoaded = true
bindEvents: ->
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
tabShown: (event) =>
$target = $(event.target)
action = $target.data('action')
if action == 'commits'
@loadCommits($target.attr('href'))
else if action == 'diffs'
@loadDiff($target.attr('href'))
@setCurrentAction(action)
# Activate a tab based on the current action
activateTab: (action) ->
action = 'notes' if action == 'show'
$(".merge-request-tabs a[data-action='#{action}']").tab('show')
# Replaces the current Merge Request-specific action in the URL with a new one
#
# If the action is "notes", the URL is reset to the standard
# `MergeRequests#show` route.
#
# Examples:
#
# location.pathname # => "/namespace/project/merge_requests/1"
# setCurrentAction('diffs')
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('notes')
# location.pathname # => "/namespace/project/merge_requests/1"
#
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
# setCurrentAction('commits')
# location.pathname # => "/namespace/project/merge_requests/1/commits"
#
# Returns the new URL String
setCurrentAction: (action) =>
# Normalize action, just to be safe
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = @_location.pathname.replace(/\/(commits|diffs)\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
new_state += "/#{action}"
# Ensure parameters and hash come along for the ride
new_state += @_location.search + @_location.hash
# Replace the current history state with the new one without breaking
# Turbolinks' history.
#
# See https://github.com/rails/turbolinks/issues/363
history.replaceState {turbolinks: true, url: new_state}, document.title, new_state
new_state
loadCommits: (source) ->
return if @commitsLoaded
@_get
url: "#{source}.json"
success: (data) =>
document.getElementById('commits').innerHTML = data.html
$('.js-timeago').timeago()
@commitsLoaded = true
loadDiff: (source) ->
return if @diffsLoaded
@_get
url: "#{source}.json"
success: (data) =>
document.getElementById('diffs').innerHTML = data.html
$('.diff-header').trigger('sticky_kit:recalc')
@diffsLoaded = true
toggleLoading: ->
$('.mr-loading-status .loading').toggle()
_get: (options) ->
defaults = {
beforeSend: @toggleLoading
complete: @toggleLoading
dataType: 'json'
type: 'GET'
}
options = $.extend({}, defaults, options)
$.ajax(options)
......@@ -10,6 +10,10 @@ header {
.center-logo {
margin: 8px 0;
text-align: center;
img {
height: 32px;
}
}
}
......
class DashboardController < Dashboard::ApplicationController
before_action :load_projects, except: [:projects]
before_action :load_projects
before_action :event_filter, only: :show
respond_to :html
......
......@@ -24,7 +24,7 @@ class PasswordsController < Devise::PasswordsController
super do |resource|
# TODO (rspeicher): In Devise master (> 3.4.1), we can set
# `Devise.sign_in_after_reset_password = false` and avoid this mess.
if resource.errors.empty? && resource.try(:otp_required_for_login?)
if resource.errors.empty? && resource.try(:two_factor_enabled?)
resource.unlock_access! if unlockable?(resource)
# Since we are not signing this user in, we use the :updated_not_active
......
......@@ -10,7 +10,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def create
if current_user.valid_otp?(params[:pin_code])
current_user.otp_required_for_login = true
current_user.two_factor_enabled = true
@codes = current_user.generate_otp_backup_codes!
current_user.save!
......@@ -30,7 +30,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def destroy
current_user.update_attributes({
otp_required_for_login: false,
two_factor_enabled: false,
encrypted_otp_secret: nil,
encrypted_otp_secret_iv: nil,
encrypted_otp_secret_salt: nil,
......
......@@ -71,7 +71,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def commits
render 'show'
respond_to do |format|
format.html { render 'show' }
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_commits') } }
end
end
def new
......
......@@ -57,7 +57,7 @@ class SessionsController < Devise::SessionsController
def authenticate_with_two_factor
user = self.resource = find_user
return unless user && user.otp_required_for_login
return unless user && user.two_factor_enabled?
if user_params[:otp_attempt].present? && session[:otp_user_id]
if valid_otp_attempt?(user)
......
......@@ -148,8 +148,6 @@ class IssuableFinder
case params[:state]
when 'closed'
items.closed
when 'rejected'
items.respond_to?(:rejected) ? items.rejected : items.closed
when 'merged'
items.respond_to?(:merged) ? items.merged : items.closed
when 'all'
......
......@@ -16,6 +16,6 @@ module AppearancesHelper
end
def brand_header_logo
image_tag 'logo-white.png'
image_tag 'logo.svg'
end
end
......@@ -179,14 +179,33 @@ module ApplicationHelper
BroadcastMessage.current
end
def time_ago_with_tooltip(date, placement = 'top', html_class = 'time_ago')
capture_haml do
haml_tag :time, date.to_s,
class: html_class, datetime: date.getutc.iso8601, title: date.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
# Render a `time` element with Javascript-based relative date and tooltip
#
# time - Time object
# placement - Tooltip placement String (default: "top")
# html_class - Custom class for `time` element (default: "time_ago")
# skip_js - When true, exclude the `script` tag (default: false)
#
# By default also includes a `script` element with Javascript necessary to
# initialize the `timeago` jQuery extension. If this method is called many
# times, for example rendering hundreds of commits, it's advisable to disable
# this behavior using the `skip_js` argument and re-initializing `timeago`
# manually once all of the elements have been rendered.
#
# A `js-timeago` class is always added to the element, even when a custom
# `html_class` argument is provided.
#
# Returns an HTML-safe String
def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
element = content_tag :time, time.to_s,
class: "#{html_class} js-timeago",
datetime: time.getutc.iso8601,
title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement }
haml_tag :script, "$('." + html_class + "').timeago().tooltip()"
end.html_safe
element += javascript_tag "$('.js-timeago').timeago()" unless skip_js
element
end
def render_markup(file_name, file_content)
......@@ -214,39 +233,6 @@ module ApplicationHelper
Gitlab::MarkupHelper.asciidoc?(filename)
end
# Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to
# external links
def link_to(name = nil, options = nil, html_options = {})
if options.kind_of?(String)
if !options.start_with?('#', '/')
html_options = add_nofollow(options, html_options)
end
end
super
end
# Add `"rel=nofollow"` to external links
#
# link - String link to check
# html_options - Hash of `html_options` passed to `link_to`
#
# Returns `html_options`, adding `rel: nofollow` for external links
def add_nofollow(link, html_options = {})
begin
uri = URI(link)
if uri && uri.absolute? && uri.host != Gitlab.config.gitlab.host
rel = html_options.fetch(:rel, '')
html_options[:rel] = (rel + ' nofollow').strip
end
rescue URI::Error
# noop
end
html_options
end
def promo_host
'about.gitlab.com'
end
......@@ -295,8 +281,7 @@ module ApplicationHelper
def state_filters_text_for(entity, project)
titles = {
opened: "Open",
merged: "Accepted"
opened: "Open"
}
entity_title = titles[entity] || entity.to_s.humanize
......
module BroadcastMessagesHelper
def broadcast_styling(broadcast_message)
if(broadcast_message.color || broadcast_message.font)
"background-color:#{broadcast_message.color};color:#{broadcast_message.font}"
else
""
styling = ''
if broadcast_message.color.present?
styling << "background-color: #{broadcast_message.color}"
styling << '; ' if broadcast_message.font.present?
end
if broadcast_message.font.present?
styling << "color: #{broadcast_message.font}"
end
styling
end
end
module IconsHelper
include FontAwesome::Rails::IconHelper
# Creates an icon tag given icon name(s) and possible icon modifiers.
#
# Right now this method simply delegates directly to `fa_icon` from the
......
......@@ -45,13 +45,13 @@ module IssuesHelper
def issue_timestamp(issue)
# Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}"
ts = time_ago_with_tooltip(issue.created_at, placement: 'bottom', html_class: 'note_created_ago')
if issue.updated_at != issue.created_at
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_edited_ago')
haml_concat time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
end
end
end
......
......@@ -25,13 +25,13 @@ module NotesHelper
def note_timestamp(note)
# Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(note.created_at, 'bottom', 'note_created_ago')}"
ts = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
if note.updated_at != note.created_at
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(note.updated_at, 'bottom', 'note_edited_ago')
haml_concat time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago')
end
end
end
......
module NotificationsHelper
include IconsHelper
def notification_icon(notification)
if notification.disabled?
icon('volume-off', class: 'ns-mute')
......
......@@ -211,7 +211,7 @@ module ProjectsHelper
def project_last_activity(project)
if project.last_activity_at
time_ago_with_tooltip(project.last_activity_at, 'bottom', 'last_activity_time_ago')
time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago')
else
"Never"
end
......
......@@ -263,7 +263,7 @@ class Ability
:"modify_#{name}",
]
else
if subject.respond_to?(:project)
if subject.respond_to?(:project) && subject.project
project_abilities(user, subject.project)
else
[]
......
......@@ -75,7 +75,7 @@ module Mentionable
refs.reject! { |ref| without.include?(ref) }
refs.each do |ref|
Note.create_cross_reference_note(ref, local_reference, a)
SystemNoteService.cross_reference(ref, local_reference, a)
end
end
......
......@@ -125,16 +125,14 @@ class MergeRequest < ActiveRecord::Base
validate :validate_fork
scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) }
scope :merged, -> { with_state(:merged) }
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
# Closed scope for merge request should return
# both merged and closed mr's
scope :closed, -> { with_states(:closed, :merged) }
scope :rejected, -> { with_states(:closed) }
scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
def self.reference_prefix
'!'
......@@ -417,4 +415,14 @@ class MergeRequest < ActiveRecord::Base
def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
end
def state_human_name
if merged?
"Merged"
elsif closed?
"Closed"
else
"Open"
end
end
end
......@@ -56,7 +56,7 @@ class Milestone < ActiveRecord::Base
end
def closed_items_count
self.issues.closed.count + self.merge_requests.closed.count
self.issues.closed.count + self.merge_requests.closed_and_merged.count
end
def total_items_count
......
......@@ -63,11 +63,6 @@ class Note < ActiveRecord::Base
after_update :set_references
class << self
# TODO (rspeicher): Update usages
def create_cross_reference_note(*args)
SystemNoteService.cross_reference(*args)
end
def discussions_from_notes(notes)
discussion_ids = []
discussions = []
......
......@@ -5,8 +5,13 @@ class Repository
def initialize(path_with_namespace, default_branch = nil, project = nil)
@path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
@project = project
if path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo)
@raw_repository.autocrlf = :input
end
rescue Gitlab::Git::Repository::NoRepository
nil
end
......
......@@ -34,7 +34,6 @@ class Snippet < ActiveRecord::Base
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
validates :file_name,
presence: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.file_name_regex,
message: Gitlab::Regex.file_name_regex_message }
......
......@@ -172,6 +172,9 @@ class User < ActiveRecord::Base
after_create :post_create_hook
after_destroy :post_destroy_hook
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars]
alias_attribute :private_token, :authentication_token
......@@ -220,10 +223,26 @@ class User < ActiveRecord::Base
end
def find_for_commit(email, name)
# Prefer email match over name match
User.where(email: email).first ||
User.joins(:emails).where(emails: { email: email }).first ||
User.where(name: name).first
user_table = arel_table
email_table = Email.arel_table
# Use ARel to build a query:
query = user_table.
# SELECT "users".* FROM "users"
project(user_table[Arel.star]).
# LEFT OUTER JOIN "emails"
join(email_table, Arel::Nodes::OuterJoin).
# ON "users"."id" = "emails"."user_id"
on(user_table[:id].eq(email_table[:user_id])).
# WHERE ("user"."email" = '<email>' OR "user"."name" = '<name>')
# OR "emails"."email" = '<email>'
where(
user_table[:email].eq(email).
or(user_table[:name].eq(name)).
or(email_table[:email].eq(email))
)
find_by_sql(query.to_sql).first
end
def filter(filter_name)
......@@ -297,6 +316,18 @@ class User < ActiveRecord::Base
@reset_token
end
# Check if the user has enabled Two-factor Authentication
def two_factor_enabled?
otp_required_for_login
end
# Set whether or not Two-factor Authentication is enabled for the current user
#
# setting - Boolean
def two_factor_enabled=(setting)
self.otp_required_for_login = setting
end
def namespace_uniq
namespace_name = self.username
existing_namespace = Namespace.by_path(namespace_name)
......@@ -704,8 +735,4 @@ class User < ActiveRecord::Base
def can_be_removed?
!solo_owned_groups.present?
end
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars]
end
......@@ -105,7 +105,7 @@ class GitPushService
author ||= commit_user(commit)
refs.each do |r|
Note.create_cross_reference_note(r, commit, author)
SystemNoteService.cross_reference(r, commit, author)
end
end
end
......
......@@ -15,7 +15,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author)
SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
end
execute_hooks(note)
......
......@@ -13,7 +13,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that
# names an issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author)
SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
end
end
end
......
......@@ -38,6 +38,14 @@
= link_to remove_email_admin_user_path(@user, email), data: { confirm: "Are you sure you want to remove #{email.email}?" }, method: :delete, class: "btn-xs btn btn-remove pull-right", title: 'Remove secondary email', id: "remove_email_#{email.id}" do
%i.fa.fa-times
%li.two-factor-status
%span.light Two-factor Authentication:
%strong{class: @user.two_factor_enabled? ? 'cgreen' : 'cred'}
- if @user.two_factor_enabled?
Enabled
- else
Disabled
%li
%span.light Can create groups:
%strong
......
%header.navbar.navbar-fixed-top.navbar-empty
.container
.center-logo
= image_tag 'logo-white.png', width: 32, height: 32
= brand_header_logo
......@@ -36,7 +36,7 @@
.panel-heading
Two-factor Authentication
.panel-body
- if current_user.otp_required_for_login
- if current_user.two_factor_enabled?
.pull-right
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
data: { confirm: 'Are you sure?' }
......
......@@ -81,21 +81,22 @@
- if issuable.is_a?(MergeRequest)
%hr
- unless @merge_request.persisted?
- if @merge_request.new_record?
.form-group
= f.label :source_branch, class: 'control-label' do
%i.fa.fa-code-fork
Source Branch
.col-sm-10
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
%p.help-block
= link_to 'Change source branch', mr_change_branches_path(@merge_request)
.form-group
= f.label :target_branch, class: 'control-label' do
%i.fa.fa-code-fork
Target Branch
.col-sm-10
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2' })
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record? })
- if @merge_request.new_record?
%p.help-block
= link_to 'Change branches', mr_change_branches_path(@merge_request)
.form-actions
- if !issuable.project.empty_repo? && (guide_url = contribution_guide_url(issuable.project)) && !issuable.persisted?
......
......@@ -29,5 +29,5 @@
= commit_author_link(commit, avatar: true, size: 24)
authored
.committed_ago
#{time_ago_with_tooltip(commit.committed_date)} &nbsp;
#{time_ago_with_tooltip(commit.committed_date, skip_js: true)} &nbsp;
= link_to_browse_code(project, commit)
......@@ -28,7 +28,7 @@
= 0
.issue-info
= "##{issue.iid} opened #{time_ago_with_tooltip(issue.created_at, 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
= "##{issue.iid} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
- if issue.milestone
......@@ -41,4 +41,4 @@
= issue.task_status
.pull-right.issue-updated-at
%small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
%small updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
......@@ -9,11 +9,11 @@
- if merge_request.merged?
%span
%i.fa.fa-check
ACCEPTED
MERGED
- elsif merge_request.closed?
%span
%i.fa.fa-ban
REJECTED
CLOSED
- else
%span.hidden-xs.hidden-sm
%span.label-branch<
......@@ -35,7 +35,7 @@
= 0
.merge-request-info
= "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
= "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
- if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
- if merge_request.milestone_id?
......@@ -48,4 +48,4 @@
= merge_request.task_status
.pull-right.hidden-xs
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
%small updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')}
......@@ -56,6 +56,7 @@
#notes.notes.tab-pane.voting_notes
= render "projects/merge_requests/discussion"
#commits.commits.tab-pane
- if current_page?(action: 'commits')
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane
- if current_page?(action: 'diffs')
......@@ -64,7 +65,6 @@
.mr-loading-status
= spinner
:javascript
var merge_request;
......
%h4.page-title
.issue-box{ class: issue_box_class(@merge_request) }
- if @merge_request.merged?
Accepted
- elsif @merge_request.closed?
Rejected
- else
Open
= @merge_request.state_human_name
= "Merge Request ##{@merge_request.iid}"
%small.creator
&middot;
......
......@@ -2,7 +2,7 @@
= render 'projects/merge_requests/widget/heading'
.mr-widget-body
%h4
Rejected
Closed
- if @merge_request.closed_event
by #{link_to_member(@project, @merge_request.closed_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
......
......@@ -2,7 +2,7 @@
= render 'projects/merge_requests/widget/heading'
.mr-widget-body
%h4
Accepted
Merged
- if @merge_request.merge_event
by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
......
......@@ -86,10 +86,10 @@
.col-md-3
= render('merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: @merge_requests.opened.assigned, id: 'ongoing')
.col-md-3
= render('merge_requests', title: 'Rejected (closed)', merge_requests: @merge_requests.rejected, id: 'closed')
= render('merge_requests', title: 'Rejected (closed)', merge_requests: @merge_requests.closed, id: 'closed')
.col-md-3
.panel.panel-primary
.panel-heading Accepted
.panel-heading Merged
%ul.well-list
- @merge_requests.merged.each do |merge_request|
= render 'merge_request', merge_request: merge_request
......
......@@ -16,7 +16,7 @@
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
#{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
......@@ -14,7 +14,7 @@
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
#{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
- if note.for_diff_line?
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
......
......@@ -14,6 +14,6 @@
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
#{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
#{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content.hide
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
......@@ -11,6 +11,6 @@
#{merge_request.project.name_with_namespace}
.pull-right
- if merge_request.merged?
%span.label.label-primary Accepted
%span.label.label-primary Merged
- elsif merge_request.closed?
%span.label.label-danger Rejected
%span.label.label-danger Closed
.file-content.code{class: user_color_scheme_class}
.line-numbers
- if blob.data.present?
- blob.data.lines.to_a.size.times do |index|
- blob.data.lines.each_index do |index|
- offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset
/ We're not using `link_to` because it is too slow once we get to thousands of lines.
%a{href: "#L#{i}", id: "L#{i}", rel: "#L#{i}"}
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
%a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
%i.fa.fa-link
= i
:preserve
......
......@@ -12,10 +12,10 @@
= icon('check-circle')
#{state_filters_text_for(:merged, @project)}
%li{class: ("active" if params[:state] == 'rejected')}
= link_to page_filter_path(state: 'rejected') do
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
= icon('ban')
#{state_filters_text_for(:rejected, @project)}
#{state_filters_text_for(:closed, @project)}
- else
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
......
......@@ -18,7 +18,7 @@
.col-sm-10
.file-holder.snippet
.file-title
= f.text_field :file_name, placeholder: "example.rb", class: 'form-control snippet-file-name', required: true
= f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name'
.file-content.code
%pre#editor= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content'
......
......@@ -9,6 +9,7 @@
.row
%section.col-md-8
.header-with-avatar
= link_to avatar_icon(@user.email), target: '_blank' do
= image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
%h3
= @user.name
......
# GitLab basics
Step-by-step guides on the basics of working with Git and GitLab.
* [Start using Git on the commandline](start_using_git.md)
# Start using Git on the commandline
If you want to start using a Git and GitLab, make sure that you have created an account on [gitlab.com](https://about.gitlab.com/)
## Open a shell
* Depending on your operating system, find the shell of your preference. Here are some suggestions
- [Terminal](http://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line) on Mac OSX
- [GitBash](https://msysgit.github.io) on Windows
- [Linux Terminal](http://www.howtogeek.com/140679/beginner-geek-how-to-start-using-the-linux-terminal/) on Linux
## Check if Git has already been installed
* Git is usually preinstalled on Mac and Linux
* Type the following command and then press enter
```
git --version
```
* You should receive a message that will tell you which Git version you have in your computer. If you don’t receive a "Git version" message, it means that you need to [download Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* If Git doesn't automatically download, there's an option on the website to [download manually](https://git-scm.com/downloads). Then follow the steps on the installation window
* After you finished installing, open a new shell and type "git --version" again to verify that it was correctly installed
## Add your Git username and set your email
* It is important because every Git commit that you create will use this information
* On your shell, type the following command to add your username
```
git config --global user.name ADD YOUR USERNAME
```
* Then verify that you have the correct username
```
git config --global user.name
```
* To set your email address, type the following command
```
git config --global user.email ADD YOUR EMAIL
```
* To verify that you entered your email correctly, type
```
git config --global user.email
```
* You'll need to do this only once because you are using the "--global" option. It tells Git to always use this information for anything you do on that system. If you want to override this with a different username or email address for specific projects, you can run the command without the "--global" option when you’re in that project
## Check your information
* To view the information that you entered, type
```
git config --global --list
```
......@@ -368,6 +368,9 @@ Make sure to edit the config file to match your setup:
# Change YOUR_SERVER_FQDN to the fully-qualified
# domain name of your host serving GitLab.
# If using Ubuntu default nginx install:
# either remove the default_server from the listen line
# or else rm -f /etc/sites-enabled/default
sudo editor /etc/nginx/sites-available/gitlab
**Note:** If you want to use HTTPS, replace the `gitlab` Nginx config with `gitlab-ssl`. See [Using HTTPS](#using-https) for HTTPS configuration details.
......
......@@ -165,13 +165,18 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
Sometimes during version upgrades you might end up with some wrong CSS or
missing some icons. In that case, try to precompile the assets again.
For Omnibus-packages:
```
sudo gitlab-rake assets:precompile
```
Note that this only applies to source installations and does NOT apply to
omnibus packages.
For installations from source:
```
cd /home/git/gitlab
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
```
For omnibus versions, the unoptimized assets (JavaScript, CSS) are frozen at
the release of upstream GitLab. The omnibus version includes optimized versions
of those assets. Unless you are modifying the JavaScript / CSS code on your
production machine after installing the package, there should be no reason to redo
rake assets:precompile on the production machine. If you suspect that assets
have been corrupted, you should reinstall the omnibus package.
......@@ -61,7 +61,7 @@ Xth: (3 working days before the 22nd)
Xth: (2 working days before the 22nd)
- [ ] Check that everyone is mentioned on the blog post (the reviewer should have done this one working day ago)
- [ ] Check that everyone is mentioned on the blog post using `@all` (the reviewer should have done this one working day ago)
- [ ] Check that MVP is added to the mvp page (source/mvp/index.html in www-gitlab-com)
Xth: (1 working day before the 22nd)
......
......@@ -77,3 +77,31 @@ information.
### Eclipse
How to add your ssh key to Eclipse: http://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
## Tip: Non-default OpenSSH key file names or locations
If, for whatever reason, you decide to specify a non-default location and filename for your Gitlab SSH key pair, you must configure your SSH client to find your Gitlab SSH private key for connections to your Gitlab server (perhaps gitlab.com). For OpenSSH clients, this is handled in the `~/.ssh/config` file with a stanza similar to the following:
```
#
# Main gitlab.com server
#
Host gitlab.com
RSAAuthentication yes
IdentityFile ~/my-ssh-key-directory/my-gitlab-private-key-filename
User mygitlabusername
```
Another example
```
#
# Our company's internal Gitlab server
#
Host my-gitlab.company.com
RSAAuthentication yes
IdentityFile ~/my-ssh-key-directory/company-com-private-key-filename
```
Note in the gitlab.com example above a username was specified to override the default chosen by OpenSSH (your local username). This is only required if your local and remote usernames differ.
Due to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document.
......@@ -39,6 +39,7 @@ Feature: Project Commits Comments
@javascript
Scenario: I can delete a comment
Given I leave a comment like "XML attached"
Then I should see a comment saying "XML attached"
And I delete a comment
Then I should not see a comment saying "XML attached"
......
......@@ -11,7 +11,7 @@ Feature: Project Merge Requests
And I should not see "Feature NS-03" in merge requests
Scenario: I should see rejected merge requests
Given I click link "Rejected"
Given I click link "Closed"
Then I should see "Feature NS-03" in merge requests
And I should not see "Bug NS-04" in merge requests
......
Feature: Project Source Multiselect Blob
Background:
Given I sign in as a user
And I own project "Shop"
And I visit ".gitignore" file in repo
@javascript
Scenario: I click line 1 in file
When I click line 1 in file
Then I should see "L1" as URI fragment
And I should see line 1 highlighted
@javascript
Scenario: I shift-click line 1 in file
When I shift-click line 1 in file
Then I should see "L1" as URI fragment
And I should see line 1 highlighted
@javascript
Scenario: I click line 1 then click line 2 in file
When I click line 1 in file
Then I should see "L1" as URI fragment
And I should see line 1 highlighted
Then I click line 2 in file
Then I should see "L2" as URI fragment
And I should see line 2 highlighted
@javascript
Scenario: I click various line numbers to test multiselect
Then I click line 1 in file
Then I should see "L1" as URI fragment
And I should see line 1 highlighted
Then I shift-click line 2 in file
Then I should see "L1-2" as URI fragment
And I should see lines 1-2 highlighted
Then I shift-click line 3 in file
Then I should see "L1-3" as URI fragment
And I should see lines 1-3 highlighted
Then I click line 3 in file
Then I should see "L3" as URI fragment
And I should see line 3 highlighted
Then I shift-click line 1 in file
Then I should see "L1-3" as URI fragment
And I should see lines 1-3 highlighted
Then I shift-click line 5 in file
Then I should see "L1-5" as URI fragment
And I should see lines 1-5 highlighted
Then I shift-click line 4 in file
Then I should see "L1-4" as URI fragment
And I should see lines 1-4 highlighted
Then I click line 5 in file
Then I should see "L5" as URI fragment
And I should see line 5 highlighted
Then I shift-click line 3 in file
Then I should see "L3-5" as URI fragment
And I should see lines 3-5 highlighted
Then I shift-click line 1 in file
Then I should see "L1-3" as URI fragment
And I should see lines 1-3 highlighted
Then I shift-click line 1 in file
Then I should see "L1" as URI fragment
And I should see line 1 highlighted
@javascript
Scenario: I multiselect lines 1-5 and then go back and forward in history
When I click line 1 in file
And I shift-click line 3 in file
And I shift-click line 2 in file
And I shift-click line 5 in file
Then I should see "L1-5" as URI fragment
And I should see lines 1-5 highlighted
Then I go back in history
Then I should see "L1-2" as URI fragment
And I should see lines 1-2 highlighted
Then I go back in history
Then I should see "L1-3" as URI fragment
And I should see lines 1-3 highlighted
Then I go back in history
Then I should see "L1" as URI fragment
And I should see line 1 highlighted
Then I go forward in history
And I go forward in history
And I go forward in history
Then I should see "L1-5" as URI fragment
And I should see lines 1-5 highlighted
......@@ -69,7 +69,7 @@ Feature: Project Wiki
And I click on the "Pages" button
Then I should see non-escaped link in the pages list
@javascript @focus
@javascript
Scenario: Creating an invalid new page
Given I create a New page with an invalid name
Then I should see an error message
......
......@@ -26,3 +26,14 @@ Feature: Snippets
Given I visit snippet page "Personal snippet one"
And I click link "Destroy"
Then I should not see "Personal snippet one" in snippets
Scenario: I create new internal snippet
Given I logout directly
And I sign in as an admin
Then I visit new snippet page
And I submit new internal snippet
Then I visit snippet page "Internal personal snippet one"
And I logout directly
Then I sign in as a user
Given I visit new snippet page
Then I visit snippet page "Internal personal snippet one"
......@@ -36,6 +36,6 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
step 'I should see a customized broadcast message' do
expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST'
expect(page).to have_selector %(div[style="background-color:#f2dede;color:#b94a48"])
expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"])
end
end
......@@ -11,9 +11,9 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
end
step 'I should see application settings saved' do
expect(current_application_settings.gravatar_enabled).to be_false
expect(current_application_settings.home_page_url).to eq 'https://about.gitlab.com/'
expect(page).to have_content 'Application settings saved successfully'
expect(current_application_settings.gravatar_enabled).to be_falsey
expect(current_application_settings.home_page_url).to eq "https://about.gitlab.com/"
expect(page).to have_content "Application settings saved successfully"
end
step 'I click on "Service Templates"' do
......
......@@ -10,7 +10,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
end
step 'I see "New project" page' do
expect(page).to have_content("Project path")
expect(page).to have_content('Project path')
end
step 'I click on "Import project from GitHub"' do
......
......@@ -154,7 +154,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end
step 'I should not see group "Owned" avatar' do
expect(Group.find_by(name: "Owned").avatar?).to be_false
expect(Group.find_by(name: "Owned").avatar?).to eq false
end
step 'I should not see the "Remove avatar" button' do
......
......@@ -53,7 +53,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see my gravatar' do
expect(@user.avatar?).to be_false
expect(@user.avatar?).to eq false
end
step 'I should not see the "Remove avatar" button' do
......@@ -87,12 +87,16 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step "I should see a missing password error message" do
page.within ".flash-container" do
expect(page).to have_content "You must provide a valid current password"
end
end
step "I should see a password error message" do
page.within '.alert' do
expect(page).to have_content "Password confirmation doesn't match"
end
end
step 'I reset my token' do
page.within '.update-token' do
......@@ -120,7 +124,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step "I am not an ldap user" do
current_user.identities.delete
expect(current_user.ldap_user?).to be_false
expect(current_user.ldap_user?).to eq false
end
step 'I redirected to expired password page' do
......
......@@ -19,8 +19,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
click_link "All"
end
step 'I click link "Rejected"' do
click_link "Rejected"
step 'I click link "Closed"' do
click_link "Closed"
end
step 'I should see merge request "Wiki Feature"' do
......@@ -31,8 +31,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see closed merge request "Bug NS-04"' do
merge_request = MergeRequest.find_by!(title: "Bug NS-04")
expect(merge_request.closed?).to be_true
expect(page).to have_content "Rejected by"
expect(merge_request).to be_closed
expect(page).to have_content "Closed by"
end
step 'I should see merge request "Bug NS-04"' do
......@@ -125,7 +125,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
expect(buttons.count).to eq(2)
buttons.each do |b|
expect(expect(b['href'])).not_to have_content('json')
expect(b['href']).not_to have_content('json')
end
end
......@@ -164,20 +164,26 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see a discussion has started on diff' do
expect(page).to have_content "#{current_user.name} started a discussion"
expect(page).to have_content sample_commit.line_code_path
expect(page).to have_content "Line is wrong"
page.within(".notes .discussion") do
page.should have_content "#{current_user.name} started a discussion"
page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
end
end
step 'I should see a discussion has started on commit diff' do
expect(page).to have_content "#{current_user.name} started a discussion on commit"
expect(page).to have_content sample_commit.line_code_path
expect(page).to have_content "Line is wrong"
page.within(".notes .discussion") do
page.should have_content "#{current_user.name} started a discussion on commit"
page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
end
end
step 'I should see a discussion has started on commit' do
expect(page).to have_content "#{current_user.name} started a discussion on commit"
expect(page).to have_content "One comment to rule them all"
page.within(".notes .discussion") do
page.should have_content "#{current_user.name} started a discussion on commit"
page.should have_content "One comment to rule them all"
end
end
step 'merge request is mergeable' do
......@@ -206,7 +212,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see merged request' do
page.within '.issue-box' do
expect(page).to have_content "Accepted"
expect(page).to have_content "Merged"
end
end
......@@ -329,13 +335,14 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
def leave_comment(message)
page.within(".js-discussion-note-form") do
page.within(".js-discussion-note-form", visible: true) do
fill_in "note_note", with: message
click_button "Add Comment"
end
page.within(".notes_holder", visible: true) do
expect(page).to have_content message
end
end
def init_diff_note_first_file
click_diff_line(sample_compare.changes[0][:line_code])
......
......@@ -59,7 +59,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see the default project avatar' do
expect(@project.avatar?).to be_false
expect(@project.avatar?).to eq false
end
step 'I should not see the "Remove avatar" button' do
......
class Spinach::Features::ProjectSourceMultiselectBlob < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
class << self
def click_line_steps(*line_numbers)
line_numbers.each do |line_number|
step "I click line #{line_number} in file" do
find("#L#{line_number}").click
end
step "I shift-click line #{line_number} in file" do
script = "$('#L#{line_number}').trigger($.Event('click', { shiftKey: true }));"
execute_script(script)
end
end
end
def check_state_steps(*ranges)
ranges.each do |range|
fragment = range.kind_of?(Array) ? "L#{range.first}-#{range.last}" : "L#{range}"
pluralization = range.kind_of?(Array) ? "s" : ""
step "I should see \"#{fragment}\" as URI fragment" do
expect(URI.parse(current_url).fragment).to eq fragment
end
step "I should see line#{pluralization} #{fragment[1..-1]} highlighted" do
ids = Array(range).map { |n| "LC#{n}" }
extra = false
highlighted = page.all("#tree-content-holder .highlight .line.hll")
highlighted.each do |element|
extra ||= ids.delete(element[:id]).nil?
end
expect(extra).to be_false and ids.should be_empty
end
end
end
end
click_line_steps *Array(1..5)
check_state_steps *Array(1..5), Array(1..2), Array(1..3), Array(1..4), Array(1..5), Array(3..5)
step 'I go back in history' do
go_back
end
step 'I go forward in history' do
go_forward
end
step 'I click on ".gitignore" file in repo' do
click_link ".gitignore"
end
end
......@@ -52,7 +52,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end
step 'I should see code results for project "Shop"' do
expect(page).to have_content 'Update capybara, rspec-rails, poltergeist to recent versions'
page.within('.results') do
page.should have_content 'Update capybara, rspec-rails, poltergeist to recent versions'
end
end
step 'I search for "Contibuting"' do
......@@ -71,7 +73,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end
step 'I should see "Foo" link in the search results' do
expect(find(:css, '.search-results')).to have_link 'Foo'
page.within('.results') do
find(:css, '.search-results').should have_link 'Foo'
end
end
step 'I should not see "Bar" link in the search results' do
......@@ -79,7 +83,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end
step 'I should see "test_wiki" link in the search results' do
expect(find(:css, '.search-results')).to have_link 'test_wiki.md'
page.within('.results') do
find(:css, '.search-results').should have_link 'test_wiki.md'
end
end
step 'project has Wiki content' do
......
......@@ -28,6 +28,10 @@ module SharedAuthentication
logout
end
step "I logout directly" do
logout_direct
end
def current_user
@user || User.first
end
......
......@@ -20,13 +20,16 @@ module SharedDiffNote
end
step 'I leave a diff comment like "Typo, please fix"' do
page.within(diff_file_selector) do
click_diff_line(sample_commit.line_code)
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
page.within("form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "Typo, please fix"
find(".js-comment-button").trigger("click")
sleep 0.05
end
end
end
step 'I leave a diff comment in a parallel view on the left side like "Old comment"' do
click_parallel_diff_line(sample_commit.line_code, 'old')
......@@ -45,29 +48,38 @@ module SharedDiffNote
end
step 'I preview a diff comment text like "Should fix it :smile:"' do
page.within(diff_file_selector) do
click_diff_line(sample_commit.line_code)
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
page.within("form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "Should fix it :smile:"
find('.js-md-preview-button').click
end
end
end
step 'I preview another diff comment text like "DRY this up"' do
page.within(diff_file_selector) do
click_diff_line(sample_commit.del_line_code)
page.within("#{diff_file_selector} form[rel$='#{sample_commit.del_line_code}']") do
page.within("form[rel$='#{sample_commit.del_line_code}']") do
fill_in "note[note]", with: "DRY this up"
find('.js-md-preview-button').click
end
end
end
step 'I open a diff comment form' do
page.within(diff_file_selector) do
click_diff_line(sample_commit.line_code)
end
end
step 'I open another diff comment form' do
page.within(diff_file_selector) do
click_diff_line(sample_commit.del_line_code)
end
end
step 'I write a diff comment like ":-1: I don\'t like this"' do
page.within(diff_file_selector) do
......@@ -194,7 +206,7 @@ module SharedDiffNote
end
def diff_file_selector
".diff-file:nth-of-type(1)"
'.diff-file:nth-of-type(1)'
end
def click_diff_line(code)
......
......@@ -2,9 +2,11 @@ module SharedNote
include Spinach::DSL
step 'I delete a comment' do
page.within('.notes') do
find('.note').hover
find(".js-note-delete").click
end
end
step 'I haven\'t written any comment text' do
page.within(".js-main-target-form") do
......@@ -16,7 +18,6 @@ module SharedNote
page.within(".js-main-target-form") do
fill_in "note[note]", with: "XML attached"
click_button "Add Comment"
sleep 0.05
end
end
......@@ -123,13 +124,14 @@ module SharedNote
end
step 'I edit the last comment with a +1' do
page.within(".notes") do
find(".note").hover
find('.js-note-edit').click
end
page.within(".current-note-edit-form") do
fill_in 'note[note]', with: '+1 Awesome!'
click_button 'Save Comment'
sleep 0.05
end
end
......
......@@ -31,6 +31,18 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
click_button "Create snippet"
end
step 'I submit new internal snippet' do
fill_in "personal_snippet_title", :with => "Internal personal snippet one"
fill_in "personal_snippet_file_name", :with => "my_snippet.rb"
choose 'personal_snippet_visibility_level_10'
page.within('.file-editor') do
find(:xpath, "//input[@id='personal_snippet_content']").set 'Content of internal snippet'
end
click_button "Create snippet"
end
step 'I should see snippet "Personal snippet three"' do
expect(page).to have_content "Personal snippet three"
expect(page).to have_content "Content of snippet three"
......@@ -58,7 +70,15 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
visit snippet_path(snippet)
end
step 'I visit snippet page "Internal personal snippet one"' do
visit snippet_path(internal_snippet)
end
def snippet
@snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one")
end
def internal_snippet
@snippet ||= PersonalSnippet.find_by!(title: "Internal personal snippet one")
end
end
......@@ -25,6 +25,7 @@ WebMock.allow_net_connect!
Spinach.hooks.before_run do
include RSpec::Mocks::ExampleMethods
RSpec::Mocks.setup
TestEnv.init(mailer: false)
include FactoryGirl::Syntax::Methods
......
......@@ -23,7 +23,7 @@ module Backup
def backup_existing_uploads_dir
timestamped_uploads_path = File.join(app_uploads_dir, '..', "uploads.#{Time.now.to_i}")
if File.exists?(app_uploads_dir)
FileUtils.mv(app_uploads_dir, timestamped_uploads_path)
FileUtils.mv(app_uploads_dir, File.expand_path(timestamped_uploads_path))
end
end
end
......
......@@ -40,6 +40,10 @@ upstream gitlab {
## Normal HTTP host
server {
## Either remove "default_server" from the listen line below,
## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
## to be served if you visit any address that your server responds to, eg.
## the ip address of the server (http://x.x.x.x/)n 0.0.0.0:80 default_server;
listen 0.0.0.0:80 default_server;
listen [::]:80 default_server;
server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com
......
......@@ -44,6 +44,10 @@ upstream gitlab {
## Redirects all HTTP traffic to the HTTPS host
server {
## Either remove "default_server" from the listen line below,
## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
## to be served if you visit any address that your server responds to, eg.
## the ip address of the server (http://x.x.x.x/)
listen 0.0.0.0:80;
listen [::]:80 ipv6only=on default_server;
server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com
......
public/apple-touch-icon-precomposed.png

11.7 KB | W: | H:

public/apple-touch-icon-precomposed.png

10.8 KB | W: | H:

public/apple-touch-icon-precomposed.png
public/apple-touch-icon-precomposed.png
public/apple-touch-icon-precomposed.png
public/apple-touch-icon-precomposed.png
  • 2-up
  • Swipe
  • Onion skin
public/apple-touch-icon.png

11.7 KB | W: | H:

public/apple-touch-icon.png

10.8 KB | W: | H:

public/apple-touch-icon.png
public/apple-touch-icon.png
public/apple-touch-icon.png
public/apple-touch-icon.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -7,7 +7,7 @@
<body>
<h1>
<img src="/gitlab_logo.png" /><br />
<img src="/logo.svg" /><br />
Deploy in progress
</h1>
<h3>Please try again in a few minutes.</h3>
......
public/favicon.ico

32.2 KB | W: | H:

public/favicon.ico

5.3 KB | W: | H:

public/favicon.ico
public/favicon.ico
public/favicon.ico
public/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="210px" height="210px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
<title>Slice 1</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
<g id="Page-1" sketch:type="MSShapeGroup">
<g id="Fill-1-+-Group-24">
<g id="Group-24">
<g id="Group">
<path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329"></path>
<path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26"></path>
<path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326"></path>
<path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329"></path>
<path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26"></path>
<path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326"></path>
<path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -16,7 +16,7 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) }
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq(1) }
it { expect(body.size).to eq 1 }
it { expect(body.first["username"]).to eq user.username }
end
......@@ -33,7 +33,7 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) }
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq(1) }
it { expect(body.size).to eq 1 }
it { expect(body.first["username"]).to eq user.username }
end
......@@ -46,6 +46,6 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) }
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq(User.count) }
it { expect(body.size).to eq User.count }
end
end
......@@ -40,10 +40,10 @@ describe Projects::CommitController do
get(:show, namespace_id: project.namespace.to_param,
project_id: project.to_param, id: commit.id, format: format)
expect(response.body).to_not include('&amp;')
expect(response.body).to_not include('&gt;')
expect(response.body).to_not include('&lt;')
expect(response.body).to_not include('&quot;')
expect(response.body).not_to include('&amp;')
expect(response.body).not_to include('&gt;')
expect(response.body).not_to include('&lt;')
expect(response.body).not_to include('&quot;')
end
end
......
require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::BitbucketController do
include ImportSpecHelper
let(:user) { create(:user, bitbucket_access_token: 'asd123', bitbucket_access_token_secret: "sekret") }
before do
sign_in(user)
controller.stub(:bitbucket_import_enabled?).and_return(true)
allow(controller).to receive(:bitbucket_import_enabled?).and_return(true)
end
describe "GET callback" do
......@@ -17,8 +20,9 @@ describe Import::BitbucketController do
token = "asdasd12345"
secret = "sekrettt"
access_token = double(token: token, secret: secret)
Gitlab::BitbucketImport::Client.any_instance.stub(:get_token).and_return(access_token)
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket")
allow_any_instance_of(Gitlab::BitbucketImport::Client).
to receive(:get_token).and_return(access_token)
stub_omniauth_provider('bitbucket')
get :callback
......@@ -35,7 +39,7 @@ describe Import::BitbucketController do
it "assigns variables" do
@project = create(:project, import_type: 'bitbucket', creator_id: user.id)
controller.stub_chain(:client, :projects).and_return([@repo])
stub_client(projects: [@repo])
get :status
......@@ -45,7 +49,7 @@ describe Import::BitbucketController do
it "does not show already added project" do
@project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim')
controller.stub_chain(:client, :projects).and_return([@repo])
stub_client(projects: [@repo])
get :status
......@@ -77,8 +81,7 @@ describe Import::BitbucketController do
to receive(:new).with(bitbucket_repo, user).
and_return(double(execute: true))
controller.stub_chain(:client, :user).and_return(bitbucket_user)
controller.stub_chain(:client, :project).and_return(bitbucket_repo)
stub_client(user: bitbucket_user, project: bitbucket_repo)
end
context "when the repository owner is the Bitbucket user" do
......
require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GithubController do
include ImportSpecHelper
let(:user) { create(:user, github_access_token: 'asd123') }
before do
sign_in(user)
controller.stub(:github_import_enabled?).and_return(true)
allow(controller).to receive(:github_import_enabled?).and_return(true)
end
describe "GET callback" do
......@@ -13,9 +16,7 @@ describe Import::GithubController do
token = "asdasd12345"
allow_any_instance_of(Gitlab::GithubImport::Client).
to receive(:get_token).and_return(token)
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: 'asd123',
app_secret: 'asd123',
name: 'github')
stub_omniauth_provider('github')
get :callback
......@@ -33,9 +34,7 @@ describe Import::GithubController do
it "assigns variables" do
@project = create(:project, import_type: 'github', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo])
controller.stub_chain(:client, :orgs).and_return([@org])
controller.stub_chain(:client, :org_repos).with(@org.login).and_return([@org_repo])
stub_client(repos: [@repo], orgs: [@org], org_repos: [@org_repo])
get :status
......@@ -45,8 +44,7 @@ describe Import::GithubController do
it "does not show already added project" do
@project = create(:project, import_type: 'github', creator_id: user.id, import_source: 'asd/vim')
controller.stub_chain(:client, :repos).and_return([@repo])
controller.stub_chain(:client, :orgs).and_return([])
stub_client(repos: [@repo], orgs: [])
get :status
......@@ -67,8 +65,7 @@ describe Import::GithubController do
}
before do
controller.stub_chain(:client, :user).and_return(github_user)
controller.stub_chain(:client, :repo).and_return(github_repo)
stub_client(user: github_user, repo: github_repo)
end
context "when the repository owner is the GitHub user" do
......
require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GitlabController do
include ImportSpecHelper
let(:user) { create(:user, gitlab_access_token: 'asd123') }
before do
sign_in(user)
controller.stub(:gitlab_import_enabled?).and_return(true)
allow(controller).to receive(:gitlab_import_enabled?).and_return(true)
end
describe "GET callback" do
it "updates access token" do
token = "asdasd12345"
Gitlab::GitlabImport::Client.any_instance.stub_chain(:client, :auth_code, :get_token, :token).and_return(token)
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "gitlab")
allow_any_instance_of(Gitlab::GitlabImport::Client).
to receive(:get_token).and_return(token)
stub_omniauth_provider('gitlab')
get :callback
......@@ -28,7 +32,7 @@ describe Import::GitlabController do
it "assigns variables" do
@project = create(:project, import_type: 'gitlab', creator_id: user.id)
controller.stub_chain(:client, :projects).and_return([@repo])
stub_client(projects: [@repo])
get :status
......@@ -38,7 +42,7 @@ describe Import::GitlabController do
it "does not show already added project" do
@project = create(:project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim')
controller.stub_chain(:client, :projects).and_return([@repo])
stub_client(projects: [@repo])
get :status
......@@ -66,8 +70,7 @@ describe Import::GitlabController do
}
before do
controller.stub_chain(:client, :user).and_return(gitlab_user)
controller.stub_chain(:client, :project).and_return(gitlab_repo)
stub_client(user: gitlab_user, project: gitlab_repo)
end
context "when the repository owner is the GitLab.com user" do
......
require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GitoriousController do
include ImportSpecHelper
let(:user) { create(:user) }
before do
......@@ -30,7 +33,7 @@ describe Import::GitoriousController do
it "assigns variables" do
@project = create(:project, import_type: 'gitorious', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo])
stub_client(repos: [@repo])
get :status
......@@ -40,7 +43,7 @@ describe Import::GitoriousController do
it "does not show already added project" do
@project = create(:project, import_type: 'gitorious', creator_id: user.id, import_source: 'asd/vim')
controller.stub_chain(:client, :repos).and_return([@repo])
stub_client(repos: [@repo])
get :status
......@@ -59,7 +62,7 @@ describe Import::GitoriousController do
expect(Gitlab::GitoriousImport::ProjectCreator).
to receive(:new).with(@repo, namespace, user).
and_return(double(execute: true))
controller.stub_chain(:client, :repo).and_return(@repo)
stub_client(repo: @repo)
post :create, format: :js
end
......
require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GoogleCodeController do
include ImportSpecHelper
let(:user) { create(:user) }
let(:dump_file) { fixture_file_upload(Rails.root + 'spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') }
......@@ -21,13 +24,12 @@ describe Import::GoogleCodeController do
describe "GET status" do
before do
@repo = OpenStruct.new(name: 'vim')
controller.stub_chain(:client, :valid?).and_return(true)
stub_client(valid?: true)
end
it "assigns variables" do
@project = create(:project, import_type: 'google_code', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo])
controller.stub_chain(:client, :incompatible_repos).and_return([])
stub_client(repos: [@repo], incompatible_repos: [])
get :status
......@@ -38,8 +40,7 @@ describe Import::GoogleCodeController do
it "does not show already added project" do
@project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
controller.stub_chain(:client, :repos).and_return([@repo])
controller.stub_chain(:client, :incompatible_repos).and_return([])
stub_client(repos: [@repo], incompatible_repos: [])
get :status
......@@ -48,8 +49,7 @@ describe Import::GoogleCodeController do
end
it "does not show any invalid projects" do
controller.stub_chain(:client, :repos).and_return([])
controller.stub_chain(:client, :incompatible_repos).and_return([@repo])
stub_client(repos: [], incompatible_repos: [@repo])
get :status
......
require 'ostruct'
# Helper methods for controller specs in the Import namespace
#
# Must be included manually.
module ImportSpecHelper
# Stub `controller` to return a null object double with the provided messages
# when `client` is called
#
# Examples:
#
# stub_client(foo: %w(foo))
#
# controller.client.foo # => ["foo"]
# controller.client.bar.baz.foo # => ["foo"]
#
# Returns the client double
def stub_client(messages = {})
client = double('client', messages).as_null_object
allow(controller).to receive(:client).and_return(client)
client
end
def stub_omniauth_provider(name)
provider = OpenStruct.new(
name: name,
app_id: 'asd123',
app_secret: 'asd123'
)
Gitlab.config.omniauth.providers << provider
end
end
......@@ -40,11 +40,11 @@ describe Profiles::TwoFactorAuthsController do
expect(user).to receive(:valid_otp?).with(pin).and_return(true)
end
it 'sets otp_required_for_login' do
it 'sets two_factor_enabled' do
go
user.reload
expect(user.otp_required_for_login).to eq true
expect(user).to be_two_factor_enabled
end
it 'presents plaintext codes for the user to save' do
......@@ -109,13 +109,13 @@ describe Profiles::TwoFactorAuthsController do
let!(:codes) { user.generate_otp_backup_codes! }
it 'clears all 2FA-related fields' do
expect(user.otp_required_for_login).to eq true
expect(user).to be_two_factor_enabled
expect(user.otp_backup_codes).not_to be_nil
expect(user.encrypted_otp_secret).not_to be_nil
delete :destroy
expect(user.otp_required_for_login).to eq false
expect(user).not_to be_two_factor_enabled
expect(user.otp_backup_codes).to be_nil
expect(user.encrypted_otp_secret).to be_nil
end
......
......@@ -40,10 +40,10 @@ describe Projects::MergeRequestsController do
get(:show, namespace_id: project.namespace.to_param,
project_id: project.to_param, id: merge_request.iid, format: format)
expect(response.body).to_not include('&amp;')
expect(response.body).to_not include('&gt;')
expect(response.body).to_not include('&lt;')
expect(response.body).to_not include('&quot;')
expect(response.body).not_to include('&amp;')
expect(response.body).not_to include('&gt;')
expect(response.body).not_to include('&lt;')
expect(response.body).not_to include('&quot;')
end
end
......@@ -79,8 +79,32 @@ describe Projects::MergeRequestsController do
end
end
context '#diffs with forked projects with submodules' do
describe 'GET diffs' do
def go(format: 'html')
get :diffs, namespace_id: project.namespace.to_param,
project_id: project.to_param, id: merge_request.iid, format: format
end
context 'as html' do
it 'renders the diff template' do
go
expect(response).to render_template('diffs')
end
end
context 'as json' do
it 'renders the diffs template to a string' do
go format: 'json'
expect(response).to render_template('projects/merge_requests/show/_diffs')
expect(JSON.parse(response.body)).to have_key('html')
end
end
context 'with forked projects with submodules' do
render_views
let(:project) { create(:project) }
let(:fork_project) { create(:forked_project_with_submodules) }
let(:merge_request) { create(:merge_request_with_diffs, source_project: fork_project, source_branch: 'add-submodule-version-bump', target_branch: 'master', target_project: project) }
......@@ -91,11 +115,36 @@ describe Projects::MergeRequestsController do
merge_request.reload
end
it '#diffs' do
get(:diffs, namespace_id: project.namespace.to_param,
project_id: project.to_param, id: merge_request.iid, format: 'json')
it 'renders' do
go format: 'json'
expect(response).to be_success
expect(response.body).to have_content('Subproject commit')
end
end
end
describe 'GET commits' do
def go(format: 'html')
get :commits, namespace_id: project.namespace.to_param,
project_id: project.to_param, id: merge_request.iid, format: format
end
context 'as html' do
it 'renders the show template' do
go
expect(response).to render_template('show')
end
end
context 'as json' do
it 'renders the commits template to a string' do
go format: 'json'
expect(response).to render_template('projects/merge_requests/show/_commits')
expect(JSON.parse(response.body)).to have_key('html')
end
end
end
end
......@@ -30,7 +30,7 @@ FactoryGirl.define do
trait :two_factor do
before(:create) do |user|
user.otp_required_for_login = true
user.two_factor_enabled = true
user.otp_secret = User.generate_otp_secret(32)
end
end
......
......@@ -63,15 +63,35 @@ describe "Admin::Users", feature: true do
end
describe "GET /admin/users/:id" do
before do
it "should have user info" do
visit admin_users_path
click_link "#{@user.name}"
end
click_link @user.name
it "should have user info" do
expect(page).to have_content(@user.email)
expect(page).to have_content(@user.name)
end
describe 'Two-factor Authentication status' do
it 'shows when enabled' do
@user.update_attribute(:two_factor_enabled, true)
visit admin_user_path(@user)
expect_two_factor_status('Enabled')
end
it 'shows when disabled' do
visit admin_user_path(@user)
expect_two_factor_status('Disabled')
end
def expect_two_factor_status(status)
page.within('.two-factor-status') do
expect(page).to have_content(status)
end
end
end
end
describe "GET /admin/users/:id/edit" do
......
......@@ -11,7 +11,8 @@ describe "GitLab Flavored Markdown", feature: true do
end
before do
Commit.any_instance.stub(title: "fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
allow_any_instance_of(Commit).to receive(:title).
and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
end
let(:commit) { project.commit }
......
......@@ -38,7 +38,7 @@ describe 'Issues', feature: true do
it 'does not change issue count' do
expect {
click_button 'Save changes'
}.to_not change { Issue.count }
}.not_to change { Issue.count }
end
it 'should update issue fields' do
......
......@@ -9,7 +9,8 @@ describe 'Profile account page', feature: true do
describe 'when signup is enabled' do
before do
ApplicationSetting.any_instance.stub(signup_enabled?: true)
allow_any_instance_of(ApplicationSetting).
to receive(:signup_enabled?).and_return(true)
visit profile_account_path
end
......@@ -23,7 +24,8 @@ describe 'Profile account page', feature: true do
describe 'when signup is disabled' do
before do
ApplicationSetting.any_instance.stub(signup_enabled?: false)
allow_any_instance_of(ApplicationSetting).
to receive(:signup_enabled?).and_return(false)
visit profile_account_path
end
......
......@@ -75,7 +75,7 @@ describe 'Profile > Preferences' do
end
def expect_preferences_saved_message
within('.flash-container') do
page.within('.flash-container') do
expect(page).to have_content('Preferences saved.')
end
end
......
......@@ -6,7 +6,7 @@ describe "Profile access", feature: true do
end
describe "GET /login" do
it { expect(new_user_session_path).not_to be_404_for :visitor }
it { expect(new_user_session_path).not_to be_not_found_for :visitor }
end
describe "GET /profile/keys" do
......
......@@ -76,8 +76,8 @@ describe ApplicationHelper do
end
it 'should return an url for the avatar with relative url' do
Gitlab.config.gitlab.stub(relative_url_root: '/gitlab')
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab')
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
user = create(:user)
user.avatar = File.open(avatar_file_path)
......@@ -97,7 +97,7 @@ describe ApplicationHelper do
let(:user_email) { 'user@email.com' }
it 'should return a generic avatar path when Gravatar is disabled' do
ApplicationSetting.any_instance.stub(gravatar_enabled?: false)
allow_any_instance_of(ApplicationSetting).to receive(:gravatar_enabled?).and_return(false)
expect(gravatar_icon(user_email)).to match('no_avatar.png')
end
......@@ -106,13 +106,13 @@ describe ApplicationHelper do
end
it 'should return default gravatar url' do
Gitlab.config.gitlab.stub(https: false)
allow(Gitlab.config.gitlab).to receive(:https).and_return(false)
url = 'http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118'
expect(gravatar_icon(user_email)).to match(url)
end
it 'should use SSL when appropriate' do
Gitlab.config.gitlab.stub(https: true)
allow(Gitlab.config.gitlab).to receive(:https).and_return(true)
expect(gravatar_icon(user_email)).to match('https://secure.gravatar.com')
end
......@@ -203,40 +203,52 @@ describe ApplicationHelper do
end
end
describe 'link_to' do
it 'should not include rel=nofollow for internal links' do
expect(link_to('Home', root_path)).to eq('<a href="/">Home</a>')
describe 'time_ago_with_tooltip' do
def element(*arguments)
Time.zone = 'UTC'
time = Time.zone.parse('2015-07-02 08:00')
element = time_ago_with_tooltip(time, *arguments)
Nokogiri::HTML::DocumentFragment.parse(element).first_element_child
end
it 'returns a time element' do
expect(element.name).to eq 'time'
end
it 'includes the date string' do
expect(element.text).to eq '2015-07-02 08:00:00 UTC'
end
it 'has a datetime attribute' do
expect(element.attr('datetime')).to eq '2015-07-02T08:00:00Z'
end
it 'should include rel=nofollow for external links' do
expect(link_to('Example', 'http://www.example.com')).
to eq '<a href="http://www.example.com" rel="nofollow">Example</a>'
it 'has a formatted title attribute' do
expect(element.attr('title')).to eq 'Jul 02, 2015 8:00am'
end
it 'should include rel=nofollow for external links and honor existing html_options' do
expect(link_to('Example', 'http://www.example.com', class: 'toggle', data: {toggle: 'dropdown'}))
.to eq '<a class="toggle" data-toggle="dropdown" href="http://www.example.com" rel="nofollow">Example</a>'
it 'includes a default js-timeago class' do
expect(element.attr('class')).to eq 'time_ago js-timeago'
end
it 'should include rel=nofollow for external links and preserve other rel values' do
expect(link_to('Example', 'http://www.example.com', rel: 'noreferrer'))
.to eq '<a href="http://www.example.com" rel="noreferrer nofollow">Example</a>'
it 'accepts a custom html_class' do
expect(element(html_class: 'custom_class').attr('class')).to eq 'custom_class js-timeago'
end
it 'should not include rel=nofollow for external links on the same host as GitLab' do
expect(Gitlab.config.gitlab).to receive(:host).and_return('example.foo')
expect(link_to('Example', 'http://example.foo/bar')).
to eq '<a href="http://example.foo/bar">Example</a>'
it 'accepts a custom tooltip placement' do
expect(element(placement: 'bottom').attr('data-placement')).to eq 'bottom'
end
it 'should not raise an error when given a bad URI' do
expect { link_to('default', 'if real=1 RANDOM; if real>1 IDLHS; if real>500 LHS') }.
not_to raise_error
it 're-initializes timeago Javascript' do
el = element.next_element
expect(el.name).to eq 'script'
expect(el.text).to include "$('.js-timeago').timeago()"
end
it 'should not raise an error when given a bad mailto URL' do
expect { link_to('email', 'mailto://foo.bar@example.es?subject=Subject%20Line') }.
not_to raise_error
it 'allows the script tag to be excluded' do
expect(element(skip_js: true)).not_to include 'script'
end
end
......
......@@ -2,20 +2,20 @@ require 'spec_helper'
describe BroadcastMessagesHelper do
describe 'broadcast_styling' do
let(:broadcast_message) { double(color: "", font: "") }
let(:broadcast_message) { double(color: '', font: '') }
context "default style" do
it "should have no style" do
expect(broadcast_styling(broadcast_message)).to match('')
expect(broadcast_styling(broadcast_message)).to eq ''
end
end
context "customiezd style" do
before { broadcast_message.stub(color: "#f2dede", font: "#b94a48") }
context "customized style" do
let(:broadcast_message) { double(color: "#f2dede", font: '#b94a48') }
it "should have a customized style" do
expect(broadcast_styling(broadcast_message)).
to match('background-color:#f2dede;color:#b94a48')
to match('background-color: #f2dede; color: #b94a48')
end
end
end
......
......@@ -48,19 +48,19 @@ describe DiffHelper do
end
it 'should return only the first file if the diff line count in the 2nd file takes the total beyond safe limits' do
diffs[1].diff.stub(lines: [""] * 4999) #simulate 4999 lines
allow(diffs[1].diff).to receive(:lines).and_return([""] * 4999) #simulate 4999 lines
expect(safe_diff_files(diffs).length).to eq(1)
end
it 'should return all files from a commit that is beyond safe limit for numbers of lines if force diff is true' do
allow(controller).to receive(:params) { { force_show_diff: true } }
diffs[1].diff.stub(lines: [""] * 4999) #simulate 4999 lines
allow(diffs[1].diff).to receive(:lines).and_return([""] * 4999) #simulate 4999 lines
expect(safe_diff_files(diffs).length).to eq(2)
end
it 'should return only the first file if the diff line count in the 2nd file takes the total beyond hard limits' do
allow(controller).to receive(:params) { { force_show_diff: true } }
diffs[1].diff.stub(lines: [""] * 49999) #simulate 49999 lines
allow(diffs[1].diff).to receive(:lines).and_return([""] * 49999) #simulate 49999 lines
expect(safe_diff_files(diffs).length).to eq(1)
end
......
require 'spec_helper'
describe NotificationsHelper do
include FontAwesome::Rails::IconHelper
include IconsHelper
describe 'notification_icon' do
let(:notification) { double(disabled?: false, participating?: false, watch?: false) }
context "disabled notification" do
before { notification.stub(disabled?: true) }
before { allow(notification).to receive(:disabled?).and_return(true) }
it "has a red icon" do
expect(notification_icon(notification)).to match('class="fa fa-volume-off ns-mute"')
......@@ -16,7 +13,7 @@ describe NotificationsHelper do
end
context "participating notification" do
before { notification.stub(participating?: true) }
before { allow(notification).to receive(:participating?).and_return(true) }
it "has a blue icon" do
expect(notification_icon(notification)).to match('class="fa fa-volume-down ns-part"')
......@@ -24,7 +21,7 @@ describe NotificationsHelper do
end
context "watched notification" do
before { notification.stub(watch?: true) }
before { allow(notification).to receive(:watch?).and_return(true) }
it "has a green icon" do
expect(notification_icon(notification)).to match('class="fa fa-volume-up ns-watch"')
......
......@@ -14,41 +14,41 @@ describe SubmoduleHelper do
context 'submodule on self' do
before do
Gitlab.config.gitlab.stub(protocol: 'http') # set this just to be sure
allow(Gitlab.config.gitlab).to receive(:protocol).and_return('http') # set this just to be sure
end
it 'should detect ssh on standard port' do
Gitlab.config.gitlab_shell.stub(ssh_port: 22) # set this just to be sure
Gitlab.config.gitlab_shell.stub(ssh_path_prefix: Settings.send(:build_gitlab_shell_ssh_path_prefix))
allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(22) # set this just to be sure
allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix))
stub_url([ config.user, '@', config.host, ':gitlab-org/gitlab-ce.git' ].join(''))
expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should detect ssh on non-standard port' do
Gitlab.config.gitlab_shell.stub(ssh_port: 2222)
Gitlab.config.gitlab_shell.stub(ssh_path_prefix: Settings.send(:build_gitlab_shell_ssh_path_prefix))
allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(2222)
allow(Gitlab.config.gitlab_shell).to receive(:ssh_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix))
stub_url([ 'ssh://', config.user, '@', config.host, ':2222/gitlab-org/gitlab-ce.git' ].join(''))
expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should detect http on standard port' do
Gitlab.config.gitlab.stub(port: 80)
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
allow(Gitlab.config.gitlab).to receive(:port).and_return(80)
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
stub_url([ 'http://', config.host, '/gitlab-org/gitlab-ce.git' ].join(''))
expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should detect http on non-standard port' do
Gitlab.config.gitlab.stub(port: 3000)
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
allow(Gitlab.config.gitlab).to receive(:port).and_return(3000)
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
stub_url([ 'http://', config.host, ':3000/gitlab-org/gitlab-ce.git' ].join(''))
expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
it 'should work with relative_url_root' do
Gitlab.config.gitlab.stub(port: 80) # set this just to be sure
Gitlab.config.gitlab.stub(relative_url_root: '/gitlab/root')
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
allow(Gitlab.config.gitlab).to receive(:port).and_return(80) # set this just to be sure
allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
stub_url([ 'http://', config.host, '/gitlab/root/gitlab-org/gitlab-ce.git' ].join(''))
expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end
......@@ -156,6 +156,6 @@ describe SubmoduleHelper do
end
def stub_url(url)
repo.stub(submodule_url_for: url)
allow(repo).to receive(:submodule_url_for).and_return(url)
end
end
#tree-content-holder
.file-content
.line-numbers
- 1.upto(25) do |i|
%a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}= i
%pre.code.highlight
%code
- 1.upto(25) do |i|
%span.line{id: "LC#{i}"}= "Line #{i}"
%ul.nav.nav-tabs.merge-request-tabs
%li.notes-tab
%a{href: '/foo/bar/merge_requests/1', data: {target: '#notes', action: 'notes', toggle: 'tab'}}
Discussion
%li.commits-tab
%a{href: '/foo/bar/merge_requests/1/commits', data: {target: '#commits', action: 'commits', toggle: 'tab'}}
Commits
%li.diffs-tab
%a{href: '/foo/bar/merge_requests/1/diffs', data: {target: '#diffs', action: 'diffs', toggle: 'tab'}}
Diffs
.tab-content
#notes.notes.tab-pane
Notes Content
#commits.commits.tab-pane
Commits Content
#diffs.diffs.tab-pane
Diffs Content
.mr-loading-status
.loading
Loading Animation
#= require line_highlighter
describe 'LineHighlighter', ->
fixture.preload('line_highlighter.html')
clickLine = (number, eventData = {}) ->
if $.isEmptyObject(eventData)
$("#L#{number}").mousedown().click()
else
e = $.Event 'mousedown', eventData
$("#L#{number}").trigger(e).click()
beforeEach ->
fixture.load('line_highlighter.html')
@class = new LineHighlighter()
@css = @class.highlightClass
@spies = {
__setLocationHash__: spyOn(@class, '__setLocationHash__').and.callFake ->
}
describe 'behavior', ->
it 'highlights one line given in the URL hash', ->
new LineHighlighter('#L13')
expect($('#LC13')).toHaveClass(@css)
it 'highlights a range of lines given in the URL hash', ->
new LineHighlighter('#L5-25')
expect($(".#{@css}").length).toBe(21)
expect($("#LC#{line}")).toHaveClass(@css) for line in [5..25]
it 'scrolls to the first highlighted line on initial load', ->
spy = spyOn($, 'scrollTo')
new LineHighlighter('#L5-25')
expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything())
it 'discards click events', ->
spy = spyOnEvent('a[data-line-number]', 'click')
clickLine(13)
expect(spy).toHaveBeenPrevented()
it 'handles garbage input from the hash', ->
func = -> new LineHighlighter('#tree-content-holder')
expect(func).not.toThrow()
describe '#clickHandler', ->
it 'discards the mousedown event', ->
spy = spyOnEvent('a[data-line-number]', 'mousedown')
clickLine(13)
expect(spy).toHaveBeenPrevented()
describe 'without shiftKey', ->
it 'highlights one line when clicked', ->
clickLine(13)
expect($('#LC13')).toHaveClass(@css)
it 'unhighlights previously highlighted lines', ->
clickLine(13)
clickLine(20)
expect($('#LC13')).not.toHaveClass(@css)
expect($('#LC20')).toHaveClass(@css)
it 'sets the hash', ->
spy = spyOn(@class, 'setHash').and.callThrough()
clickLine(13)
expect(spy).toHaveBeenCalledWith(13)
describe 'with shiftKey', ->
it 'sets the hash', ->
spy = spyOn(@class, 'setHash').and.callThrough()
clickLine(13)
clickLine(20, shiftKey: true)
expect(spy).toHaveBeenCalledWith(13)
expect(spy).toHaveBeenCalledWith(13, 20)
describe 'without existing highlight', ->
it 'highlights the clicked line', ->
clickLine(13, shiftKey: true)
expect($('#LC13')).toHaveClass(@css)
expect($(".#{@css}").length).toBe(1)
it 'sets the hash', ->
spy = spyOn(@class, 'setHash')
clickLine(13, shiftKey: true)
expect(spy).toHaveBeenCalledWith(13)
describe 'with existing single-line highlight', ->
it 'uses existing line as last line when target is lesser', ->
clickLine(20)
clickLine(15, shiftKey: true)
expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass(@css) for line in [15..20]
it 'uses existing line as first line when target is greater', ->
clickLine(5)
clickLine(10, shiftKey: true)
expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass(@css) for line in [5..10]
describe 'with existing multi-line highlight', ->
beforeEach ->
clickLine(10, shiftKey: true)
clickLine(13, shiftKey: true)
it 'uses target as first line when it is less than existing first line', ->
clickLine(5, shiftKey: true)
expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass(@css) for line in [5..10]
it 'uses target as last line when it is greater than existing first line', ->
clickLine(15, shiftKey: true)
expect($(".#{@css}").length).toBe(6)
expect($("#LC#{line}")).toHaveClass(@css) for line in [10..15]
describe '#hashToRange', ->
beforeEach ->
@subject = @class.hashToRange
it 'extracts a single line number from the hash', ->
expect(@subject('#L5')).toEqual([5, null])
it 'extracts a range of line numbers from the hash', ->
expect(@subject('#L5-15')).toEqual([5, 15])
it 'returns [null, null] when the hash is not a line number', ->
expect(@subject('#foo')).toEqual([null, null])
describe '#highlightLine', ->
beforeEach ->
@subject = @class.highlightLine
it 'highlights the specified line', ->
@subject(13)
expect($('#LC13')).toHaveClass(@css)
it 'accepts a String-based number', ->
@subject('13')
expect($('#LC13')).toHaveClass(@css)
describe '#setHash', ->
beforeEach ->
@subject = @class.setHash
it 'sets the location hash for a single line', ->
@subject(5)
expect(@spies.__setLocationHash__).toHaveBeenCalledWith('#L5')
it 'sets the location hash for a range', ->
@subject(5, 15)
expect(@spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15')
#= require merge_request_tabs
describe 'MergeRequestTabs', ->
stubLocation = (stubs) ->
defaults = {pathname: '', search: '', hash: ''}
$.extend(defaults, stubs)
fixture.preload('merge_request_tabs.html')
beforeEach ->
@class = new MergeRequestTabs()
@spies = {
ajax: spyOn($, 'ajax').and.callFake ->
history: spyOn(history, 'replaceState').and.callFake ->
}
describe '#activateTab', ->
beforeEach ->
fixture.load('merge_request_tabs.html')
@subject = @class.activateTab
it 'shows the first tab when action is show', ->
@subject('show')
expect($('#notes')).toHaveClass('active')
it 'shows the notes tab when action is notes', ->
@subject('notes')
expect($('#notes')).toHaveClass('active')
it 'shows the commits tab when action is commits', ->
@subject('commits')
expect($('#commits')).toHaveClass('active')
it 'shows the diffs tab when action is diffs', ->
@subject('diffs')
expect($('#diffs')).toHaveClass('active')
describe '#setCurrentAction', ->
beforeEach ->
@subject = @class.setCurrentAction
it 'changes from commits', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/commits')
expect(@subject('notes')).toBe('/foo/bar/merge_requests/1')
expect(@subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs')
it 'changes from diffs', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/diffs')
expect(@subject('notes')).toBe('/foo/bar/merge_requests/1')
expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits')
it 'changes from notes', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1')
expect(@subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs')
expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits')
it 'includes search parameters and hash string', ->
@class._location = stubLocation({
pathname: '/foo/bar/merge_requests/1/diffs'
search: '?view=parallel'
hash: '#L15-35'
})
expect(@subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35')
it 'replaces the current history state', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1')
new_state = @subject('commits')
expect(@spies.history).toHaveBeenCalledWith(
{turbolinks: true, url: new_state},
document.title,
new_state
)
it 'treats "show" like "notes"', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/commits')
expect(@subject('show')).toBe('/foo/bar/merge_requests/1')
......@@ -9,8 +9,11 @@ describe ExtractsPath do
before do
@project = project
project.stub(repository: double(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0']))
project.stub(path_with_namespace: 'gitlab/gitlab-ci')
repo = double(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0'])
allow(project).to receive(:repository).and_return(repo)
allow(project).to receive(:path_with_namespace).
and_return('gitlab/gitlab-ci')
end
describe '#assign_ref' do
......
......@@ -27,16 +27,18 @@ describe Gitlab::Auth do
it "should not find user with invalid password" do
password = 'wrong'
expect( gl_auth.find(username, password) ).to_not eql user
expect( gl_auth.find(username, password) ).not_to eql user
end
it "should not find user with invalid login" do
user = 'wrong'
expect( gl_auth.find(username, password) ).to_not eql user
expect( gl_auth.find(username, password) ).not_to eql user
end
context "with ldap enabled" do
before { Gitlab::LDAP::Config.stub(enabled?: true) }
before do
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
end
it "tries to autheticate with db before ldap" do
expect(Gitlab::LDAP::Authentication).not_to receive(:login)
......
......@@ -5,7 +5,7 @@ describe Gitlab::Shell do
let(:gitlab_shell) { Gitlab::Shell.new }
before do
Project.stub(find: project)
allow(Project).to receive(:find).and_return(project)
end
it { is_expected.to respond_to :add_key }
......
......@@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Client do
let(:raw_data) { "No clue" }
it "returns true" do
expect(subject).to_not be_valid
expect(subject).not_to be_valid
end
end
end
......
......@@ -25,7 +25,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject.execute
%w(New NeedInfo Accepted Wishlist Started Fixed Invalid Duplicate WontFix Incomplete).each do |status|
expect(project.labels.find_by(name: "Status: #{status}")).to_not be_nil
expect(project.labels.find_by(name: "Status: #{status}")).not_to be_nil
end
end
......@@ -39,7 +39,7 @@ describe Gitlab::GoogleCodeImport::Importer do
Component-Systray Component-Clock Component-Launcher Component-Tint2conf Component-Docs Component-New
).each do |label|
label.sub!("-", ": ")
expect(project.labels.find_by(name: label)).to_not be_nil
expect(project.labels.find_by(name: label)).not_to be_nil
end
end
......@@ -47,7 +47,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject.execute
issue = project.issues.first
expect(issue).to_not be_nil
expect(issue).not_to be_nil
expect(issue.iid).to eq(169)
expect(issue.author).to eq(project.creator)
expect(issue.assignee).to eq(mapped_user)
......@@ -72,7 +72,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject.execute
note = project.issues.first.notes.first
expect(note).to_not be_nil
expect(note).not_to be_nil
expect(note.note).to include("Comment 1")
expect(note.note).to include("@#{mapped_user.username}")
expect(note.note).to include("November 18, 2009 05:14")
......
......@@ -8,16 +8,24 @@ describe Gitlab::LDAP::Access do
subject { access.allowed? }
context 'when the user cannot be found' do
before { Gitlab::LDAP::Person.stub(find_by_dn: nil) }
before do
allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(nil)
end
it { is_expected.to be_falsey }
end
context 'when the user is found' do
before { Gitlab::LDAP::Person.stub(find_by_dn: :ldap_user) }
before do
allow(Gitlab::LDAP::Person).
to receive(:find_by_dn).and_return(:ldap_user)
end
context 'and the user is disabled via active directory' do
before { Gitlab::LDAP::Person.stub(disabled_via_active_directory?: true) }
before do
allow(Gitlab::LDAP::Person).
to receive(:disabled_via_active_directory?).and_return(true)
end
it { is_expected.to be_falsey }
......@@ -31,7 +39,8 @@ describe Gitlab::LDAP::Access do
before do
user.block
Gitlab::LDAP::Person.stub(disabled_via_active_directory?: false)
allow(Gitlab::LDAP::Person).
to receive(:disabled_via_active_directory?).and_return(false)
end
it { is_expected.to be_truthy }
......@@ -39,7 +48,8 @@ describe Gitlab::LDAP::Access do
context 'when auto-created users are blocked' do
before do
Gitlab::LDAP::Config.any_instance.stub(block_auto_created_users: true)
allow_any_instance_of(Gitlab::LDAP::Config).
to receive(:block_auto_created_users).and_return(true)
end
it "does not unblock user in GitLab" do
......@@ -51,7 +61,8 @@ describe Gitlab::LDAP::Access do
context "when auto-created users are not blocked" do
before do
Gitlab::LDAP::Config.any_instance.stub(block_auto_created_users: false)
allow_any_instance_of(Gitlab::LDAP::Config).
to receive(:block_auto_created_users).and_return(false)
end
it "should unblock user in GitLab" do
......@@ -63,8 +74,9 @@ describe Gitlab::LDAP::Access do
context 'without ActiveDirectory enabled' do
before do
Gitlab::LDAP::Config.stub(enabled?: true)
Gitlab::LDAP::Config.any_instance.stub(active_directory: false)
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
allow_any_instance_of(Gitlab::LDAP::Config).
to receive(:active_directory).and_return(false)
end
it { is_expected.to be_truthy }
......
......@@ -3,27 +3,32 @@ require 'spec_helper'
describe Gitlab::LDAP::Adapter do
let(:adapter) { Gitlab::LDAP::Adapter.new 'ldapmain' }
describe :dn_matches_filter? do
describe '#dn_matches_filter?' do
let(:ldap) { double(:ldap) }
subject { adapter.dn_matches_filter?(:dn, :filter) }
before { adapter.stub(ldap: ldap) }
before { allow(adapter).to receive(:ldap).and_return(ldap) }
context "when the search is successful" do
context "and the result is non-empty" do
before { ldap.stub(search: [:foo]) }
before { allow(ldap).to receive(:search).and_return([:foo]) }
it { is_expected.to be_truthy }
end
context "and the result is empty" do
before { ldap.stub(search: []) }
before { allow(ldap).to receive(:search).and_return([]) }
it { is_expected.to be_falsey }
end
end
context "when the search encounters an error" do
before { ldap.stub(search: nil, get_operation_result: double(code: 1, message: 'some error')) }
before do
allow(ldap).to receive_messages(
search: nil,
get_operation_result: double(code: 1, message: 'some error')
)
end
it { is_expected.to be_falsey }
end
......
require 'spec_helper'
describe Gitlab::LDAP::Authentication do
let(:klass) { Gitlab::LDAP::Authentication }
let(:user) { create(:omniauth_user, extern_uid: dn) }
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
let(:login) { 'john' }
let(:password) { 'password' }
describe :login do
let(:adapter) { double :adapter }
describe 'login' do
before do
Gitlab::LDAP::Config.stub(enabled?: true)
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
end
it "finds the user if authentication is successful" do
user
expect(user).not_to be_nil
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
expect(klass.login(login, password)).to be_truthy
adapter = double('adapter', dn: dn).as_null_object
allow_any_instance_of(described_class).
to receive(:adapter).and_return(adapter)
expect(described_class.login(login, password)).to be_truthy
end
it "is false if the user does not exist" do
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter,
bind_as: double(:ldap_user, dn: dn)
))
expect(klass.login(login, password)).to be_falsey
adapter = double('adapter', dn: dn).as_null_object
allow_any_instance_of(described_class).
to receive(:adapter).and_return(adapter)
expect(described_class.login(login, password)).to be_falsey
end
it "is false if authentication fails" do
user
expect(user).not_to be_nil
# try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter, bind_as: nil))
expect(klass.login(login, password)).to be_falsey
adapter = double('adapter', bind_as: nil).as_null_object
allow_any_instance_of(described_class).
to receive(:adapter).and_return(adapter)
expect(described_class.login(login, password)).to be_falsey
end
it "fails if ldap is disabled" do
Gitlab::LDAP::Config.stub(enabled?: false)
expect(klass.login(login, password)).to be_falsey
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(false)
expect(described_class.login(login, password)).to be_falsey
end
it "fails if no login is supplied" do
expect(klass.login('', password)).to be_falsey
expect(described_class.login('', password)).to be_falsey
end
it "fails if no password is supplied" do
expect(klass.login(login, '')).to be_falsey
expect(described_class.login(login, '')).to be_falsey
end
end
end
......@@ -14,7 +14,7 @@ describe Gitlab::LDAP::Config do
end
it "raises an error if a unknow provider is used" do
expect{ Gitlab::LDAP::Config.new 'unknown' }.to raise_error
expect{ Gitlab::LDAP::Config.new 'unknown' }.to raise_error(RuntimeError)
end
end
end
......@@ -16,31 +16,31 @@ describe Gitlab::LDAP::User do
describe :changed? do
it "marks existing ldap user as changed" do
existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
expect(ldap_user.changed?).to be_truthy
end
it "marks existing non-ldap user if the email matches as changed" do
existing_user = create(:user, email: 'john@example.com')
create(:user, email: 'john@example.com')
expect(ldap_user.changed?).to be_truthy
end
it "dont marks existing ldap user as changed" do
existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain')
create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain')
expect(ldap_user.changed?).to be_falsey
end
end
describe :find_or_create do
it "finds the user if already existing" do
existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
expect{ ldap_user.save }.to_not change{ User.count }
expect{ ldap_user.save }.not_to change{ User.count }
end
it "connects to existing non-ldap user if the email matches" do
existing_user = create(:omniauth_user, email: 'john@example.com', provider: "twitter")
expect{ ldap_user.save }.to_not change{ User.count }
expect{ ldap_user.save }.not_to change{ User.count }
existing_user.reload
expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
......@@ -52,11 +52,15 @@ describe Gitlab::LDAP::User do
end
end
describe 'blocking' do
def configure_block(value)
allow_any_instance_of(Gitlab::LDAP::Config).
to receive(:block_auto_created_users).and_return(value)
end
context 'signup' do
context 'dont block on create' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
before { configure_block(false) }
it do
ldap_user.save
......@@ -66,7 +70,7 @@ describe Gitlab::LDAP::User do
end
context 'block on create' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
before { configure_block(true) }
it do
ldap_user.save
......@@ -83,7 +87,7 @@ describe Gitlab::LDAP::User do
end
context 'dont block on create' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
before { configure_block(false) }
it do
ldap_user.save
......@@ -93,7 +97,7 @@ describe Gitlab::LDAP::User do
end
context 'block on create' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
before { configure_block(true) }
it do
ldap_user.save
......
......@@ -51,7 +51,7 @@ describe Gitlab::OAuth::AuthHash do
it { expect(auth_hash.email).to eql email_utf8 }
it { expect(auth_hash.username).to eql nickname_utf8 }
it { expect(auth_hash.name).to eql name_utf8 }
it { expect(auth_hash.password).to_not be_empty }
it { expect(auth_hash.password).not_to be_empty }
end
context 'email not provided' do
......
......@@ -19,23 +19,34 @@ describe Gitlab::OAuth::User do
let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
it "finds an existing user based on uid and provider (facebook)" do
# FIXME (rspeicher): It's unlikely that this test is actually doing anything
# `auth` is never used and removing it entirely doesn't break the test, so
# what's it doing?
auth = double(info: double(name: 'John'), uid: 'my-uid', provider: 'my-provider')
expect( oauth_user.persisted? ).to be_truthy
end
it "returns false if use is not found in database" do
auth_hash.stub(uid: 'non-existing')
allow(auth_hash).to receive(:uid).and_return('non-existing')
expect( oauth_user.persisted? ).to be_falsey
end
end
describe :save do
def stub_omniauth_config(messages)
allow(Gitlab.config.omniauth).to receive_messages(messages)
end
def stub_ldap_config(messages)
allow(Gitlab::LDAP::Config).to receive_messages(messages)
end
let(:provider) { 'twitter' }
describe 'signup' do
shared_examples "to verify compliance with allow_single_sign_on" do
context "with allow_single_sign_on enabled" do
before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
before { stub_omniauth_config(allow_single_sign_on: true) }
it "creates a user from Omniauth" do
oauth_user.save
......@@ -48,7 +59,7 @@ describe Gitlab::OAuth::User do
end
context "with allow_single_sign_on disabled (Default)" do
before { Gitlab.config.omniauth.stub allow_single_sign_on: false }
before { stub_omniauth_config(allow_single_sign_on: false) }
it "throws an error" do
expect{ oauth_user.save }.to raise_error StandardError
end
......@@ -56,28 +67,28 @@ describe Gitlab::OAuth::User do
end
context "with auto_link_ldap_user disabled (default)" do
before { Gitlab.config.omniauth.stub auto_link_ldap_user: false }
before { stub_omniauth_config(auto_link_ldap_user: false) }
include_examples "to verify compliance with allow_single_sign_on"
end
context "with auto_link_ldap_user enabled" do
before { Gitlab.config.omniauth.stub auto_link_ldap_user: true }
before { stub_omniauth_config(auto_link_ldap_user: true) }
context "and no LDAP provider defined" do
before { allow(Gitlab::LDAP::Config).to receive(:providers).and_return([]) }
before { stub_ldap_config(providers: []) }
include_examples "to verify compliance with allow_single_sign_on"
end
context "and at least one LDAP provider is defined" do
before { allow(Gitlab::LDAP::Config).to receive(:providers).and_return(['ldapmain']) }
before { stub_ldap_config(providers: %w(ldapmain)) }
context "and a corresponding LDAP person" do
before do
ldap_user.stub(:uid) { uid }
ldap_user.stub(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
allow(ldap_user).to receive(:uid) { uid }
allow(ldap_user).to receive(:username) { uid }
allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] }
allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
end
......@@ -128,11 +139,11 @@ describe Gitlab::OAuth::User do
describe 'blocking' do
let(:provider) { 'twitter' }
before { Gitlab.config.omniauth.stub allow_single_sign_on: true }
before { stub_omniauth_config(allow_single_sign_on: true) }
context 'signup with omniauth only' do
context 'dont block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: false }
before { stub_omniauth_config(block_auto_created_users: false) }
it do
oauth_user.save
......@@ -142,7 +153,7 @@ describe Gitlab::OAuth::User do
end
context 'block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: true }
before { stub_omniauth_config(block_auto_created_users: true) }
it do
oauth_user.save
......@@ -154,17 +165,17 @@ describe Gitlab::OAuth::User do
context 'signup with linked omniauth and LDAP account' do
before do
Gitlab.config.omniauth.stub auto_link_ldap_user: true
ldap_user.stub(:uid) { uid }
ldap_user.stub(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' }
stub_omniauth_config(auto_link_ldap_user: true)
allow(ldap_user).to receive(:uid) { uid }
allow(ldap_user).to receive(:username) { uid }
allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] }
allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
end
context "and no account for the LDAP user" do
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
it do
oauth_user.save
......@@ -174,7 +185,7 @@ describe Gitlab::OAuth::User do
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
it do
oauth_user.save
......@@ -188,7 +199,7 @@ describe Gitlab::OAuth::User do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
it do
oauth_user.save
......@@ -198,7 +209,7 @@ describe Gitlab::OAuth::User do
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
it do
oauth_user.save
......@@ -217,7 +228,7 @@ describe Gitlab::OAuth::User do
end
context 'dont block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: false }
before { stub_omniauth_config(block_auto_created_users: false) }
it do
oauth_user.save
......@@ -227,7 +238,7 @@ describe Gitlab::OAuth::User do
end
context 'block on create' do
before { Gitlab.config.omniauth.stub block_auto_created_users: true }
before { stub_omniauth_config(block_auto_created_users: true) }
it do
oauth_user.save
......@@ -237,7 +248,7 @@ describe Gitlab::OAuth::User do
end
context 'dont block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: false }
before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: false) }
it do
oauth_user.save
......@@ -247,7 +258,7 @@ describe Gitlab::OAuth::User do
end
context 'block on create (LDAP)' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true }
before { allow_any_instance_of(Gitlab::LDAP::Config).to receive_messages(block_auto_created_users: true) }
it do
oauth_user.save
......
......@@ -28,7 +28,7 @@ describe 'Gitlab::Popen', no_db: true do
context 'unsafe string command' do
it 'raises an error when it gets called with a string argument' do
expect { @klass.new.popen('ls', path) }.to raise_error
expect { @klass.new.popen('ls', path) }.to raise_error(RuntimeError)
end
end
......
......@@ -27,7 +27,7 @@ describe 'Gitlab::Satellite::MergeAction' do
context 'between branches' do
it 'should raise exception -- not expected to be used by non forks' do
expect { Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between }.to raise_error
expect { Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between }.to raise_error(RuntimeError)
end
end
end
......@@ -75,7 +75,7 @@ describe 'Gitlab::Satellite::MergeAction' do
context 'between branches' do
it 'should get proper diffs' do
expect{ Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite }.to raise_error
expect{ Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite }.to raise_error(RuntimeError)
end
end
end
......
......@@ -10,14 +10,14 @@ describe Gitlab::Upgrader do
describe 'latest_version?' do
it 'should be true if newest version' do
upgrader.stub(latest_version_raw: current_version)
allow(upgrader).to receive(:latest_version_raw).and_return(current_version)
expect(upgrader.latest_version?).to be_truthy
end
end
describe 'latest_version_raw' do
it 'should be latest version for GitLab 5' do
upgrader.stub(current_version_raw: "5.3.0")
allow(upgrader).to receive(:current_version_raw).and_return("5.3.0")
expect(upgrader.latest_version_raw).to eq("v5.4.2")
end
......
require 'spec_helper'
require 'email_spec'
describe Notify do
include EmailSpec::Helpers
......
......@@ -14,7 +14,7 @@ describe CommitRange do
let(:range2) { described_class.new("#{sha_from}..#{sha_to}") }
it 'raises ArgumentError when given an invalid range string' do
expect { described_class.new("Foo") }.to raise_error
expect { described_class.new("Foo") }.to raise_error(ArgumentError)
end
describe '#to_s' do
......
......@@ -77,13 +77,13 @@ eos
let(:other_issue) { create :issue, project: other_project }
it 'detects issues that this commit is marked as closing' do
commit.stub(safe_message: "Fixes ##{issue.iid}")
allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid}")
expect(commit.closes_issues).to eq([issue])
end
it 'does not detect issues from other projects' do
ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
commit.stub(safe_message: "Fixes #{ext_ref}")
allow(commit).to receive(:safe_message).and_return("Fixes #{ext_ref}")
expect(commit.closes_issues).to be_empty
end
end
......@@ -93,7 +93,9 @@ eos
let(:author) { create(:user, email: commit.author_email) }
let(:backref_text) { "commit #{subject.id}" }
let(:set_mentionable_text) { ->(txt){ subject.stub(safe_message: txt) } }
let(:set_mentionable_text) do
->(txt) { allow(subject).to receive(:safe_message).and_return(txt) }
end
# Include the subject in the repository stub.
let(:extra_commits) { [subject] }
......
......@@ -11,7 +11,10 @@ describe Issue, "Issuable" do
end
describe "Validation" do
before { subject.stub(set_iid: false) }
before do
allow(subject).to receive(:set_iid).and_return(false)
end
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:iid) }
it { is_expected.to validate_presence_of(:author) }
......
......@@ -23,7 +23,7 @@ describe Issue, "Mentionable" do
end
it 'correctly removes already-mentioned Commits' do
expect(Note).not_to receive(:create_cross_reference_note)
expect(SystemNoteService).not_to receive(:cross_reference)
issue.create_cross_references!(project, author, [commit2])
end
......
......@@ -58,10 +58,10 @@ describe :forked_from_project do
end
def fork_project(from_project, user)
context = Projects::ForkService.new(from_project, user)
shell = double("gitlab_shell")
shell.stub(fork_repository: true)
context.stub(gitlab_shell: shell)
context.execute
end
shell = double('gitlab_shell', fork_repository: true)
service = Projects::ForkService.new(from_project, user)
allow(service).to receive(:gitlab_shell).and_return(shell)
service.execute
end
......@@ -52,7 +52,7 @@ describe ServiceHook do
expect {
@service_hook.execute(@data)
}.to raise_error
}.to raise_error(RuntimeError)
end
end
end
......@@ -73,7 +73,7 @@ describe ProjectHook do
expect {
@project_hook.execute(@data, 'push_hooks')
}.to raise_error
}.to raise_error(RuntimeError)
end
end
end
......@@ -70,7 +70,7 @@ describe Issue do
it_behaves_like 'an editable mentionable' do
subject { create(:issue, project: project) }
let(:backref_text) { "issue ##{subject.iid}" }
let(:backref_text) { "issue #{subject.to_reference}" }
let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
end
......
......@@ -24,8 +24,11 @@ describe GroupMember do
describe "#after_create" do
it "should send email to user" do
membership = build(:group_member)
membership.stub(notification_service: double('NotificationService').as_null_object)
allow(membership).to receive(:notification_service).
and_return(double('NotificationService').as_null_object)
expect(membership).to receive(:notification_service)
membership.save
end
end
......@@ -33,7 +36,8 @@ describe GroupMember do
describe "#after_update" do
before do
@group_member = create :group_member
@group_member.stub(notification_service: double('NotificationService').as_null_object)
allow(@group_member).to receive(:notification_service).
and_return(double('NotificationService').as_null_object)
end
it "should send email to user" do
......
......@@ -111,17 +111,18 @@ describe MergeRequest do
let(:commit2) { double('commit2', closes_issues: [issue1]) }
before do
subject.stub(commits: [commit0, commit1, commit2])
allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
end
it 'accesses the set of issues that will be closed on acceptance' do
subject.project.stub(default_branch: subject.target_branch)
allow(subject.project).to receive(:default_branch).
and_return(subject.target_branch)
expect(subject.closes_issues).to eq([issue0, issue1].sort_by(&:id))
end
it 'only lists issues as to be closed if it targets the default branch' do
subject.project.stub(default_branch: 'master')
allow(subject.project).to receive(:default_branch).and_return('master')
subject.target_branch = 'something-else'
expect(subject.closes_issues).to be_empty
......@@ -130,7 +131,8 @@ describe MergeRequest do
it 'detects issues mentioned in the description' do
issue2 = create(:issue, project: subject.project)
subject.description = "Closes #{issue2.to_reference}"
subject.project.stub(default_branch: subject.target_branch)
allow(subject.project).to receive(:default_branch).
and_return(subject.target_branch)
expect(subject.closes_issues).to include(issue2)
end
......@@ -163,10 +165,10 @@ describe MergeRequest do
end
it_behaves_like 'an editable mentionable' do
subject { create(:merge_request, source_project: project, target_project: project) }
subject { create(:merge_request, source_project: project) }
let(:backref_text) { "merge request !#{subject.iid}" }
let(:set_mentionable_text) { ->(txt){ subject.title = txt } }
let(:backref_text) { "merge request #{subject.to_reference}" }
let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
end
it_behaves_like 'a Taskable' do
......
......@@ -21,11 +21,11 @@ describe Milestone do
it { is_expected.to have_many(:issues) }
end
describe "Mass assignment" do
describe "Validation" do
before do
allow(subject).to receive(:set_iid).and_return(false)
end
describe "Validation" do
before { subject.stub(set_iid: false) }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:project) }
end
......@@ -66,7 +66,7 @@ describe Milestone do
describe :expired? do
context "expired" do
before do
milestone.stub(due_date: Date.today.prev_year)
allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
end
it { expect(milestone.expired?).to be_truthy }
......@@ -74,7 +74,7 @@ describe Milestone do
context "not expired" do
before do
milestone.stub(due_date: Date.today.next_year)
allow(milestone).to receive(:due_date).and_return(Date.today.next_year)
end
it { expect(milestone.expired?).to be_falsey }
......@@ -83,7 +83,7 @@ describe Milestone do
describe :percent_complete do
before do
milestone.stub(
allow(milestone).to receive_messages(
closed_items_count: 3,
total_items_count: 4
)
......
......@@ -53,7 +53,7 @@ describe Namespace do
describe :move_dir do
before do
@namespace = create :namespace
@namespace.stub(path_changed?: true)
allow(@namespace).to receive(:path_changed?).and_return(true)
end
it "should raise error when directory exists" do
......@@ -62,8 +62,8 @@ describe Namespace do
it "should move dir if path changed" do
new_path = @namespace.path + "_new"
@namespace.stub(path_was: @namespace.path)
@namespace.stub(path: new_path)
allow(@namespace).to receive(:path_was).and_return(@namespace.path)
allow(@namespace).to receive(:path).and_return(new_path)
expect(@namespace.move_dir).to be_truthy
end
end
......
......@@ -42,7 +42,7 @@ describe AsanaService, models: true do
before do
@asana = AsanaService.new
@asana.stub(
allow(@asana).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
......
......@@ -32,7 +32,7 @@ describe AssemblaService, models: true do
before do
@assembla_service = AssemblaService.new
@assembla_service.stub(
allow(@assembla_service).to receive_messages(
project_id: project.id,
project: project,
service_hook: true,
......
......@@ -29,12 +29,10 @@ describe BuildkiteService do
describe 'commits methods' do
before do
@project = Project.new
@project.stub(
default_branch: 'default-brancho'
)
allow(@project).to receive(:default_branch).and_return('default-brancho')
@service = BuildkiteService.new
@service.stub(
allow(@service).to receive_messages(
project: @project,
service_hook: true,
project_url: 'https://buildkite.com/account-name/example-project',
......
......@@ -32,7 +32,7 @@ describe FlowdockService do
before do
@flowdock_service = FlowdockService.new
@flowdock_service.stub(
allow(@flowdock_service).to receive_messages(
project_id: project.id,
project: project,
service_hook: true,
......
......@@ -32,7 +32,7 @@ describe GemnasiumService do
before do
@gemnasium_service = GemnasiumService.new
@gemnasium_service.stub(
allow(@gemnasium_service).to receive_messages(
project_id: project.id,
project: project,
service_hook: true,
......
......@@ -21,18 +21,15 @@
require 'spec_helper'
describe GitlabCiService do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
describe "Mass assignment" do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_one(:service_hook) }
end
describe 'commits methods' do
before do
@service = GitlabCiService.new
@service.stub(
allow(@service).to receive_messages(
service_hook: true,
project_url: 'http://ci.gitlab.org/projects/2',
token: 'verySecret'
......@@ -56,9 +53,9 @@ describe GitlabCiService do
it "calls ci_yaml_file" do
service_hook = double
service_hook.should_receive(:execute)
@service.should_receive(:service_hook).and_return(service_hook)
@service.should_receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
expect(service_hook).to receive(:execute)
expect(@service).to receive(:service_hook).and_return(service_hook)
expect(@service).to receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
@service.execute(push_sample_data)
end
......@@ -72,7 +69,7 @@ describe GitlabCiService do
@user = create(:user)
@service = GitlabCiService.new
@service.stub(
allow(@service).to receive_messages(
service_hook: true,
project_url: 'http://ci.gitlab.org/projects/2',
token: 'verySecret',
......
......@@ -37,7 +37,7 @@ describe HipchatService do
let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
before(:each) do
hipchat.stub(
allow(hipchat).to receive_messages(
project_id: project.id,
project: project,
room: 123456,
......@@ -48,7 +48,7 @@ describe HipchatService do
end
it 'should use v1 if version is provided' do
hipchat.stub(api_version: 'v1')
allow(hipchat).to receive(:api_version).and_return('v1')
expect(HipChat::Client).to receive(:new).
with(token,
api_version: 'v1',
......@@ -59,7 +59,7 @@ describe HipchatService do
end
it 'should use v2 as the version when nothing is provided' do
hipchat.stub(api_version: '')
allow(hipchat).to receive(:api_version).and_return('')
expect(HipChat::Client).to receive(:new).
with(token,
api_version: 'v2',
......@@ -245,12 +245,12 @@ describe HipchatService do
end
it "should set notfiy to true" do
hipchat.stub(notify: '1')
allow(hipchat).to receive(:notify).and_return('1')
expect(hipchat.send(:message_options)).to eq({notify: true, color: 'yellow'})
end
it "should set the color" do
hipchat.stub(color: 'red')
allow(hipchat).to receive(:color).and_return('red')
expect(hipchat.send(:message_options)).to eq({notify: false, color: 'red'})
end
end
......
......@@ -24,8 +24,8 @@ require 'json'
describe IrkerService do
describe 'Associations' do
it { should belong_to :project }
it { should have_one :service_hook }
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
end
describe 'Validations' do
......@@ -66,7 +66,7 @@ describe IrkerService do
let(:colorize_messages) { '1' }
before do
irker.stub(
allow(irker).to receive_messages(
active: true,
project: project,
project_id: project.id,
......
......@@ -52,7 +52,7 @@ describe PushoverService do
let(:api_url) { 'https://api.pushover.net/1/messages.json' }
before do
pushover.stub(
allow(pushover).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
......
......@@ -46,7 +46,7 @@ describe SlackService do
let(:channel) { 'slack_channel' }
before do
slack.stub(
allow(slack).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
......@@ -96,7 +96,7 @@ describe SlackService do
end
it 'should use the username as an option for slack when configured' do
slack.stub(username: username)
allow(slack).to receive(:username).and_return(username)
expect(Slack::Notifier).to receive(:new).
with(webhook_url, username: username).
and_return(
......@@ -106,7 +106,7 @@ describe SlackService do
end
it 'should use the channel as an option when it is configured' do
slack.stub(channel: channel)
allow(slack).to receive(:channel).and_return(channel)
expect(Slack::Notifier).to receive(:new).
with(webhook_url, channel: channel).
and_return(
......@@ -130,7 +130,7 @@ describe SlackService do
let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
before do
slack.stub(
allow(slack).to receive_messages(
project: project,
project_id: project.id,
service_hook: true,
......
......@@ -127,7 +127,7 @@ describe Project do
describe 'last_activity' do
it 'should alias last_activity to last_event' do
project.stub(last_event: last_event)
allow(project).to receive(:last_event).and_return(last_event)
expect(project.last_activity).to eq(last_event)
end
end
......
......@@ -39,9 +39,7 @@ describe Service do
let (:project) { create :project }
before do
@service.stub(
project: project
)
allow(@service).to receive(:project).and_return(project)
@testable = @service.can_test?
end
......@@ -54,9 +52,7 @@ describe Service do
let (:project) { create :project }
before do
@service.stub(
project: project
)
allow(@service).to receive(:project).and_return(project)
@testable = @service.can_test?
end
......
......@@ -40,7 +40,6 @@ describe Snippet do
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_length_of(:title).is_within(0..255) }
it { is_expected.to validate_presence_of(:file_name) }
it { is_expected.to validate_length_of(:file_name).is_within(0..255) }
it { is_expected.to validate_presence_of(:content) }
......
......@@ -210,6 +210,30 @@ describe User do
end
end
describe '#two_factor_enabled' do
it 'returns two-factor authentication status' do
enabled = build_stubbed(:user, two_factor_enabled: true)
disabled = build_stubbed(:user)
expect(enabled).to be_two_factor_enabled
expect(disabled).not_to be_two_factor_enabled
end
end
describe '#two_factor_enabled=' do
it 'enables two-factor authentication' do
user = build_stubbed(:user, two_factor_enabled: false)
expect { user.two_factor_enabled = true }.
to change { user.two_factor_enabled? }.to(true)
end
it 'disables two-factor authentication' do
user = build_stubbed(:user, two_factor_enabled: true)
expect { user.two_factor_enabled = false }.
to change { user.two_factor_enabled? }.to(false)
end
end
describe 'authentication token' do
it "should have authentication token" do
user = create(:user)
......@@ -340,6 +364,31 @@ describe User do
end
end
describe '.find_for_commit' do
it 'finds by primary email' do
user = create(:user, email: 'foo@example.com')
expect(User.find_for_commit(user.email, '')).to eq user
end
it 'finds by secondary email' do
email = create(:email, email: 'foo@example.com')
user = email.user
expect(User.find_for_commit(email.email, '')).to eq user
end
it 'finds by name' do
user = create(:user, name: 'Joey JoJo')
expect(User.find_for_commit('', 'Joey JoJo')).to eq user
end
it 'returns nil when nothing found' do
expect(User.find_for_commit('', '')).to be_nil
end
end
describe 'search' do
let(:user1) { create(:user, username: 'James', email: 'james@testing.com') }
let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') }
......@@ -409,21 +458,25 @@ describe User do
it 'is false when LDAP is disabled' do
# Create a condition which would otherwise cause 'true' to be returned
user.stub(ldap_user?: true)
allow(user).to receive(:ldap_user?).and_return(true)
user.last_credential_check_at = nil
expect(user.requires_ldap_check?).to be_falsey
end
context 'when LDAP is enabled' do
before { Gitlab.config.ldap.stub(enabled: true) }
before do
allow(Gitlab.config.ldap).to receive(:enabled).and_return(true)
end
it 'is false for non-LDAP users' do
user.stub(ldap_user?: false)
allow(user).to receive(:ldap_user?).and_return(false)
expect(user.requires_ldap_check?).to be_falsey
end
context 'and when the user is an LDAP user' do
before { user.stub(ldap_user?: true) }
before do
allow(user).to receive(:ldap_user?).and_return(true)
end
it 'is true when the user has never had an LDAP check before' do
user.last_credential_check_at = nil
......
require "spec_helper"
......@@ -47,7 +47,7 @@ describe API, api: true do
it "should return nil for a user without access" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token
Gitlab::UserAccess.stub(allowed?: false)
allow(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
expect(current_user).to be_nil
end
......@@ -72,13 +72,13 @@ describe API, api: true do
it "should throw an error when the current user is not an admin and attempting to sudo" do
set_env(user, admin.id)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
set_param(user, admin.id)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
set_env(user, admin.username)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
set_param(user, admin.username)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
end
it "should throw an error when the user cannot be found for a given id" do
......@@ -86,10 +86,10 @@ describe API, api: true do
expect(user.id).not_to eq(id)
expect(admin.id).not_to eq(id)
set_env(admin, id)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
set_param(admin, id)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
end
it "should throw an error when the user cannot be found for a given username" do
......@@ -97,10 +97,10 @@ describe API, api: true do
expect(user.username).not_to eq(username)
expect(admin.username).not_to eq(username)
set_env(admin, username)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
set_param(admin, username)
expect { current_user }.to raise_error
expect { current_user }.to raise_error(Exception)
end
it "should handle sudo's to oneself" do
......
......@@ -141,7 +141,9 @@ describe API::API, api: true do
end
describe "DELETE /projects/:id/repository/branches/:branch" do
before { Repository.any_instance.stub(rm_branch: true) }
before do
allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true)
end
it "should remove branch" do
delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
......
......@@ -60,9 +60,8 @@ describe API::API, api: true do
end
it "should return a 400 if editor fails to create file" do
Repository.any_instance.stub(
commit_file: false,
)
allow_any_instance_of(Repository).to receive(:commit_file).
and_return(false)
post api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(400)
......@@ -112,9 +111,7 @@ describe API::API, api: true do
end
it "should return a 400 if satellite fails to create file" do
Repository.any_instance.stub(
remove_file: false,
)
allow_any_instance_of(Repository).to receive(:remove_file).and_return(false)
delete api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(400)
......
......@@ -167,7 +167,8 @@ describe API::API, api: true do
describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:project) }
before(:each) do
Projects::TransferService.any_instance.stub(execute: true)
allow_any_instance_of(Projects::TransferService).
to receive(:execute).and_return(true)
allow(Project).to receive(:find).and_return(project)
end
......
......@@ -49,9 +49,8 @@ describe API::API, api: true do
get api("/projects/#{project.id}/merge_requests?state=closed", user)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
expect(json_response.second['title']).to eq(merge_request_closed.title)
expect(json_response.first['title']).to eq(merge_request_merged.title)
expect(json_response.length).to eq(1)
expect(json_response.first['title']).to eq(merge_request_closed.title)
end
it "should return an array of merged merge_requests" do
......@@ -301,14 +300,20 @@ describe API::API, api: true do
describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
it "should return merge_request in case of success" do
MergeRequest.any_instance.stub(can_be_merged?: true, automerge!: true)
allow_any_instance_of(MergeRequest).
to receive_messages(can_be_merged?: true, automerge!: true)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
expect(response.status).to eq(200)
end
it "should return 405 if branch can't be merged" do
MergeRequest.any_instance.stub(can_be_merged?: false)
allow_any_instance_of(MergeRequest).
to receive(:can_be_merged?).and_return(false)
put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
expect(response.status).to eq(405)
expect(json_response['message']).to eq('Branch cannot be merged')
end
......
......@@ -132,7 +132,7 @@ describe API::API, api: true do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to_not change { ProjectMember.count }
}.not_to change { ProjectMember.count }
end
it "should return 200 if team member already removed" do
......
......@@ -121,15 +121,13 @@ describe API::API, api: true do
get api('/projects/all', admin)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
project_name = project.name
expect(json_response.detect {
|project| project['name'] == project_name
}['name']).to eq(project_name)
expect(json_response.detect {
|project| project['owner']['username'] == user.username
}['owner']['username']).to eq(user.username)
expect(json_response).to satisfy do |response|
response.one? do |entry|
entry['name'] == project.name &&
entry['owner']['username'] == user.username
end
end
end
end
end
......@@ -138,9 +136,8 @@ describe API::API, api: true do
context 'maximum number of projects reached' do
it 'should not create new project and respond with 403' do
allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
expect {
post api('/projects', user2), name: 'foo'
}.to change {Project.count}.by(0)
expect { post api('/projects', user2), name: 'foo' }.
to change {Project.count}.by(0)
expect(response.status).to eq(403)
end
end
......@@ -158,7 +155,7 @@ describe API::API, api: true do
end
it 'should not create new project without name and return 400' do
expect { post api('/projects', user) }.to_not change { Project.count }
expect { post api('/projects', user) }.not_to change { Project.count }
expect(response.status).to eq(400)
end
......@@ -257,7 +254,7 @@ describe API::API, api: true do
it 'should respond with 400 on failure and not project' do
expect { post api("/projects/user/#{user.id}", admin) }.
to_not change { Project.count }
not_to change { Project.count }
expect(response.status).to eq(400)
expect(json_response['message']['name']).to eq([
......
......@@ -49,7 +49,7 @@ describe API::API, api: true do
it "should not create new hook without url" do
expect {
post api("/hooks", admin)
}.to_not change { SystemHook.count }
}.not_to change { SystemHook.count }
end
end
......
......@@ -19,7 +19,7 @@ describe ArchiveRepositoryService do
it "raises an error" do
expect {
subject.execute(timeout: 0.0)
}.to raise_error
}.to raise_error(RuntimeError)
end
end
......
......@@ -124,7 +124,9 @@ describe GitPushService do
end
it "when pushing a branch for the first time with default branch protection disabled" do
ApplicationSetting.any_instance.stub(default_branch_protection: 0)
allow(ApplicationSetting.current_application_settings).
to receive(:default_branch_protection).
and_return(Gitlab::Access::PROTECTION_NONE)
expect(project).to receive(:execute_hooks)
expect(project.default_branch).to eq("master")
......@@ -133,7 +135,9 @@ describe GitPushService do
end
it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do
ApplicationSetting.any_instance.stub(default_branch_protection: 1)
allow(ApplicationSetting.current_application_settings).
to receive(:default_branch_protection).
and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
expect(project).to receive(:execute_hooks)
expect(project.default_branch).to eq("master")
......@@ -154,32 +158,35 @@ describe GitPushService do
let(:commit) { project.commit }
before do
commit.stub({
allow(commit).to receive_messages(
safe_message: "this commit \n mentions ##{issue.id}",
references: [issue],
author_name: commit_author.name,
author_email: commit_author.email
})
project.repository.stub(commits_between: [commit])
)
allow(project.repository).to receive(:commits_between).and_return([commit])
end
it "creates a note if a pushed commit mentions an issue" do
expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author)
expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)
service.execute(project, user, @oldrev, @newrev, @ref)
end
it "only creates a cross-reference note if one doesn't already exist" do
Note.create_cross_reference_note(issue, commit, user)
SystemNoteService.cross_reference(issue, commit, user)
expect(Note).not_to receive(:create_cross_reference_note).with(issue, commit, commit_author)
expect(SystemNoteService).not_to receive(:cross_reference).with(issue, commit, commit_author)
service.execute(project, user, @oldrev, @newrev, @ref)
end
it "defaults to the pushing user if the commit's author is not known" do
commit.stub(author_name: 'unknown name', author_email: 'unknown@email.com')
expect(Note).to receive(:create_cross_reference_note).with(issue, commit, user)
allow(commit).to receive_messages(
author_name: 'unknown name',
author_email: 'unknown@email.com'
)
expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, user)
service.execute(project, user, @oldrev, @newrev, @ref)
end
......@@ -188,7 +195,7 @@ describe GitPushService do
allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([])
allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit])
expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author)
expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)
service.execute(project, user, @blankrev, @newrev, 'refs/heads/other')
end
......@@ -201,14 +208,15 @@ describe GitPushService do
let(:closing_commit) { project.commit }
before do
closing_commit.stub({
allow(closing_commit).to receive_messages(
issue_closing_regex: /^([Cc]loses|[Ff]ixes) #\d+/,
safe_message: "this is some work.\n\ncloses ##{issue.iid}",
author_name: commit_author.name,
author_email: commit_author.email
})
)
project.repository.stub(commits_between: [closing_commit])
allow(project.repository).to receive(:commits_between).
and_return([closing_commit])
end
it "closes issues with commit messages" do
......@@ -224,7 +232,7 @@ describe GitPushService do
end
it "doesn't close issues when pushed to non-default branches" do
project.stub(default_branch: 'durf')
allow(project).to receive(:default_branch).and_return('durf')
# The push still shouldn't create cross-reference notes.
expect {
......
......@@ -58,7 +58,7 @@ describe NotificationService do
end
it 'filters out "mentioned in" notes' do
mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author)
mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author)
expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note)
......@@ -130,7 +130,7 @@ describe NotificationService do
end
it 'filters out "mentioned in" notes' do
mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author)
mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author)
expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note)
......
......@@ -2,7 +2,6 @@ ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'shoulda/matchers'
require 'email_spec'
require 'sidekiq/testing/inline'
# Requires supporting ruby files with custom matchers and macros, etc,
......
......@@ -19,36 +19,3 @@ unless ENV['CI'] || ENV['CI_SERVER']
# Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run
end
module CapybaraHelpers
# Execute a block a certain number of times before considering it a failure
#
# The given block is called, and if it raises a `Capybara::ExpectationNotMet`
# error, we wait `interval` seconds and then try again, until `retries` is
# met.
#
# This allows for better handling of timing-sensitive expectations in a
# sketchy CI environment, for example.
#
# interval - Delay between retries in seconds (default: 0.5)
# retries - Number of times to execute before failing (default: 5)
def allowing_for_delay(interval: 0.5, retries: 5)
tries = 0
begin
yield
rescue Capybara::ExpectationNotMet => ex
if tries <= retries
tries += 1
sleep interval
retry
else
raise ex
end
end
end
end
RSpec.configure do |config|
config.include CapybaraHelpers, type: :feature
end
module CapybaraHelpers
# Execute a block a certain number of times before considering it a failure
#
# The given block is called, and if it raises a `Capybara::ExpectationNotMet`
# error, we wait `interval` seconds and then try again, until `retries` is
# met.
#
# This allows for better handling of timing-sensitive expectations in a
# sketchy CI environment, for example.
#
# interval - Delay between retries in seconds (default: 0.5)
# retries - Number of times to execute before failing (default: 5)
def allowing_for_delay(interval: 0.5, retries: 5)
tries = 0
begin
sleep interval
yield
rescue Capybara::ExpectationNotMet => ex
if tries <= retries
tries += 1
sleep interval
retry
else
raise ex
end
end
end
end
RSpec.configure do |config|
config.include CapybaraHelpers, type: :feature
end
# RSpec.configure do |config|
# config.around(:each) do |example|
# DatabaseCleaner.strategy = :transaction
# DatabaseCleaner.clean_with(:truncation)
# DatabaseCleaner.cleaning do
# example.run
# end
# end
# config.around(:each, js: true) do |example|
# DatabaseCleaner.strategy = :truncation
# DatabaseCleaner.clean_with(:truncation)
# DatabaseCleaner.cleaning do
# example.run
# end
# end
# end
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
......@@ -36,15 +18,4 @@ RSpec.configure do |config|
config.after(:each) do
DatabaseCleaner.clean
end
# rspec-rails 3 will no longer automatically infer an example group's spec type
# from the file location. You can explicitly opt-in to the feature using this
# config option.
# To explicitly tag specs without using automatic inference, set the `:type`
# metadata manually:
#
# describe ThingsController, :type => :controller do
# # Equivalent to being in spec/controllers
# end
config.infer_spec_type_from_file_location!
end
......@@ -39,4 +39,9 @@ module LoginHelpers
def logout
find(:css, ".fa.fa-sign-out").click
end
# Logout without JavaScript driver
def logout_direct
page.driver.submit :delete, '/users/sign_out', {}
end
end
RSpec::Matchers.define :be_valid_commit do
match do |actual|
actual != nil
actual.id == ValidCommit::ID
actual.message == ValidCommit::MESSAGE
actual &&
actual.id == ValidCommit::ID &&
actual.message == ValidCommit::MESSAGE &&
actual.author_name == ValidCommit::AUTHOR_FULL_NAME
end
end
def emulate_user(user)
user = case user
when :user then create(:user)
when :visitor then nil
when :admin then create(:admin)
else user
end
login_with(user) if user
end
RSpec::Matchers.define :be_allowed_for do |user|
match do |url|
include UrlAccess
url_allowed?(user, url)
emulate_user(user)
visit url
status_code != 404 && current_path != new_user_session_path
end
end
RSpec::Matchers.define :be_denied_for do |user|
match do |url|
include UrlAccess
url_denied?(user, url)
emulate_user(user)
visit url
status_code == 404 || current_path == new_user_session_path
end
end
RSpec::Matchers.define :be_404_for do |user|
RSpec::Matchers.define :be_not_found_for do |user|
match do |url|
include UrlAccess
url_404?(user, url)
emulate_user(user)
visit url
status_code == 404
end
end
......@@ -33,38 +46,12 @@ RSpec::Matchers.define :include_module do |expected|
described_class.included_modules.include?(expected)
end
failure_message_for_should do
"expected #{described_class} to include the #{expected} module"
description do
"includes the #{expected} module"
end
end
module UrlAccess
def url_allowed?(user, url)
emulate_user(user)
visit url
(status_code != 404 && current_path != new_user_session_path)
end
def url_denied?(user, url)
emulate_user(user)
visit url
(status_code == 404 || current_path == new_user_session_path)
end
def url_404?(user, url)
emulate_user(user)
visit url
status_code == 404
end
def emulate_user(user)
user = case user
when :user then create(:user)
when :visitor then nil
when :admin then create(:admin)
else user
end
login_with(user) if user
failure_message do
"expected #{described_class} to include the #{expected} module"
end
end
......
......@@ -80,7 +80,7 @@ shared_examples 'a mentionable' do
ext_issue, ext_mr, ext_commit]
mentioned_objects.each do |referenced|
expect(Note).to receive(:create_cross_reference_note).
expect(SystemNoteService).to receive(:cross_reference).
with(referenced, subject.local_reference, author)
end
......@@ -88,7 +88,7 @@ shared_examples 'a mentionable' do
end
it 'detects existing cross-references' do
Note.create_cross_reference_note(mentioned_issue, subject.local_reference, author)
SystemNoteService.cross_reference(mentioned_issue, subject.local_reference, author)
expect(subject).to have_mentioned(mentioned_issue)
expect(subject).not_to have_mentioned(mentioned_mr)
......@@ -132,13 +132,13 @@ shared_examples 'an editable mentionable' do
# These three objects were already referenced, and should not receive new
# notes
[mentioned_issue, mentioned_commit, ext_issue].each do |oldref|
expect(Note).not_to receive(:create_cross_reference_note).
expect(SystemNoteService).not_to receive(:cross_reference).
with(oldref, any_args)
end
# These two issues are new and should receive reference notes
new_issues.each do |newref|
expect(Note).to receive(:create_cross_reference_note).
expect(SystemNoteService).to receive(:cross_reference).
with(newref, subject.local_reference, author)
end
......
......@@ -41,11 +41,13 @@ module TestEnv
end
def disable_mailer
NotificationService.any_instance.stub(mailer: double.as_null_object)
allow_any_instance_of(NotificationService).to receive(:mailer).
and_return(double.as_null_object)
end
def enable_mailer
allow_any_instance_of(NotificationService).to receive(:mailer).and_call_original
allow_any_instance_of(NotificationService).to receive(:mailer).
and_call_original
end
# Clean /tmp/tests
......
......@@ -23,30 +23,33 @@ describe 'gitlab:app namespace rake task' do
context 'gitlab version' do
before do
Dir.stub glob: []
allow(Dir).to receive :chdir
File.stub exists?: true
Kernel.stub system: true
FileUtils.stub cp_r: true
FileUtils.stub mv: true
Rake::Task["gitlab:shell:setup"].stub invoke: true
allow(Dir).to receive(:glob).and_return([])
allow(Dir).to receive(:chdir)
allow(File).to receive(:exists?).and_return(true)
allow(Kernel).to receive(:system).and_return(true)
allow(FileUtils).to receive(:cp_r).and_return(true)
allow(FileUtils).to receive(:mv).and_return(true)
allow(Rake::Task["gitlab:shell:setup"]).
to receive(:invoke).and_return(true)
end
let(:gitlab_version) { Gitlab::VERSION }
it 'should fail on mismatch' do
YAML.stub load_file: {gitlab_version: "not #{gitlab_version}" }
expect { run_rake_task('gitlab:backup:restore') }.to(
raise_error SystemExit
)
allow(YAML).to receive(:load_file).
and_return({gitlab_version: "not #{gitlab_version}" })
expect { run_rake_task('gitlab:backup:restore') }.
to raise_error(SystemExit)
end
it 'should invoke restoration on mach' do
YAML.stub load_file: {gitlab_version: gitlab_version}
expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke
expect(Rake::Task["gitlab:backup:repo:restore"]).to receive :invoke
expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke
expect { run_rake_task('gitlab:backup:restore') }.to_not raise_error
allow(YAML).to receive(:load_file).
and_return({gitlab_version: gitlab_version})
expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke)
expect(Rake::Task["gitlab:backup:repo:restore"]).to receive(:invoke)
expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke)
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end
end
......@@ -140,13 +143,14 @@ describe 'gitlab:app namespace rake task' do
end
it 'does not invoke repositories restore' do
Rake::Task["gitlab:shell:setup"].stub invoke: true
allow(Rake::Task["gitlab:shell:setup"]).
to receive(:invoke).and_return(true)
allow($stdout).to receive :write
expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke
expect(Rake::Task["gitlab:backup:repo:restore"]).not_to receive :invoke
expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke
expect { run_rake_task('gitlab:backup:restore') }.to_not raise_error
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end
end
end # gitlab:app namespace
......@@ -21,7 +21,7 @@ describe 'gitlab:mail_google_schema_whitelisting rake task' do
end
it 'should run the task without errors' do
expect { run_rake_task }.to_not raise_error
expect { run_rake_task }.not_to raise_error
end
end
end
......@@ -30,7 +30,7 @@ describe PostReceive do
end
it "asks the project to trigger all hooks" do
Project.stub(find_with_namespace: project)
allow(Project).to receive(:find_with_namespace).and_return(project)
expect(project).to receive(:execute_hooks).twice
expect(project).to receive(:execute_services).twice
expect(project).to receive(:update_merge_requests)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment