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. Please view this file on the master branch, on stable branches it's out of date.
v 7.13.0 (unreleased) 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) - 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) - 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 - Remove project visibility icons from dashboard projects list
...@@ -8,8 +9,12 @@ v 7.13.0 (unreleased) ...@@ -8,8 +9,12 @@ v 7.13.0 (unreleased)
- Allow users to customize their default Dashboard page. - 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 - 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 - 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) 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) - 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 - Update oauth button logos for Twitter and Google to recommended assets
- Fix hooks for web based events with external issue references (Daniel Gerhardt) - Fix hooks for web based events with external issue references (Daniel Gerhardt)
...@@ -48,8 +53,8 @@ v 7.12.0 (unreleased) ...@@ -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) - 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) - Fix new/empty milestones showing 100% completion value (Jonah Bishop)
- Add a note when an Issue or Merge Request's title changes - Add a note when an Issue or Merge Request's title changes
- Consistently refer to MRs as either Accepted or Rejected. - Consistently refer to MRs as either Merged or Closed.
- Add Accepted and Rejected tabs to MR lists. - Add Merged tab to MR lists.
- Prefix EmailsOnPush email subject with `[Git]`. - Prefix EmailsOnPush email subject with `[Git]`.
- Group project contributions by both name and email. - Group project contributions by both name and email.
- Clarify navigation labels for Project Settings and Group Settings. - Clarify navigation labels for Project Settings and Group Settings.
...@@ -61,7 +66,7 @@ v 7.12.0 (unreleased) ...@@ -61,7 +66,7 @@ v 7.12.0 (unreleased)
- Allow to configure a URL to show after sign out - Allow to configure a URL to show after sign out
- Add an option to automatically sign-in with an Omniauth provider - Add an option to automatically sign-in with an Omniauth provider
- Better performance for web editor (switched from satellites to rugged) - 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 - When remove project - move repository and schedule it removal
- Improve group removing logic - Improve group removing logic
- Trigger create-hooks on backup restore task - 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 ...@@ -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). 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 ## 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. 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. 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. 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 This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
reported by emailing contact@gitlab.com
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/) 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/)
\ No newline at end of file
...@@ -34,7 +34,7 @@ gem "browser", '~> 0.8.0' ...@@ -34,7 +34,7 @@ gem "browser", '~> 0.8.0'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # 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 # Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc) # GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
...@@ -222,16 +222,16 @@ group :development do ...@@ -222,16 +222,16 @@ group :development do
end end
group :development, :test do group :development, :test do
gem 'awesome_print'
gem 'byebug'
gem 'pry-rails'
gem 'coveralls', require: false 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 'rubocop', '0.28.0', require: false
gem 'spinach-rails' 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) # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.3.0' gem 'minitest', '~> 5.3.0'
...@@ -239,8 +239,9 @@ group :development, :test do ...@@ -239,8 +239,9 @@ group :development, :test do
# Generate Fake data # Generate Fake data
gem 'ffaker', '~> 2.0.0' gem 'ffaker', '~> 2.0.0'
# PhantomJS driver for Capybara gem 'capybara', '~> 2.3.0'
gem 'poltergeist', '~> 1.5.1' gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.6.0'
gem 'teaspoon', '~> 1.0.0' gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine' gem 'teaspoon-jasmine'
...@@ -249,14 +250,12 @@ group :development, :test do ...@@ -249,14 +250,12 @@ group :development, :test do
gem 'spring-commands-rspec', '~> 1.0.0' gem 'spring-commands-rspec', '~> 1.0.0'
gem 'spring-commands-spinach', '~> 1.0.0' gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem "byebug"
end end
group :test do group :test do
gem 'simplecov', require: false gem 'simplecov', require: false
gem 'shoulda-matchers', '~> 2.8.0', 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 'webmock', '~> 1.21.0'
gem 'test_after_commit' gem 'test_after_commit'
end end
......
...@@ -82,7 +82,7 @@ GEM ...@@ -82,7 +82,7 @@ GEM
columnize (~> 0.8) columnize (~> 0.8)
debugger-linecache (~> 1.2) debugger-linecache (~> 1.2)
cal-heatmap-rails (0.0.1) cal-heatmap-rails (0.0.1)
capybara (2.2.1) capybara (2.3.0)
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
rack (>= 1.0.0) rack (>= 1.0.0)
...@@ -125,7 +125,7 @@ GEM ...@@ -125,7 +125,7 @@ GEM
d3_rails (3.5.5) d3_rails (3.5.5)
railties (>= 3.1.0) railties (>= 3.1.0)
daemons (1.1.9) daemons (1.1.9)
database_cleaner (1.3.0) database_cleaner (1.4.1)
debug_inspector (0.0.2) debug_inspector (0.0.2)
debugger-linecache (1.2.0) debugger-linecache (1.2.0)
default_value_for (3.0.0) default_value_for (3.0.0)
...@@ -154,7 +154,7 @@ GEM ...@@ -154,7 +154,7 @@ GEM
dotenv (0.9.0) dotenv (0.9.0)
dropzonejs-rails (0.4.14) dropzonejs-rails (0.4.14)
rails (> 3.1) rails (> 3.1)
email_spec (1.5.0) email_spec (1.6.0)
launchy (~> 2.1) launchy (~> 2.1)
mail (~> 2.2) mail (~> 2.2)
encryptor (1.3.0) encryptor (1.3.0)
...@@ -266,7 +266,7 @@ GEM ...@@ -266,7 +266,7 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.1.0) gitlab_emoji (0.1.0)
gemojione (~> 2.0) gemojione (~> 2.0)
gitlab_git (7.2.3) gitlab_git (7.2.5)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
...@@ -348,7 +348,7 @@ GEM ...@@ -348,7 +348,7 @@ GEM
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
kgio (2.9.2) kgio (2.9.2)
launchy (2.4.2) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.1.2) letter_opener (1.1.2)
launchy (~> 2.2) launchy (~> 2.2)
...@@ -431,8 +431,8 @@ GEM ...@@ -431,8 +431,8 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
parser (2.2.0.2) parser (2.2.0.2)
ast (>= 1.1, < 3.0) ast (>= 1.1, < 3.0)
pg (0.15.1) pg (0.18.2)
poltergeist (1.5.1) poltergeist (1.6.0)
capybara (~> 2.1) capybara (~> 2.1)
cliver (~> 0.3.1) cliver (~> 0.3.1)
multi_json (~> 1.0) multi_json (~> 1.0)
...@@ -449,7 +449,7 @@ GEM ...@@ -449,7 +449,7 @@ GEM
quiet_assets (1.0.2) quiet_assets (1.0.2)
railties (>= 3.1, < 5.0) railties (>= 3.1, < 5.0)
racc (1.4.10) racc (1.4.10)
rack (1.5.4) rack (1.5.5)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (4.3.0) rack-attack (4.3.0)
...@@ -530,21 +530,23 @@ GEM ...@@ -530,21 +530,23 @@ GEM
rqrcode (0.4.2) rqrcode (0.4.2)
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2) rqrcode (>= 0.4.2)
rspec-collection_matchers (1.1.2) rspec-core (3.3.1)
rspec-expectations (>= 2.99.0.beta1) rspec-support (~> 3.3.0)
rspec-core (2.99.2) rspec-expectations (3.3.0)
rspec-expectations (2.99.2) diff-lcs (>= 1.2.0, < 2.0)
diff-lcs (>= 1.1.3, < 2.0) rspec-support (~> 3.3.0)
rspec-mocks (2.99.3) rspec-mocks (3.3.0)
rspec-rails (2.99.0) diff-lcs (>= 1.2.0, < 2.0)
actionpack (>= 3.0) rspec-support (~> 3.3.0)
activemodel (>= 3.0) rspec-rails (3.3.2)
activesupport (>= 3.0) actionpack (>= 3.0, < 4.3)
railties (>= 3.0) activesupport (>= 3.0, < 4.3)
rspec-collection_matchers railties (>= 3.0, < 4.3)
rspec-core (~> 2.99.0) rspec-core (~> 3.3.0)
rspec-expectations (~> 2.99.0) rspec-expectations (~> 3.3.0)
rspec-mocks (~> 2.99.0) rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
rubocop (0.28.0) rubocop (0.28.0)
astrolabe (~> 1.3) astrolabe (~> 1.3)
parser (>= 2.2.0.pre.7, < 3.0) parser (>= 2.2.0.pre.7, < 3.0)
...@@ -707,7 +709,9 @@ GEM ...@@ -707,7 +709,9 @@ GEM
webmock (1.21.0) webmock (1.21.0)
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) 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) wikicloth (0.8.1)
builder builder
expression_parser expression_parser
...@@ -735,7 +739,7 @@ DEPENDENCIES ...@@ -735,7 +739,7 @@ DEPENDENCIES
browser (~> 0.8.0) browser (~> 0.8.0)
byebug byebug
cal-heatmap-rails (~> 0.0.1) cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1) capybara (~> 2.3.0)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave carrierwave
charlock_holmes charlock_holmes
...@@ -744,7 +748,7 @@ DEPENDENCIES ...@@ -744,7 +748,7 @@ DEPENDENCIES
coveralls coveralls
creole (~> 0.3.6) creole (~> 0.3.6)
d3_rails (~> 3.5.5) d3_rails (~> 3.5.5)
database_cleaner database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
devise (= 3.2.4) devise (= 3.2.4)
devise-async (= 0.9.0) devise-async (= 0.9.0)
...@@ -752,7 +756,7 @@ DEPENDENCIES ...@@ -752,7 +756,7 @@ DEPENDENCIES
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (= 2.1.3) doorkeeper (= 2.1.3)
dropzonejs-rails dropzonejs-rails
email_spec email_spec (~> 1.6.0)
enumerize enumerize
factory_girl_rails factory_girl_rails
ffaker (~> 2.0.0) ffaker (~> 2.0.0)
...@@ -765,7 +769,7 @@ DEPENDENCIES ...@@ -765,7 +769,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.2) gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1) gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1) gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.3) gitlab_git (~> 7.2.5)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1) gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2) gollum-lib (~> 4.0.2)
...@@ -800,7 +804,7 @@ DEPENDENCIES ...@@ -800,7 +804,7 @@ DEPENDENCIES
omniauth-twitter omniauth-twitter
org-ruby (= 0.9.12) org-ruby (= 0.9.12)
pg pg
poltergeist (~> 1.5.1) poltergeist (~> 1.6.0)
pry-rails pry-rails
quiet_assets (~> 1.0.1) quiet_assets (~> 1.0.1)
rack-attack (~> 4.3.0) rack-attack (~> 4.3.0)
...@@ -815,7 +819,7 @@ DEPENDENCIES ...@@ -815,7 +819,7 @@ DEPENDENCIES
request_store request_store
rerun (~> 0.10.0) rerun (~> 0.10.0)
rqrcode-rails3 rqrcode-rails3
rspec-rails (= 2.99) rspec-rails (~> 3.3.0)
rubocop (= 0.28.0) rubocop (= 0.28.0)
rugments (~> 1.0.0.beta7) rugments (~> 1.0.0.beta7)
sanitize (~> 2.0) 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 @@ $ -> ...@@ -141,8 +141,7 @@ $ ->
$('.trigger-submit').on 'change', -> $('.trigger-submit').on 'change', ->
$(@).parents('form').submit() $(@).parents('form').submit()
$("abbr.timeago").timeago() $('abbr.timeago, .js-timeago').timeago()
$('.js-timeago').timeago()
# Flash # Flash
if (flash = $(".flash-container")).length > 0 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 ...@@ -87,7 +87,7 @@ class Dispatcher
new TreeView() new TreeView()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:blob:show' when 'projects:blob:show'
new BlobView() new LineHighlighter()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:labels:new', 'projects:labels:edit' when 'projects:labels:new', 'projects:labels:edit'
new Labels() 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 jquery.waitforimages
#= require task_list #= require task_list
#= require merge_request_tabs
class @MergeRequest class @MergeRequest
# Initialize MergeRequest behavior # Initialize MergeRequest behavior
# #
# Options: # Options:
# action - String, current controller action # 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) -> constructor: (@opts) ->
@initContextWidget() @initContextWidget()
this.$el = $('.merge-request') 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.$('.show-all-commits').on 'click', =>
this.showAllCommits() this.showAllCommits()
# `MergeRequests#new` has no tab-persisting or lazy-loading behavior
unless @opts.action == 'new'
new MergeRequestTabs(@opts)
# Prevent duplicate event bindings # Prevent duplicate event bindings
@disableTaskList() @disableTaskList()
...@@ -52,83 +48,6 @@ class @MergeRequest ...@@ -52,83 +48,6 @@ class @MergeRequest
$(".context .inline-update").on "change", "#merge_request_assignee_id", -> $(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit() $(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: -> showAllCommits: ->
this.$('.first-commits').remove() this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide' 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 { ...@@ -10,6 +10,10 @@ header {
.center-logo { .center-logo {
margin: 8px 0; margin: 8px 0;
text-align: center; text-align: center;
img {
height: 32px;
}
} }
} }
......
class DashboardController < Dashboard::ApplicationController class DashboardController < Dashboard::ApplicationController
before_action :load_projects, except: [:projects] before_action :load_projects
before_action :event_filter, only: :show before_action :event_filter, only: :show
respond_to :html respond_to :html
def show def show
......
...@@ -24,7 +24,7 @@ class PasswordsController < Devise::PasswordsController ...@@ -24,7 +24,7 @@ class PasswordsController < Devise::PasswordsController
super do |resource| super do |resource|
# TODO (rspeicher): In Devise master (> 3.4.1), we can set # TODO (rspeicher): In Devise master (> 3.4.1), we can set
# `Devise.sign_in_after_reset_password = false` and avoid this mess. # `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) resource.unlock_access! if unlockable?(resource)
# Since we are not signing this user in, we use the :updated_not_active # Since we are not signing this user in, we use the :updated_not_active
......
...@@ -10,7 +10,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -10,7 +10,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def create def create
if current_user.valid_otp?(params[:pin_code]) 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! @codes = current_user.generate_otp_backup_codes!
current_user.save! current_user.save!
...@@ -30,7 +30,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -30,7 +30,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def destroy def destroy
current_user.update_attributes({ current_user.update_attributes({
otp_required_for_login: false, two_factor_enabled: false,
encrypted_otp_secret: nil, encrypted_otp_secret: nil,
encrypted_otp_secret_iv: nil, encrypted_otp_secret_iv: nil,
encrypted_otp_secret_salt: nil, encrypted_otp_secret_salt: nil,
......
...@@ -71,7 +71,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -71,7 +71,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def commits 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 end
def new def new
......
...@@ -57,7 +57,7 @@ class SessionsController < Devise::SessionsController ...@@ -57,7 +57,7 @@ class SessionsController < Devise::SessionsController
def authenticate_with_two_factor def authenticate_with_two_factor
user = self.resource = find_user 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 user_params[:otp_attempt].present? && session[:otp_user_id]
if valid_otp_attempt?(user) if valid_otp_attempt?(user)
......
...@@ -45,10 +45,10 @@ class IssuableFinder ...@@ -45,10 +45,10 @@ class IssuableFinder
def group def group
return @group if defined?(@group) return @group if defined?(@group)
@group = @group =
if params[:group_id].present? if params[:group_id].present?
Group.find(params[:group_id]) Group.find(params[:group_id])
else else
nil nil
end end
end end
...@@ -56,10 +56,10 @@ class IssuableFinder ...@@ -56,10 +56,10 @@ class IssuableFinder
def project def project
return @project if defined?(@project) return @project if defined?(@project)
@project = @project =
if params[:project_id].present? if params[:project_id].present?
Project.find(params[:project_id]) Project.find(params[:project_id])
else else
nil nil
end end
end end
...@@ -76,7 +76,7 @@ class IssuableFinder ...@@ -76,7 +76,7 @@ class IssuableFinder
return @milestones if defined?(@milestones) return @milestones if defined?(@milestones)
@milestones = @milestones =
if milestones? && params[:milestone_title] != NONE if milestones? && params[:milestone_title] != NONE
Milestone.where(title: params[:milestone_title]) Milestone.where(title: params[:milestone_title])
else else
nil nil
...@@ -90,7 +90,7 @@ class IssuableFinder ...@@ -90,7 +90,7 @@ class IssuableFinder
def assignee def assignee
return @assignee if defined?(@assignee) return @assignee if defined?(@assignee)
@assignee = @assignee =
if assignee? && params[:assignee_id] != NONE if assignee? && params[:assignee_id] != NONE
User.find(params[:assignee_id]) User.find(params[:assignee_id])
else else
...@@ -105,7 +105,7 @@ class IssuableFinder ...@@ -105,7 +105,7 @@ class IssuableFinder
def author def author
return @author if defined?(@author) return @author if defined?(@author)
@author = @author =
if author? && params[:author_id] != NONE if author? && params[:author_id] != NONE
User.find(params[:author_id]) User.find(params[:author_id])
else else
...@@ -148,8 +148,6 @@ class IssuableFinder ...@@ -148,8 +148,6 @@ class IssuableFinder
case params[:state] case params[:state]
when 'closed' when 'closed'
items.closed items.closed
when 'rejected'
items.respond_to?(:rejected) ? items.rejected : items.closed
when 'merged' when 'merged'
items.respond_to?(:merged) ? items.merged : items.closed items.respond_to?(:merged) ? items.merged : items.closed
when 'all' when 'all'
......
...@@ -16,6 +16,6 @@ module AppearancesHelper ...@@ -16,6 +16,6 @@ module AppearancesHelper
end end
def brand_header_logo def brand_header_logo
image_tag 'logo-white.png' image_tag 'logo.svg'
end end
end end
...@@ -179,14 +179,33 @@ module ApplicationHelper ...@@ -179,14 +179,33 @@ module ApplicationHelper
BroadcastMessage.current BroadcastMessage.current
end end
def time_ago_with_tooltip(date, placement = 'top', html_class = 'time_ago') # Render a `time` element with Javascript-based relative date and tooltip
capture_haml do #
haml_tag :time, date.to_s, # time - Time object
class: html_class, datetime: date.getutc.iso8601, title: date.in_time_zone.stamp('Aug 21, 2011 9:23pm'), # placement - Tooltip placement String (default: "top")
data: { toggle: 'tooltip', placement: placement } # html_class - Custom class for `time` element (default: "time_ago")
# skip_js - When true, exclude the `script` tag (default: false)
haml_tag :script, "$('." + html_class + "').timeago().tooltip()" #
end.html_safe # 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 }
element += javascript_tag "$('.js-timeago').timeago()" unless skip_js
element
end end
def render_markup(file_name, file_content) def render_markup(file_name, file_content)
...@@ -214,39 +233,6 @@ module ApplicationHelper ...@@ -214,39 +233,6 @@ module ApplicationHelper
Gitlab::MarkupHelper.asciidoc?(filename) Gitlab::MarkupHelper.asciidoc?(filename)
end 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 def promo_host
'about.gitlab.com' 'about.gitlab.com'
end end
...@@ -295,10 +281,9 @@ module ApplicationHelper ...@@ -295,10 +281,9 @@ module ApplicationHelper
def state_filters_text_for(entity, project) def state_filters_text_for(entity, project)
titles = { titles = {
opened: "Open", opened: "Open"
merged: "Accepted"
} }
entity_title = titles[entity] || entity.to_s.humanize entity_title = titles[entity] || entity.to_s.humanize
count = count =
......
module BroadcastMessagesHelper module BroadcastMessagesHelper
def broadcast_styling(broadcast_message) def broadcast_styling(broadcast_message)
if(broadcast_message.color || broadcast_message.font) styling = ''
"background-color:#{broadcast_message.color};color:#{broadcast_message.font}"
else if broadcast_message.color.present?
"" styling << "background-color: #{broadcast_message.color}"
styling << '; ' if broadcast_message.font.present?
end end
if broadcast_message.font.present?
styling << "color: #{broadcast_message.font}"
end
styling
end end
end end
module IconsHelper module IconsHelper
include FontAwesome::Rails::IconHelper
# Creates an icon tag given icon name(s) and possible icon modifiers. # Creates an icon tag given icon name(s) and possible icon modifiers.
# #
# Right now this method simply delegates directly to `fa_icon` from the # Right now this method simply delegates directly to `fa_icon` from the
......
...@@ -45,13 +45,13 @@ module IssuesHelper ...@@ -45,13 +45,13 @@ module IssuesHelper
def issue_timestamp(issue) def issue_timestamp(issue)
# Shows the created at time and the updated at time if different # 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 if issue.updated_at != issue.created_at
ts << capture_haml do ts << capture_haml do
haml_tag :span do haml_tag :span do
haml_concat '&middot;' haml_concat '&middot;'
haml_concat icon('edit', title: 'edited') 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 end
end end
......
...@@ -25,13 +25,13 @@ module NotesHelper ...@@ -25,13 +25,13 @@ module NotesHelper
def note_timestamp(note) def note_timestamp(note)
# Shows the created at time and the updated at time if different # 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 if note.updated_at != note.created_at
ts << capture_haml do ts << capture_haml do
haml_tag :span do haml_tag :span do
haml_concat '&middot;' haml_concat '&middot;'
haml_concat icon('edit', title: 'edited') 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 end
end end
......
module NotificationsHelper module NotificationsHelper
include IconsHelper
def notification_icon(notification) def notification_icon(notification)
if notification.disabled? if notification.disabled?
icon('volume-off', class: 'ns-mute') icon('volume-off', class: 'ns-mute')
......
...@@ -211,7 +211,7 @@ module ProjectsHelper ...@@ -211,7 +211,7 @@ module ProjectsHelper
def project_last_activity(project) def project_last_activity(project)
if project.last_activity_at 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 else
"Never" "Never"
end end
......
...@@ -263,7 +263,7 @@ class Ability ...@@ -263,7 +263,7 @@ class Ability
:"modify_#{name}", :"modify_#{name}",
] ]
else else
if subject.respond_to?(:project) if subject.respond_to?(:project) && subject.project
project_abilities(user, subject.project) project_abilities(user, subject.project)
else else
[] []
......
...@@ -75,7 +75,7 @@ module Mentionable ...@@ -75,7 +75,7 @@ module Mentionable
refs.reject! { |ref| without.include?(ref) } refs.reject! { |ref| without.include?(ref) }
refs.each do |ref| refs.each do |ref|
Note.create_cross_reference_note(ref, local_reference, a) SystemNoteService.cross_reference(ref, local_reference, a)
end end
end end
......
...@@ -125,16 +125,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -125,16 +125,14 @@ class MergeRequest < ActiveRecord::Base
validate :validate_fork 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 :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 :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 :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } 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 :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) } scope :of_projects, ->(ids) { where(target_project_id: ids) }
# Closed scope for merge request should return scope :merged, -> { with_state(:merged) }
# both merged and closed mr's scope :closed, -> { with_state(:closed) }
scope :closed, -> { with_states(:closed, :merged) } scope :closed_and_merged, -> { with_states(:closed, :merged) }
scope :rejected, -> { with_states(:closed) }
def self.reference_prefix def self.reference_prefix
'!' '!'
...@@ -417,4 +415,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -417,4 +415,14 @@ class MergeRequest < ActiveRecord::Base
def can_be_merged_by?(user) def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch) ::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
end end
def state_human_name
if merged?
"Merged"
elsif closed?
"Closed"
else
"Open"
end
end
end end
...@@ -56,7 +56,7 @@ class Milestone < ActiveRecord::Base ...@@ -56,7 +56,7 @@ class Milestone < ActiveRecord::Base
end end
def closed_items_count 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 end
def total_items_count def total_items_count
......
...@@ -63,11 +63,6 @@ class Note < ActiveRecord::Base ...@@ -63,11 +63,6 @@ class Note < ActiveRecord::Base
after_update :set_references after_update :set_references
class << self class << self
# TODO (rspeicher): Update usages
def create_cross_reference_note(*args)
SystemNoteService.cross_reference(*args)
end
def discussions_from_notes(notes) def discussions_from_notes(notes)
discussion_ids = [] discussion_ids = []
discussions = [] discussions = []
......
...@@ -5,8 +5,13 @@ class Repository ...@@ -5,8 +5,13 @@ class Repository
def initialize(path_with_namespace, default_branch = nil, project = nil) def initialize(path_with_namespace, default_branch = nil, project = nil)
@path_with_namespace = path_with_namespace @path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
@project = project @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 rescue Gitlab::Git::Repository::NoRepository
nil nil
end end
......
...@@ -34,7 +34,6 @@ class Snippet < ActiveRecord::Base ...@@ -34,7 +34,6 @@ class Snippet < ActiveRecord::Base
validates :author, presence: true validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { within: 0..255 }
validates :file_name, validates :file_name,
presence: true,
length: { within: 0..255 }, length: { within: 0..255 },
format: { with: Gitlab::Regex.file_name_regex, format: { with: Gitlab::Regex.file_name_regex,
message: Gitlab::Regex.file_name_regex_message } message: Gitlab::Regex.file_name_regex_message }
......
...@@ -172,6 +172,9 @@ class User < ActiveRecord::Base ...@@ -172,6 +172,9 @@ class User < ActiveRecord::Base
after_create :post_create_hook after_create :post_create_hook
after_destroy :post_destroy_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 alias_attribute :private_token, :authentication_token
...@@ -220,10 +223,26 @@ class User < ActiveRecord::Base ...@@ -220,10 +223,26 @@ class User < ActiveRecord::Base
end end
def find_for_commit(email, name) def find_for_commit(email, name)
# Prefer email match over name match user_table = arel_table
User.where(email: email).first || email_table = Email.arel_table
User.joins(:emails).where(emails: { email: email }).first ||
User.where(name: name).first # 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 end
def filter(filter_name) def filter(filter_name)
...@@ -297,6 +316,18 @@ class User < ActiveRecord::Base ...@@ -297,6 +316,18 @@ class User < ActiveRecord::Base
@reset_token @reset_token
end 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 def namespace_uniq
namespace_name = self.username namespace_name = self.username
existing_namespace = Namespace.by_path(namespace_name) existing_namespace = Namespace.by_path(namespace_name)
...@@ -704,8 +735,4 @@ class User < ActiveRecord::Base ...@@ -704,8 +735,4 @@ class User < ActiveRecord::Base
def can_be_removed? def can_be_removed?
!solo_owned_groups.present? !solo_owned_groups.present?
end end
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars]
end end
...@@ -105,7 +105,7 @@ class GitPushService ...@@ -105,7 +105,7 @@ class GitPushService
author ||= commit_user(commit) author ||= commit_user(commit)
refs.each do |r| refs.each do |r|
Note.create_cross_reference_note(r, commit, author) SystemNoteService.cross_reference(r, commit, author)
end end
end end
end end
......
...@@ -15,7 +15,7 @@ module Notes ...@@ -15,7 +15,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that names an # Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit. # issue, merge request, or commit.
note.references.each do |mentioned| 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
execute_hooks(note) execute_hooks(note)
......
...@@ -13,7 +13,7 @@ module Notes ...@@ -13,7 +13,7 @@ module Notes
# Create a cross-reference note if this Note contains GFM that # Create a cross-reference note if this Note contains GFM that
# names an issue, merge request, or commit. # names an issue, merge request, or commit.
note.references.each do |mentioned| 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 end
end end
......
...@@ -38,6 +38,14 @@ ...@@ -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 = 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 %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 %li
%span.light Can create groups: %span.light Can create groups:
%strong %strong
......
%header.navbar.navbar-fixed-top.navbar-empty %header.navbar.navbar-fixed-top.navbar-empty
.container .container
.center-logo .center-logo
= image_tag 'logo-white.png', width: 32, height: 32 = brand_header_logo
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
.panel-heading .panel-heading
Two-factor Authentication Two-factor Authentication
.panel-body .panel-body
- if current_user.otp_required_for_login - if current_user.two_factor_enabled?
.pull-right .pull-right
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm', = link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
data: { confirm: 'Are you sure?' } data: { confirm: 'Are you sure?' }
......
...@@ -15,10 +15,10 @@ ...@@ -15,10 +15,10 @@
- if issuable.is_a?(MergeRequest) - if issuable.is_a?(MergeRequest)
%p.help-block %p.help-block
- if issuable.work_in_progress? - if issuable.work_in_progress?
Remove the <code>WIP</code> prefix from the title to allow this Remove the <code>WIP</code> prefix from the title to allow this
<strong>Work In Progress</strong> merge request to be accepted when it's ready. <strong>Work In Progress</strong> merge request to be accepted when it's ready.
- else - else
Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a
<strong>Work In Progress</strong> merge request from being accepted before it's ready. <strong>Work In Progress</strong> merge request from being accepted before it's ready.
.form-group.issuable-description .form-group.issuable-description
= f.label :description, 'Description', class: 'control-label' = f.label :description, 'Description', class: 'control-label'
...@@ -81,21 +81,22 @@ ...@@ -81,21 +81,22 @@
- if issuable.is_a?(MergeRequest) - if issuable.is_a?(MergeRequest)
%hr %hr
- unless @merge_request.persisted? - if @merge_request.new_record?
.form-group .form-group
= f.label :source_branch, class: 'control-label' do = f.label :source_branch, class: 'control-label' do
%i.fa.fa-code-fork %i.fa.fa-code-fork
Source Branch Source Branch
.col-sm-10 .col-sm-10
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true }) = 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 .form-group
= f.label :target_branch, class: 'control-label' do = f.label :target_branch, class: 'control-label' do
%i.fa.fa-code-fork %i.fa.fa-code-fork
Target Branch Target Branch
.col-sm-10 .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 .form-actions
- if !issuable.project.empty_repo? && (guide_url = contribution_guide_url(issuable.project)) && !issuable.persisted? - if !issuable.project.empty_repo? && (guide_url = contribution_guide_url(issuable.project)) && !issuable.persisted?
......
...@@ -29,5 +29,5 @@ ...@@ -29,5 +29,5 @@
= commit_author_link(commit, avatar: true, size: 24) = commit_author_link(commit, avatar: true, size: 24)
authored authored
.committed_ago .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) = link_to_browse_code(project, commit)
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
= 0 = 0
.issue-info .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 - if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue = render 'votes/votes_inline', votable: issue
- if issue.milestone - if issue.milestone
...@@ -41,4 +41,4 @@ ...@@ -41,4 +41,4 @@
= issue.task_status = issue.task_status
.pull-right.issue-updated-at .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 @@ ...@@ -9,11 +9,11 @@
- if merge_request.merged? - if merge_request.merged?
%span %span
%i.fa.fa-check %i.fa.fa-check
ACCEPTED MERGED
- elsif merge_request.closed? - elsif merge_request.closed?
%span %span
%i.fa.fa-ban %i.fa.fa-ban
REJECTED CLOSED
- else - else
%span.hidden-xs.hidden-sm %span.hidden-xs.hidden-sm
%span.label-branch< %span.label-branch<
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
= 0 = 0
.merge-request-info .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 - if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request = render 'votes/votes_inline', votable: merge_request
- if merge_request.milestone_id? - if merge_request.milestone_id?
...@@ -48,4 +48,4 @@ ...@@ -48,4 +48,4 @@
= merge_request.task_status = merge_request.task_status
.pull-right.hidden-xs .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,7 +56,8 @@ ...@@ -56,7 +56,8 @@
#notes.notes.tab-pane.voting_notes #notes.notes.tab-pane.voting_notes
= render "projects/merge_requests/discussion" = render "projects/merge_requests/discussion"
#commits.commits.tab-pane #commits.commits.tab-pane
= render "projects/merge_requests/show/commits" - if current_page?(action: 'commits')
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane #diffs.diffs.tab-pane
- if current_page?(action: 'diffs') - if current_page?(action: 'diffs')
= render "projects/merge_requests/show/diffs" = render "projects/merge_requests/show/diffs"
...@@ -64,7 +65,6 @@ ...@@ -64,7 +65,6 @@
.mr-loading-status .mr-loading-status
= spinner = spinner
:javascript :javascript
var merge_request; var merge_request;
......
%h4.page-title %h4.page-title
.issue-box{ class: issue_box_class(@merge_request) } .issue-box{ class: issue_box_class(@merge_request) }
- if @merge_request.merged? = @merge_request.state_human_name
Accepted
- elsif @merge_request.closed?
Rejected
- else
Open
= "Merge Request ##{@merge_request.iid}" = "Merge Request ##{@merge_request.iid}"
%small.creator %small.creator
&middot; &middot;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= render 'projects/merge_requests/widget/heading' = render 'projects/merge_requests/widget/heading'
.mr-widget-body .mr-widget-body
%h4 %h4
Rejected Closed
- if @merge_request.closed_event - if @merge_request.closed_event
by #{link_to_member(@project, @merge_request.closed_event.author, avatar: true)} by #{link_to_member(@project, @merge_request.closed_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)} #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= render 'projects/merge_requests/widget/heading' = render 'projects/merge_requests/widget/heading'
.mr-widget-body .mr-widget-body
%h4 %h4
Accepted Merged
- if @merge_request.merge_event - if @merge_request.merge_event
by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)} by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)} #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
......
...@@ -86,10 +86,10 @@ ...@@ -86,10 +86,10 @@
.col-md-3 .col-md-3
= render('merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: @merge_requests.opened.assigned, id: 'ongoing') = render('merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: @merge_requests.opened.assigned, id: 'ongoing')
.col-md-3 .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 .col-md-3
.panel.panel-primary .panel.panel-primary
.panel-heading Accepted .panel-heading Merged
%ul.well-list %ul.well-list
- @merge_requests.merged.each do |merge_request| - @merge_requests.merged.each do |merge_request|
= render 'merge_request', merge_request: merge_request = render 'merge_request', merge_request: merge_request
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= link_to_member(@project, last_note.author, avatar: false) = link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update %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 .discussion-body.js-toggle-content
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note = render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
last updated by last updated by
= link_to_member(@project, last_note.author, avatar: false) = link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update %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 .discussion-body.js-toggle-content
- if note.for_diff_line? - if note.for_diff_line?
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note = render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
......
...@@ -14,6 +14,6 @@ ...@@ -14,6 +14,6 @@
last updated by last updated by
= link_to_member(@project, last_note.author, avatar: false) = link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update %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 .discussion-body.js-toggle-content.hide
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note = render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
...@@ -11,6 +11,6 @@ ...@@ -11,6 +11,6 @@
#{merge_request.project.name_with_namespace} #{merge_request.project.name_with_namespace}
.pull-right .pull-right
- if merge_request.merged? - if merge_request.merged?
%span.label.label-primary Accepted %span.label.label-primary Merged
- elsif merge_request.closed? - elsif merge_request.closed?
%span.label.label-danger Rejected %span.label.label-danger Closed
.file-content.code{class: user_color_scheme_class} .file-content.code{class: user_color_scheme_class}
.line-numbers .line-numbers
- if blob.data.present? - 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 - offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset - i = index + offset
/ We're not using `link_to` because it is too slow once we get to thousands of lines. -# 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}"} %a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
%i.fa.fa-link %i.fa.fa-link
= i = i
:preserve :preserve
......
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
= icon('check-circle') = icon('check-circle')
#{state_filters_text_for(:merged, @project)} #{state_filters_text_for(:merged, @project)}
%li{class: ("active" if params[:state] == 'rejected')} %li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'rejected') do = link_to page_filter_path(state: 'closed') do
= icon('ban') = icon('ban')
#{state_filters_text_for(:rejected, @project)} #{state_filters_text_for(:closed, @project)}
- else - else
%li{class: ("active" if params[:state] == 'closed')} %li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do = link_to page_filter_path(state: 'closed') do
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
.col-sm-10 .col-sm-10
.file-holder.snippet .file-holder.snippet
.file-title .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 .file-content.code
%pre#editor= @snippet.content %pre#editor= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content' = f.hidden_field :content, class: 'snippet-file-content'
......
...@@ -9,7 +9,8 @@ ...@@ -9,7 +9,8 @@
.row .row
%section.col-md-8 %section.col-md-8
.header-with-avatar .header-with-avatar
= image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: '' = link_to avatar_icon(@user.email), target: '_blank' do
= image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
%h3 %h3
= @user.name = @user.name
- if @user == current_user - if @user == current_user
......
# 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: ...@@ -368,6 +368,9 @@ Make sure to edit the config file to match your setup:
# Change YOUR_SERVER_FQDN to the fully-qualified # Change YOUR_SERVER_FQDN to the fully-qualified
# domain name of your host serving GitLab. # 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 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. **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 ...@@ -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 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. missing some icons. In that case, try to precompile the assets again.
For Omnibus-packages: Note that this only applies to source installations and does NOT apply to
``` omnibus packages.
sudo gitlab-rake assets:precompile
```
For installations from source: For installations from source:
``` ```
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production 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) ...@@ -61,7 +61,7 @@ Xth: (3 working days before the 22nd)
Xth: (2 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) - [ ] Check that MVP is added to the mvp page (source/mvp/index.html in www-gitlab-com)
Xth: (1 working day before the 22nd) Xth: (1 working day before the 22nd)
......
...@@ -30,7 +30,7 @@ cat ~/.ssh/id_rsa.pub ...@@ -30,7 +30,7 @@ cat ~/.ssh/id_rsa.pub
Copy-paste the key to the 'My SSH Keys' section under the 'SSH' tab in your Copy-paste the key to the 'My SSH Keys' section under the 'SSH' tab in your
user profile. Please copy the complete key starting with `ssh-` and ending user profile. Please copy the complete key starting with `ssh-` and ending
with your username and host. with your username and host.
Use code below to copy your public key to the clipboard. Depending on your Use code below to copy your public key to the clipboard. Depending on your
OS you'll need to use a different command: OS you'll need to use a different command:
...@@ -77,3 +77,31 @@ information. ...@@ -77,3 +77,31 @@ information.
### Eclipse ### Eclipse
How to add your ssh key to Eclipse: http://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration 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 ...@@ -39,6 +39,7 @@ Feature: Project Commits Comments
@javascript @javascript
Scenario: I can delete a comment Scenario: I can delete a comment
Given I leave a comment like "XML attached" Given I leave a comment like "XML attached"
Then I should see a comment saying "XML attached"
And I delete a comment And I delete a comment
Then I should not see a comment saying "XML attached" Then I should not see a comment saying "XML attached"
......
...@@ -11,7 +11,7 @@ Feature: Project Merge Requests ...@@ -11,7 +11,7 @@ Feature: Project Merge Requests
And I should not see "Feature NS-03" in merge requests And I should not see "Feature NS-03" in merge requests
Scenario: I should see rejected 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 Then I should see "Feature NS-03" in merge requests
And I should not see "Bug NS-04" 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 ...@@ -69,7 +69,7 @@ Feature: Project Wiki
And I click on the "Pages" button And I click on the "Pages" button
Then I should see non-escaped link in the pages list Then I should see non-escaped link in the pages list
@javascript @focus @javascript
Scenario: Creating an invalid new page Scenario: Creating an invalid new page
Given I create a New page with an invalid name Given I create a New page with an invalid name
Then I should see an error message Then I should see an error message
......
...@@ -25,4 +25,15 @@ Feature: Snippets ...@@ -25,4 +25,15 @@ Feature: Snippets
Scenario: I destroy "Personal snippet one" Scenario: I destroy "Personal snippet one"
Given I visit snippet page "Personal snippet one" Given I visit snippet page "Personal snippet one"
And I click link "Destroy" And I click link "Destroy"
Then I should not see "Personal snippet one" in snippets Then I should not see "Personal snippet one" in snippets
\ No newline at end of file
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 ...@@ -36,6 +36,6 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
step 'I should see a customized broadcast message' do 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_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
end end
...@@ -11,9 +11,9 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps ...@@ -11,9 +11,9 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
end end
step 'I should see application settings saved' do step 'I should see application settings saved' do
expect(current_application_settings.gravatar_enabled).to be_false expect(current_application_settings.gravatar_enabled).to be_falsey
expect(current_application_settings.home_page_url).to eq 'https://about.gitlab.com/' expect(current_application_settings.home_page_url).to eq "https://about.gitlab.com/"
expect(page).to have_content 'Application settings saved successfully' expect(page).to have_content "Application settings saved successfully"
end end
step 'I click on "Service Templates"' do step 'I click on "Service Templates"' do
......
...@@ -10,7 +10,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps ...@@ -10,7 +10,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
end end
step 'I see "New project" page' do step 'I see "New project" page' do
expect(page).to have_content("Project path") expect(page).to have_content('Project path')
end end
step 'I click on "Import project from GitHub"' do step 'I click on "Import project from GitHub"' do
......
...@@ -154,7 +154,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps ...@@ -154,7 +154,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end end
step 'I should not see group "Owned" avatar' do 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 end
step 'I should not see the "Remove avatar" button' do step 'I should not see the "Remove avatar" button' do
......
...@@ -53,7 +53,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -53,7 +53,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end end
step 'I should see my gravatar' do step 'I should see my gravatar' do
expect(@user.avatar?).to be_false expect(@user.avatar?).to eq false
end end
step 'I should not see the "Remove avatar" button' do step 'I should not see the "Remove avatar" button' do
...@@ -87,11 +87,15 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -87,11 +87,15 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end end
step "I should see a missing password error message" do step "I should see a missing password error message" do
expect(page).to have_content "You must provide a valid current password" page.within ".flash-container" do
expect(page).to have_content "You must provide a valid current password"
end
end end
step "I should see a password error message" do step "I should see a password error message" do
expect(page).to have_content "Password confirmation doesn't match" page.within '.alert' do
expect(page).to have_content "Password confirmation doesn't match"
end
end end
step 'I reset my token' do step 'I reset my token' do
...@@ -120,7 +124,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -120,7 +124,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step "I am not an ldap user" do step "I am not an ldap user" do
current_user.identities.delete current_user.identities.delete
expect(current_user.ldap_user?).to be_false expect(current_user.ldap_user?).to eq false
end end
step 'I redirected to expired password page' do step 'I redirected to expired password page' do
......
...@@ -19,8 +19,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -19,8 +19,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
click_link "All" click_link "All"
end end
step 'I click link "Rejected"' do step 'I click link "Closed"' do
click_link "Rejected" click_link "Closed"
end end
step 'I should see merge request "Wiki Feature"' do step 'I should see merge request "Wiki Feature"' do
...@@ -31,8 +31,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -31,8 +31,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see closed merge request "Bug NS-04"' do step 'I should see closed merge request "Bug NS-04"' do
merge_request = MergeRequest.find_by!(title: "Bug NS-04") merge_request = MergeRequest.find_by!(title: "Bug NS-04")
expect(merge_request.closed?).to be_true expect(merge_request).to be_closed
expect(page).to have_content "Rejected by" expect(page).to have_content "Closed by"
end end
step 'I should see merge request "Bug NS-04"' do step 'I should see merge request "Bug NS-04"' do
...@@ -125,7 +125,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -125,7 +125,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
expect(buttons.count).to eq(2) expect(buttons.count).to eq(2)
buttons.each do |b| buttons.each do |b|
expect(expect(b['href'])).not_to have_content('json') expect(b['href']).not_to have_content('json')
end end
end end
...@@ -164,20 +164,26 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -164,20 +164,26 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I should see a discussion has started on diff' do step 'I should see a discussion has started on diff' do
expect(page).to have_content "#{current_user.name} started a discussion" page.within(".notes .discussion") do
expect(page).to have_content sample_commit.line_code_path page.should have_content "#{current_user.name} started a discussion"
expect(page).to have_content "Line is wrong" page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
end
end end
step 'I should see a discussion has started on commit diff' do 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" page.within(".notes .discussion") do
expect(page).to have_content sample_commit.line_code_path page.should have_content "#{current_user.name} started a discussion on commit"
expect(page).to have_content "Line is wrong" page.should have_content sample_commit.line_code_path
page.should have_content "Line is wrong"
end
end end
step 'I should see a discussion has started on commit' do step 'I should see a discussion has started on commit' do
expect(page).to have_content "#{current_user.name} started a discussion on commit" page.within(".notes .discussion") do
expect(page).to have_content "One comment to rule them all" page.should have_content "#{current_user.name} started a discussion on commit"
page.should have_content "One comment to rule them all"
end
end end
step 'merge request is mergeable' do step 'merge request is mergeable' do
...@@ -206,7 +212,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -206,7 +212,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see merged request' do step 'I should see merged request' do
page.within '.issue-box' do page.within '.issue-box' do
expect(page).to have_content "Accepted" expect(page).to have_content "Merged"
end end
end end
...@@ -329,12 +335,13 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -329,12 +335,13 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
def leave_comment(message) 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 fill_in "note_note", with: message
click_button "Add Comment" click_button "Add Comment"
end end
page.within(".notes_holder", visible: true) do
expect(page).to have_content message expect(page).to have_content message
end
end end
def init_diff_note_first_file def init_diff_note_first_file
......
...@@ -59,7 +59,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -59,7 +59,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end end
step 'I should see the default project avatar' do step 'I should see the default project avatar' do
expect(@project.avatar?).to be_false expect(@project.avatar?).to eq false
end end
step 'I should not see the "Remove avatar" button' do 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 ...@@ -52,7 +52,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end end
step 'I should see code results for project "Shop"' do 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 end
step 'I search for "Contibuting"' do step 'I search for "Contibuting"' do
...@@ -71,7 +73,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps ...@@ -71,7 +73,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end end
step 'I should see "Foo" link in the search results' do 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 end
step 'I should not see "Bar" link in the search results' do step 'I should not see "Bar" link in the search results' do
...@@ -79,7 +83,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps ...@@ -79,7 +83,9 @@ class Spinach::Features::Search < Spinach::FeatureSteps
end end
step 'I should see "test_wiki" link in the search results' do 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 end
step 'project has Wiki content' do step 'project has Wiki content' do
......
...@@ -28,6 +28,10 @@ module SharedAuthentication ...@@ -28,6 +28,10 @@ module SharedAuthentication
logout logout
end end
step "I logout directly" do
logout_direct
end
def current_user def current_user
@user || User.first @user || User.first
end end
......
...@@ -20,11 +20,14 @@ module SharedDiffNote ...@@ -20,11 +20,14 @@ module SharedDiffNote
end end
step 'I leave a diff comment like "Typo, please fix"' do step 'I leave a diff comment like "Typo, please fix"' do
click_diff_line(sample_commit.line_code) page.within(diff_file_selector) do
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do click_diff_line(sample_commit.line_code)
fill_in "note[note]", with: "Typo, please fix"
find(".js-comment-button").trigger("click") page.within("form[rel$='#{sample_commit.line_code}']") do
sleep 0.05 fill_in "note[note]", with: "Typo, please fix"
find(".js-comment-button").trigger("click")
sleep 0.05
end
end end
end end
...@@ -45,28 +48,37 @@ module SharedDiffNote ...@@ -45,28 +48,37 @@ module SharedDiffNote
end end
step 'I preview a diff comment text like "Should fix it :smile:"' do step 'I preview a diff comment text like "Should fix it :smile:"' do
click_diff_line(sample_commit.line_code) page.within(diff_file_selector) do
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do click_diff_line(sample_commit.line_code)
fill_in "note[note]", with: "Should fix it :smile:"
find('.js-md-preview-button').click 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
end end
step 'I preview another diff comment text like "DRY this up"' do step 'I preview another diff comment text like "DRY this up"' do
click_diff_line(sample_commit.del_line_code) 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" fill_in "note[note]", with: "DRY this up"
find('.js-md-preview-button').click find('.js-md-preview-button').click
end
end end
end end
step 'I open a diff comment form' do step 'I open a diff comment form' do
click_diff_line(sample_commit.line_code) page.within(diff_file_selector) do
click_diff_line(sample_commit.line_code)
end
end end
step 'I open another diff comment form' do step 'I open another diff comment form' do
click_diff_line(sample_commit.del_line_code) page.within(diff_file_selector) do
click_diff_line(sample_commit.del_line_code)
end
end end
step 'I write a diff comment like ":-1: I don\'t like this"' do step 'I write a diff comment like ":-1: I don\'t like this"' do
...@@ -194,7 +206,7 @@ module SharedDiffNote ...@@ -194,7 +206,7 @@ module SharedDiffNote
end end
def diff_file_selector def diff_file_selector
".diff-file:nth-of-type(1)" '.diff-file:nth-of-type(1)'
end end
def click_diff_line(code) def click_diff_line(code)
......
...@@ -2,8 +2,10 @@ module SharedNote ...@@ -2,8 +2,10 @@ module SharedNote
include Spinach::DSL include Spinach::DSL
step 'I delete a comment' do step 'I delete a comment' do
find('.note').hover page.within('.notes') do
find(".js-note-delete").click find('.note').hover
find(".js-note-delete").click
end
end end
step 'I haven\'t written any comment text' do step 'I haven\'t written any comment text' do
...@@ -16,7 +18,6 @@ module SharedNote ...@@ -16,7 +18,6 @@ module SharedNote
page.within(".js-main-target-form") do page.within(".js-main-target-form") do
fill_in "note[note]", with: "XML attached" fill_in "note[note]", with: "XML attached"
click_button "Add Comment" click_button "Add Comment"
sleep 0.05
end end
end end
...@@ -123,13 +124,14 @@ module SharedNote ...@@ -123,13 +124,14 @@ module SharedNote
end end
step 'I edit the last comment with a +1' do step 'I edit the last comment with a +1' do
find(".note").hover page.within(".notes") do
find('.js-note-edit').click find(".note").hover
find('.js-note-edit').click
end
page.within(".current-note-edit-form") do page.within(".current-note-edit-form") do
fill_in 'note[note]', with: '+1 Awesome!' fill_in 'note[note]', with: '+1 Awesome!'
click_button 'Save Comment' click_button 'Save Comment'
sleep 0.05
end end
end end
......
...@@ -31,6 +31,18 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps ...@@ -31,6 +31,18 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
click_button "Create snippet" click_button "Create snippet"
end 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 step 'I should see snippet "Personal snippet three"' do
expect(page).to have_content "Personal snippet three" expect(page).to have_content "Personal snippet three"
expect(page).to have_content "Content of snippet three" expect(page).to have_content "Content of snippet three"
...@@ -58,7 +70,15 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps ...@@ -58,7 +70,15 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
visit snippet_path(snippet) visit snippet_path(snippet)
end end
step 'I visit snippet page "Internal personal snippet one"' do
visit snippet_path(internal_snippet)
end
def snippet def snippet
@snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one") @snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one")
end end
def internal_snippet
@snippet ||= PersonalSnippet.find_by!(title: "Internal personal snippet one")
end
end end
...@@ -25,6 +25,7 @@ WebMock.allow_net_connect! ...@@ -25,6 +25,7 @@ WebMock.allow_net_connect!
Spinach.hooks.before_run do Spinach.hooks.before_run do
include RSpec::Mocks::ExampleMethods include RSpec::Mocks::ExampleMethods
RSpec::Mocks.setup
TestEnv.init(mailer: false) TestEnv.init(mailer: false)
include FactoryGirl::Syntax::Methods include FactoryGirl::Syntax::Methods
......
...@@ -23,7 +23,7 @@ module Backup ...@@ -23,7 +23,7 @@ module Backup
def backup_existing_uploads_dir def backup_existing_uploads_dir
timestamped_uploads_path = File.join(app_uploads_dir, '..', "uploads.#{Time.now.to_i}") timestamped_uploads_path = File.join(app_uploads_dir, '..', "uploads.#{Time.now.to_i}")
if File.exists?(app_uploads_dir) 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 end
end end
......
...@@ -40,6 +40,10 @@ upstream gitlab { ...@@ -40,6 +40,10 @@ upstream gitlab {
## Normal HTTP host ## Normal HTTP host
server { 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 0.0.0.0:80 default_server;
listen [::]:80 default_server; listen [::]:80 default_server;
server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com
......
...@@ -44,6 +44,10 @@ upstream gitlab { ...@@ -44,6 +44,10 @@ upstream gitlab {
## Redirects all HTTP traffic to the HTTPS host ## Redirects all HTTP traffic to the HTTPS host
server { 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 0.0.0.0:80;
listen [::]:80 ipv6only=on default_server; listen [::]:80 ipv6only=on default_server;
server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com 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 @@ ...@@ -7,7 +7,7 @@
<body> <body>
<h1> <h1>
<img src="/gitlab_logo.png" /><br /> <img src="/logo.svg" /><br />
Deploy in progress Deploy in progress
</h1> </h1>
<h3>Please try again in a few minutes.</h3> <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 ...@@ -16,7 +16,7 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
it { expect(body).to be_kind_of(Array) } 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 } it { expect(body.first["username"]).to eq user.username }
end end
...@@ -33,7 +33,7 @@ describe AutocompleteController do ...@@ -33,7 +33,7 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
it { expect(body).to be_kind_of(Array) } 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 } it { expect(body.first["username"]).to eq user.username }
end end
...@@ -46,6 +46,6 @@ describe AutocompleteController do ...@@ -46,6 +46,6 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
it { expect(body).to be_kind_of(Array) } 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
end end
...@@ -40,10 +40,10 @@ describe Projects::CommitController do ...@@ -40,10 +40,10 @@ describe Projects::CommitController do
get(:show, namespace_id: project.namespace.to_param, get(:show, namespace_id: project.namespace.to_param,
project_id: project.to_param, id: commit.id, format: format) project_id: project.to_param, id: commit.id, format: format)
expect(response.body).to_not include('&amp;') expect(response.body).not_to include('&amp;')
expect(response.body).to_not include('&gt;') expect(response.body).not_to include('&gt;')
expect(response.body).to_not include('&lt;') expect(response.body).not_to include('&lt;')
expect(response.body).to_not include('&quot;') expect(response.body).not_to include('&quot;')
end end
end end
......
require 'spec_helper' require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::BitbucketController do describe Import::BitbucketController do
include ImportSpecHelper
let(:user) { create(:user, bitbucket_access_token: 'asd123', bitbucket_access_token_secret: "sekret") } let(:user) { create(:user, bitbucket_access_token: 'asd123', bitbucket_access_token_secret: "sekret") }
before do before do
sign_in(user) sign_in(user)
controller.stub(:bitbucket_import_enabled?).and_return(true) allow(controller).to receive(:bitbucket_import_enabled?).and_return(true)
end end
describe "GET callback" do describe "GET callback" do
before do before do
session[:oauth_request_token] = {} session[:oauth_request_token] = {}
end end
it "updates access token" do it "updates access token" do
token = "asdasd12345" token = "asdasd12345"
secret = "sekrettt" secret = "sekrettt"
access_token = double(token: token, secret: secret) access_token = double(token: token, secret: secret)
Gitlab::BitbucketImport::Client.any_instance.stub(:get_token).and_return(access_token) allow_any_instance_of(Gitlab::BitbucketImport::Client).
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket") to receive(:get_token).and_return(access_token)
stub_omniauth_provider('bitbucket')
get :callback get :callback
...@@ -35,7 +39,7 @@ describe Import::BitbucketController do ...@@ -35,7 +39,7 @@ describe Import::BitbucketController do
it "assigns variables" do it "assigns variables" do
@project = create(:project, import_type: 'bitbucket', creator_id: user.id) @project = create(:project, import_type: 'bitbucket', creator_id: user.id)
controller.stub_chain(:client, :projects).and_return([@repo]) stub_client(projects: [@repo])
get :status get :status
...@@ -45,7 +49,7 @@ describe Import::BitbucketController do ...@@ -45,7 +49,7 @@ describe Import::BitbucketController do
it "does not show already added project" do it "does not show already added project" do
@project = create(:project, import_type: 'bitbucket', creator_id: user.id, import_source: 'asd/vim') @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 get :status
...@@ -77,8 +81,7 @@ describe Import::BitbucketController do ...@@ -77,8 +81,7 @@ describe Import::BitbucketController do
to receive(:new).with(bitbucket_repo, user). to receive(:new).with(bitbucket_repo, user).
and_return(double(execute: true)) and_return(double(execute: true))
controller.stub_chain(:client, :user).and_return(bitbucket_user) stub_client(user: bitbucket_user, project: bitbucket_repo)
controller.stub_chain(:client, :project).and_return(bitbucket_repo)
end end
context "when the repository owner is the Bitbucket user" do context "when the repository owner is the Bitbucket user" do
......
require 'spec_helper' require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GithubController do describe Import::GithubController do
include ImportSpecHelper
let(:user) { create(:user, github_access_token: 'asd123') } let(:user) { create(:user, github_access_token: 'asd123') }
before do before do
sign_in(user) sign_in(user)
controller.stub(:github_import_enabled?).and_return(true) allow(controller).to receive(:github_import_enabled?).and_return(true)
end end
describe "GET callback" do describe "GET callback" do
...@@ -13,9 +16,7 @@ describe Import::GithubController do ...@@ -13,9 +16,7 @@ describe Import::GithubController do
token = "asdasd12345" token = "asdasd12345"
allow_any_instance_of(Gitlab::GithubImport::Client). allow_any_instance_of(Gitlab::GithubImport::Client).
to receive(:get_token).and_return(token) to receive(:get_token).and_return(token)
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: 'asd123', stub_omniauth_provider('github')
app_secret: 'asd123',
name: 'github')
get :callback get :callback
...@@ -33,9 +34,7 @@ describe Import::GithubController do ...@@ -33,9 +34,7 @@ describe Import::GithubController do
it "assigns variables" do it "assigns variables" do
@project = create(:project, import_type: 'github', creator_id: user.id) @project = create(:project, import_type: 'github', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo]) stub_client(repos: [@repo], orgs: [@org], org_repos: [@org_repo])
controller.stub_chain(:client, :orgs).and_return([@org])
controller.stub_chain(:client, :org_repos).with(@org.login).and_return([@org_repo])
get :status get :status
...@@ -45,8 +44,7 @@ describe Import::GithubController do ...@@ -45,8 +44,7 @@ describe Import::GithubController do
it "does not show already added project" do it "does not show already added project" do
@project = create(:project, import_type: 'github', creator_id: user.id, import_source: 'asd/vim') @project = create(:project, import_type: 'github', creator_id: user.id, import_source: 'asd/vim')
controller.stub_chain(:client, :repos).and_return([@repo]) stub_client(repos: [@repo], orgs: [])
controller.stub_chain(:client, :orgs).and_return([])
get :status get :status
...@@ -67,8 +65,7 @@ describe Import::GithubController do ...@@ -67,8 +65,7 @@ describe Import::GithubController do
} }
before do before do
controller.stub_chain(:client, :user).and_return(github_user) stub_client(user: github_user, repo: github_repo)
controller.stub_chain(:client, :repo).and_return(github_repo)
end end
context "when the repository owner is the GitHub user" do context "when the repository owner is the GitHub user" do
......
require 'spec_helper' require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GitlabController do describe Import::GitlabController do
include ImportSpecHelper
let(:user) { create(:user, gitlab_access_token: 'asd123') } let(:user) { create(:user, gitlab_access_token: 'asd123') }
before do before do
sign_in(user) sign_in(user)
controller.stub(:gitlab_import_enabled?).and_return(true) allow(controller).to receive(:gitlab_import_enabled?).and_return(true)
end end
describe "GET callback" do describe "GET callback" do
it "updates access token" do it "updates access token" do
token = "asdasd12345" token = "asdasd12345"
Gitlab::GitlabImport::Client.any_instance.stub_chain(:client, :auth_code, :get_token, :token).and_return(token) allow_any_instance_of(Gitlab::GitlabImport::Client).
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "gitlab") to receive(:get_token).and_return(token)
stub_omniauth_provider('gitlab')
get :callback get :callback
...@@ -28,7 +32,7 @@ describe Import::GitlabController do ...@@ -28,7 +32,7 @@ describe Import::GitlabController do
it "assigns variables" do it "assigns variables" do
@project = create(:project, import_type: 'gitlab', creator_id: user.id) @project = create(:project, import_type: 'gitlab', creator_id: user.id)
controller.stub_chain(:client, :projects).and_return([@repo]) stub_client(projects: [@repo])
get :status get :status
...@@ -38,7 +42,7 @@ describe Import::GitlabController do ...@@ -38,7 +42,7 @@ describe Import::GitlabController do
it "does not show already added project" do it "does not show already added project" do
@project = create(:project, import_type: 'gitlab', creator_id: user.id, import_source: 'asd/vim') @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 get :status
...@@ -66,8 +70,7 @@ describe Import::GitlabController do ...@@ -66,8 +70,7 @@ describe Import::GitlabController do
} }
before do before do
controller.stub_chain(:client, :user).and_return(gitlab_user) stub_client(user: gitlab_user, project: gitlab_repo)
controller.stub_chain(:client, :project).and_return(gitlab_repo)
end end
context "when the repository owner is the GitLab.com user" do context "when the repository owner is the GitLab.com user" do
......
require 'spec_helper' require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GitoriousController do describe Import::GitoriousController do
include ImportSpecHelper
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
...@@ -30,7 +33,7 @@ describe Import::GitoriousController do ...@@ -30,7 +33,7 @@ describe Import::GitoriousController do
it "assigns variables" do it "assigns variables" do
@project = create(:project, import_type: 'gitorious', creator_id: user.id) @project = create(:project, import_type: 'gitorious', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo]) stub_client(repos: [@repo])
get :status get :status
...@@ -40,7 +43,7 @@ describe Import::GitoriousController do ...@@ -40,7 +43,7 @@ describe Import::GitoriousController do
it "does not show already added project" do it "does not show already added project" do
@project = create(:project, import_type: 'gitorious', creator_id: user.id, import_source: 'asd/vim') @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 get :status
...@@ -59,7 +62,7 @@ describe Import::GitoriousController do ...@@ -59,7 +62,7 @@ describe Import::GitoriousController do
expect(Gitlab::GitoriousImport::ProjectCreator). expect(Gitlab::GitoriousImport::ProjectCreator).
to receive(:new).with(@repo, namespace, user). to receive(:new).with(@repo, namespace, user).
and_return(double(execute: true)) and_return(double(execute: true))
controller.stub_chain(:client, :repo).and_return(@repo) stub_client(repo: @repo)
post :create, format: :js post :create, format: :js
end end
......
require 'spec_helper' require 'spec_helper'
require_relative 'import_spec_helper'
describe Import::GoogleCodeController do describe Import::GoogleCodeController do
include ImportSpecHelper
let(:user) { create(:user) } let(:user) { create(:user) }
let(:dump_file) { fixture_file_upload(Rails.root + 'spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') } let(:dump_file) { fixture_file_upload(Rails.root + 'spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') }
...@@ -21,13 +24,12 @@ describe Import::GoogleCodeController do ...@@ -21,13 +24,12 @@ describe Import::GoogleCodeController do
describe "GET status" do describe "GET status" do
before do before do
@repo = OpenStruct.new(name: 'vim') @repo = OpenStruct.new(name: 'vim')
controller.stub_chain(:client, :valid?).and_return(true) stub_client(valid?: true)
end end
it "assigns variables" do it "assigns variables" do
@project = create(:project, import_type: 'google_code', creator_id: user.id) @project = create(:project, import_type: 'google_code', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo]) stub_client(repos: [@repo], incompatible_repos: [])
controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status get :status
...@@ -38,8 +40,7 @@ describe Import::GoogleCodeController do ...@@ -38,8 +40,7 @@ describe Import::GoogleCodeController do
it "does not show already added project" do it "does not show already added project" do
@project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim') @project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
controller.stub_chain(:client, :repos).and_return([@repo]) stub_client(repos: [@repo], incompatible_repos: [])
controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status get :status
...@@ -48,8 +49,7 @@ describe Import::GoogleCodeController do ...@@ -48,8 +49,7 @@ describe Import::GoogleCodeController do
end end
it "does not show any invalid projects" do it "does not show any invalid projects" do
controller.stub_chain(:client, :repos).and_return([]) stub_client(repos: [], incompatible_repos: [@repo])
controller.stub_chain(:client, :incompatible_repos).and_return([@repo])
get :status 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 ...@@ -40,11 +40,11 @@ describe Profiles::TwoFactorAuthsController do
expect(user).to receive(:valid_otp?).with(pin).and_return(true) expect(user).to receive(:valid_otp?).with(pin).and_return(true)
end end
it 'sets otp_required_for_login' do it 'sets two_factor_enabled' do
go go
user.reload user.reload
expect(user.otp_required_for_login).to eq true expect(user).to be_two_factor_enabled
end end
it 'presents plaintext codes for the user to save' do it 'presents plaintext codes for the user to save' do
...@@ -109,13 +109,13 @@ describe Profiles::TwoFactorAuthsController do ...@@ -109,13 +109,13 @@ describe Profiles::TwoFactorAuthsController do
let!(:codes) { user.generate_otp_backup_codes! } let!(:codes) { user.generate_otp_backup_codes! }
it 'clears all 2FA-related fields' do 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.otp_backup_codes).not_to be_nil
expect(user.encrypted_otp_secret).not_to be_nil expect(user.encrypted_otp_secret).not_to be_nil
delete :destroy 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.otp_backup_codes).to be_nil
expect(user.encrypted_otp_secret).to be_nil expect(user.encrypted_otp_secret).to be_nil
end end
......
...@@ -40,10 +40,10 @@ describe Projects::MergeRequestsController do ...@@ -40,10 +40,10 @@ describe Projects::MergeRequestsController do
get(:show, namespace_id: project.namespace.to_param, get(:show, namespace_id: project.namespace.to_param,
project_id: project.to_param, id: merge_request.iid, format: format) project_id: project.to_param, id: merge_request.iid, format: format)
expect(response.body).to_not include('&amp;') expect(response.body).not_to include('&amp;')
expect(response.body).to_not include('&gt;') expect(response.body).not_to include('&gt;')
expect(response.body).to_not include('&lt;') expect(response.body).not_to include('&lt;')
expect(response.body).to_not include('&quot;') expect(response.body).not_to include('&quot;')
end end
end end
...@@ -79,23 +79,72 @@ describe Projects::MergeRequestsController do ...@@ -79,23 +79,72 @@ describe Projects::MergeRequestsController do
end end
end end
context '#diffs with forked projects with submodules' do describe 'GET diffs' do
render_views def go(format: 'html')
let(:project) { create(:project) } get :diffs, namespace_id: project.namespace.to_param,
let(:fork_project) { create(:forked_project_with_submodules) } project_id: project.to_param, id: merge_request.iid, format: format
let(:merge_request) { create(:merge_request_with_diffs, source_project: fork_project, source_branch: 'add-submodule-version-bump', target_branch: 'master', target_project: project) } end
context 'as html' do
it 'renders the diff template' do
go
before do expect(response).to render_template('diffs')
fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id) end
fork_project.save end
merge_request.reload
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 end
it '#diffs' do context 'with forked projects with submodules' do
get(:diffs, namespace_id: project.namespace.to_param, render_views
project_id: project.to_param, id: merge_request.iid, format: 'json')
expect(response).to be_success let(:project) { create(:project) }
expect(response.body).to have_content('Subproject commit') 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) }
before do
fork_project.build_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
fork_project.save
merge_request.reload
end
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 end
end end
...@@ -30,7 +30,7 @@ FactoryGirl.define do ...@@ -30,7 +30,7 @@ FactoryGirl.define do
trait :two_factor do trait :two_factor do
before(:create) do |user| before(:create) do |user|
user.otp_required_for_login = true user.two_factor_enabled = true
user.otp_secret = User.generate_otp_secret(32) user.otp_secret = User.generate_otp_secret(32)
end end
end end
......
...@@ -63,15 +63,35 @@ describe "Admin::Users", feature: true do ...@@ -63,15 +63,35 @@ describe "Admin::Users", feature: true do
end end
describe "GET /admin/users/:id" do describe "GET /admin/users/:id" do
before do it "should have user info" do
visit admin_users_path visit admin_users_path
click_link "#{@user.name}" click_link @user.name
end
it "should have user info" do
expect(page).to have_content(@user.email) expect(page).to have_content(@user.email)
expect(page).to have_content(@user.name) expect(page).to have_content(@user.name)
end 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 end
describe "GET /admin/users/:id/edit" do describe "GET /admin/users/:id/edit" do
......
...@@ -11,7 +11,8 @@ describe "GitLab Flavored Markdown", feature: true do ...@@ -11,7 +11,8 @@ describe "GitLab Flavored Markdown", feature: true do
end end
before do 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 end
let(:commit) { project.commit } let(:commit) { project.commit }
......
...@@ -38,7 +38,7 @@ describe 'Issues', feature: true do ...@@ -38,7 +38,7 @@ describe 'Issues', feature: true do
it 'does not change issue count' do it 'does not change issue count' do
expect { expect {
click_button 'Save changes' click_button 'Save changes'
}.to_not change { Issue.count } }.not_to change { Issue.count }
end end
it 'should update issue fields' do it 'should update issue fields' do
......
...@@ -9,7 +9,8 @@ describe 'Profile account page', feature: true do ...@@ -9,7 +9,8 @@ describe 'Profile account page', feature: true do
describe 'when signup is enabled' do describe 'when signup is enabled' do
before 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 visit profile_account_path
end end
...@@ -23,7 +24,8 @@ describe 'Profile account page', feature: true do ...@@ -23,7 +24,8 @@ describe 'Profile account page', feature: true do
describe 'when signup is disabled' do describe 'when signup is disabled' do
before 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 visit profile_account_path
end end
......
...@@ -75,7 +75,7 @@ describe 'Profile > Preferences' do ...@@ -75,7 +75,7 @@ describe 'Profile > Preferences' do
end end
def expect_preferences_saved_message def expect_preferences_saved_message
within('.flash-container') do page.within('.flash-container') do
expect(page).to have_content('Preferences saved.') expect(page).to have_content('Preferences saved.')
end end
end end
......
...@@ -6,7 +6,7 @@ describe "Profile access", feature: true do ...@@ -6,7 +6,7 @@ describe "Profile access", feature: true do
end end
describe "GET /login" do 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 end
describe "GET /profile/keys" do describe "GET /profile/keys" do
......
...@@ -76,8 +76,8 @@ describe ApplicationHelper do ...@@ -76,8 +76,8 @@ describe ApplicationHelper do
end end
it 'should return an url for the avatar with relative url' do it 'should return an url for the avatar with relative url' do
Gitlab.config.gitlab.stub(relative_url_root: '/gitlab') allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab')
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url)) allow(Gitlab.config.gitlab).to receive(:url).and_return(Settings.send(:build_gitlab_url))
user = create(:user) user = create(:user)
user.avatar = File.open(avatar_file_path) user.avatar = File.open(avatar_file_path)
...@@ -97,7 +97,7 @@ describe ApplicationHelper do ...@@ -97,7 +97,7 @@ describe ApplicationHelper do
let(:user_email) { 'user@email.com' } let(:user_email) { 'user@email.com' }
it 'should return a generic avatar path when Gravatar is disabled' do 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') expect(gravatar_icon(user_email)).to match('no_avatar.png')
end end
...@@ -106,13 +106,13 @@ describe ApplicationHelper do ...@@ -106,13 +106,13 @@ describe ApplicationHelper do
end end
it 'should return default gravatar url' do 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' url = 'http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118'
expect(gravatar_icon(user_email)).to match(url) expect(gravatar_icon(user_email)).to match(url)
end end
it 'should use SSL when appropriate' do 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') expect(gravatar_icon(user_email)).to match('https://secure.gravatar.com')
end end
...@@ -203,40 +203,52 @@ describe ApplicationHelper do ...@@ -203,40 +203,52 @@ describe ApplicationHelper do
end end
end end
describe 'link_to' do describe 'time_ago_with_tooltip' do
it 'should not include rel=nofollow for internal links' do def element(*arguments)
expect(link_to('Home', root_path)).to eq('<a href="/">Home</a>') 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 end
it 'should include rel=nofollow for external links' do it 'has a formatted title attribute' do
expect(link_to('Example', 'http://www.example.com')). expect(element.attr('title')).to eq 'Jul 02, 2015 8:00am'
to eq '<a href="http://www.example.com" rel="nofollow">Example</a>'
end end
it 'should include rel=nofollow for external links and honor existing html_options' do it 'includes a default js-timeago class' do
expect(link_to('Example', 'http://www.example.com', class: 'toggle', data: {toggle: 'dropdown'})) expect(element.attr('class')).to eq 'time_ago js-timeago'
.to eq '<a class="toggle" data-toggle="dropdown" href="http://www.example.com" rel="nofollow">Example</a>'
end end
it 'should include rel=nofollow for external links and preserve other rel values' do it 'accepts a custom html_class' do
expect(link_to('Example', 'http://www.example.com', rel: 'noreferrer')) expect(element(html_class: 'custom_class').attr('class')).to eq 'custom_class js-timeago'
.to eq '<a href="http://www.example.com" rel="noreferrer nofollow">Example</a>'
end end
it 'should not include rel=nofollow for external links on the same host as GitLab' do it 'accepts a custom tooltip placement' do
expect(Gitlab.config.gitlab).to receive(:host).and_return('example.foo') expect(element(placement: 'bottom').attr('data-placement')).to eq 'bottom'
expect(link_to('Example', 'http://example.foo/bar')).
to eq '<a href="http://example.foo/bar">Example</a>'
end end
it 'should not raise an error when given a bad URI' do it 're-initializes timeago Javascript' do
expect { link_to('default', 'if real=1 RANDOM; if real>1 IDLHS; if real>500 LHS') }. el = element.next_element
not_to raise_error
expect(el.name).to eq 'script'
expect(el.text).to include "$('.js-timeago').timeago()"
end end
it 'should not raise an error when given a bad mailto URL' do it 'allows the script tag to be excluded' do
expect { link_to('email', 'mailto://foo.bar@example.es?subject=Subject%20Line') }. expect(element(skip_js: true)).not_to include 'script'
not_to raise_error
end end
end end
......
...@@ -2,20 +2,20 @@ require 'spec_helper' ...@@ -2,20 +2,20 @@ require 'spec_helper'
describe BroadcastMessagesHelper do describe BroadcastMessagesHelper do
describe 'broadcast_styling' do describe 'broadcast_styling' do
let(:broadcast_message) { double(color: "", font: "") } let(:broadcast_message) { double(color: '', font: '') }
context "default style" do context "default style" do
it "should have no style" do it "should have no style" do
expect(broadcast_styling(broadcast_message)).to match('') expect(broadcast_styling(broadcast_message)).to eq ''
end end
end end
context "customiezd style" do context "customized style" do
before { broadcast_message.stub(color: "#f2dede", font: "#b94a48") } let(:broadcast_message) { double(color: "#f2dede", font: '#b94a48') }
it "should have a customized style" do it "should have a customized style" do
expect(broadcast_styling(broadcast_message)). expect(broadcast_styling(broadcast_message)).
to match('background-color:#f2dede;color:#b94a48') to match('background-color: #f2dede; color: #b94a48')
end end
end end
end end
......
...@@ -48,19 +48,19 @@ describe DiffHelper do ...@@ -48,19 +48,19 @@ describe DiffHelper do
end end
it 'should return only the first file if the diff line count in the 2nd file takes the total beyond safe limits' do 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) expect(safe_diff_files(diffs).length).to eq(1)
end end
it 'should return all files from a commit that is beyond safe limit for numbers of lines if force diff is true' do 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 } } 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) expect(safe_diff_files(diffs).length).to eq(2)
end end
it 'should return only the first file if the diff line count in the 2nd file takes the total beyond hard limits' do 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 } } 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) expect(safe_diff_files(diffs).length).to eq(1)
end end
......
require 'spec_helper' require 'spec_helper'
describe NotificationsHelper do describe NotificationsHelper do
include FontAwesome::Rails::IconHelper
include IconsHelper
describe 'notification_icon' do describe 'notification_icon' do
let(:notification) { double(disabled?: false, participating?: false, watch?: false) } let(:notification) { double(disabled?: false, participating?: false, watch?: false) }
context "disabled notification" do context "disabled notification" do
before { notification.stub(disabled?: true) } before { allow(notification).to receive(:disabled?).and_return(true) }
it "has a red icon" do it "has a red icon" do
expect(notification_icon(notification)).to match('class="fa fa-volume-off ns-mute"') expect(notification_icon(notification)).to match('class="fa fa-volume-off ns-mute"')
...@@ -16,7 +13,7 @@ describe NotificationsHelper do ...@@ -16,7 +13,7 @@ describe NotificationsHelper do
end end
context "participating notification" do context "participating notification" do
before { notification.stub(participating?: true) } before { allow(notification).to receive(:participating?).and_return(true) }
it "has a blue icon" do it "has a blue icon" do
expect(notification_icon(notification)).to match('class="fa fa-volume-down ns-part"') expect(notification_icon(notification)).to match('class="fa fa-volume-down ns-part"')
...@@ -24,7 +21,7 @@ describe NotificationsHelper do ...@@ -24,7 +21,7 @@ describe NotificationsHelper do
end end
context "watched notification" do context "watched notification" do
before { notification.stub(watch?: true) } before { allow(notification).to receive(:watch?).and_return(true) }
it "has a green icon" do it "has a green icon" do
expect(notification_icon(notification)).to match('class="fa fa-volume-up ns-watch"') expect(notification_icon(notification)).to match('class="fa fa-volume-up ns-watch"')
......
...@@ -14,41 +14,41 @@ describe SubmoduleHelper do ...@@ -14,41 +14,41 @@ describe SubmoduleHelper do
context 'submodule on self' do context 'submodule on self' do
before 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 end
it 'should detect ssh on standard port' do it 'should detect ssh on standard port' do
Gitlab.config.gitlab_shell.stub(ssh_port: 22) # set this just to be sure allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(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_path_prefix).and_return(Settings.send(:build_gitlab_shell_ssh_path_prefix))
stub_url([ config.user, '@', config.host, ':gitlab-org/gitlab-ce.git' ].join('')) 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') ]) expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end end
it 'should detect ssh on non-standard port' do it 'should detect ssh on non-standard port' do
Gitlab.config.gitlab_shell.stub(ssh_port: 2222) allow(Gitlab.config.gitlab_shell).to receive(:ssh_port).and_return(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_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('')) 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') ]) expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end end
it 'should detect http on standard port' do it 'should detect http on standard port' do
Gitlab.config.gitlab.stub(port: 80) allow(Gitlab.config.gitlab).to receive(:port).and_return(80)
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url)) 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('')) 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') ]) expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end end
it 'should detect http on non-standard port' do it 'should detect http on non-standard port' do
Gitlab.config.gitlab.stub(port: 3000) allow(Gitlab.config.gitlab).to receive(:port).and_return(3000)
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url)) 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('')) 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') ]) expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end end
it 'should work with relative_url_root' do it 'should work with relative_url_root' do
Gitlab.config.gitlab.stub(port: 80) # set this just to be sure allow(Gitlab.config.gitlab).to receive(:port).and_return(80) # set this just to be sure
Gitlab.config.gitlab.stub(relative_url_root: '/gitlab/root') allow(Gitlab.config.gitlab).to receive(:relative_url_root).and_return('/gitlab/root')
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url)) 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('')) 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') ]) expect(submodule_links(submodule_item)).to eq([ namespace_project_path('gitlab-org', 'gitlab-ce'), namespace_project_tree_path('gitlab-org', 'gitlab-ce', 'hash') ])
end end
...@@ -156,6 +156,6 @@ describe SubmoduleHelper do ...@@ -156,6 +156,6 @@ describe SubmoduleHelper do
end end
def stub_url(url) def stub_url(url)
repo.stub(submodule_url_for: url) allow(repo).to receive(:submodule_url_for).and_return(url)
end end
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 ...@@ -9,8 +9,11 @@ describe ExtractsPath do
before do before do
@project = project @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 end
describe '#assign_ref' do describe '#assign_ref' do
......
...@@ -27,16 +27,18 @@ describe Gitlab::Auth do ...@@ -27,16 +27,18 @@ describe Gitlab::Auth do
it "should not find user with invalid password" do it "should not find user with invalid password" do
password = 'wrong' password = 'wrong'
expect( gl_auth.find(username, password) ).to_not eql user expect( gl_auth.find(username, password) ).not_to eql user
end end
it "should not find user with invalid login" do it "should not find user with invalid login" do
user = 'wrong' user = 'wrong'
expect( gl_auth.find(username, password) ).to_not eql user expect( gl_auth.find(username, password) ).not_to eql user
end end
context "with ldap enabled" do 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 it "tries to autheticate with db before ldap" do
expect(Gitlab::LDAP::Authentication).not_to receive(:login) expect(Gitlab::LDAP::Authentication).not_to receive(:login)
......
...@@ -5,7 +5,7 @@ describe Gitlab::Shell do ...@@ -5,7 +5,7 @@ describe Gitlab::Shell do
let(:gitlab_shell) { Gitlab::Shell.new } let(:gitlab_shell) { Gitlab::Shell.new }
before do before do
Project.stub(find: project) allow(Project).to receive(:find).and_return(project)
end end
it { is_expected.to respond_to :add_key } it { is_expected.to respond_to :add_key }
......
...@@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Client do ...@@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Client do
let(:raw_data) { "No clue" } let(:raw_data) { "No clue" }
it "returns true" do it "returns true" do
expect(subject).to_not be_valid expect(subject).not_to be_valid
end end
end end
end end
......
...@@ -25,7 +25,7 @@ describe Gitlab::GoogleCodeImport::Importer do ...@@ -25,7 +25,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject.execute subject.execute
%w(New NeedInfo Accepted Wishlist Started Fixed Invalid Duplicate WontFix Incomplete).each do |status| %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
end end
...@@ -39,7 +39,7 @@ describe Gitlab::GoogleCodeImport::Importer do ...@@ -39,7 +39,7 @@ describe Gitlab::GoogleCodeImport::Importer do
Component-Systray Component-Clock Component-Launcher Component-Tint2conf Component-Docs Component-New Component-Systray Component-Clock Component-Launcher Component-Tint2conf Component-Docs Component-New
).each do |label| ).each do |label|
label.sub!("-", ": ") 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
end end
...@@ -47,7 +47,7 @@ describe Gitlab::GoogleCodeImport::Importer do ...@@ -47,7 +47,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject.execute subject.execute
issue = project.issues.first issue = project.issues.first
expect(issue).to_not be_nil expect(issue).not_to be_nil
expect(issue.iid).to eq(169) expect(issue.iid).to eq(169)
expect(issue.author).to eq(project.creator) expect(issue.author).to eq(project.creator)
expect(issue.assignee).to eq(mapped_user) expect(issue.assignee).to eq(mapped_user)
...@@ -72,7 +72,7 @@ describe Gitlab::GoogleCodeImport::Importer do ...@@ -72,7 +72,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject.execute subject.execute
note = project.issues.first.notes.first 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("Comment 1")
expect(note.note).to include("@#{mapped_user.username}") expect(note.note).to include("@#{mapped_user.username}")
expect(note.note).to include("November 18, 2009 05:14") expect(note.note).to include("November 18, 2009 05:14")
......
...@@ -8,16 +8,24 @@ describe Gitlab::LDAP::Access do ...@@ -8,16 +8,24 @@ describe Gitlab::LDAP::Access do
subject { access.allowed? } subject { access.allowed? }
context 'when the user cannot be found' do 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 } it { is_expected.to be_falsey }
end end
context 'when the user is found' do 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 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 } it { is_expected.to be_falsey }
...@@ -30,8 +38,9 @@ describe Gitlab::LDAP::Access do ...@@ -30,8 +38,9 @@ describe Gitlab::LDAP::Access do
context 'and has no disabled flag in active diretory' do context 'and has no disabled flag in active diretory' do
before do before do
user.block 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 end
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
...@@ -39,7 +48,8 @@ describe Gitlab::LDAP::Access do ...@@ -39,7 +48,8 @@ describe Gitlab::LDAP::Access do
context 'when auto-created users are blocked' do context 'when auto-created users are blocked' do
before 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 end
it "does not unblock user in GitLab" do it "does not unblock user in GitLab" do
...@@ -51,7 +61,8 @@ describe Gitlab::LDAP::Access do ...@@ -51,7 +61,8 @@ describe Gitlab::LDAP::Access do
context "when auto-created users are not blocked" do context "when auto-created users are not blocked" do
before 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 end
it "should unblock user in GitLab" do it "should unblock user in GitLab" do
...@@ -63,8 +74,9 @@ describe Gitlab::LDAP::Access do ...@@ -63,8 +74,9 @@ describe Gitlab::LDAP::Access do
context 'without ActiveDirectory enabled' do context 'without ActiveDirectory enabled' do
before do before do
Gitlab::LDAP::Config.stub(enabled?: true) allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
Gitlab::LDAP::Config.any_instance.stub(active_directory: false) allow_any_instance_of(Gitlab::LDAP::Config).
to receive(:active_directory).and_return(false)
end end
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
......
...@@ -3,27 +3,32 @@ require 'spec_helper' ...@@ -3,27 +3,32 @@ require 'spec_helper'
describe Gitlab::LDAP::Adapter do describe Gitlab::LDAP::Adapter do
let(:adapter) { Gitlab::LDAP::Adapter.new 'ldapmain' } let(:adapter) { Gitlab::LDAP::Adapter.new 'ldapmain' }
describe :dn_matches_filter? do describe '#dn_matches_filter?' do
let(:ldap) { double(:ldap) } let(:ldap) { double(:ldap) }
subject { adapter.dn_matches_filter?(:dn, :filter) } 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 "when the search is successful" do
context "and the result is non-empty" 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 } it { is_expected.to be_truthy }
end end
context "and the result is empty" do 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 } it { is_expected.to be_falsey }
end end
end end
context "when the search encounters an error" do 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 } it { is_expected.to be_falsey }
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LDAP::Authentication do describe Gitlab::LDAP::Authentication do
let(:klass) { Gitlab::LDAP::Authentication } let(:user) { create(:omniauth_user, extern_uid: dn) }
let(:user) { create(:omniauth_user, extern_uid: dn) } let(:dn) { 'uid=john,ou=people,dc=example,dc=com' }
let(:dn) { 'uid=john,ou=people,dc=example,dc=com' } let(:login) { 'john' }
let(:login) { 'john' }
let(:password) { 'password' } let(:password) { 'password' }
describe :login do describe 'login' do
let(:adapter) { double :adapter }
before do before do
Gitlab::LDAP::Config.stub(enabled?: true) allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
end end
it "finds the user if authentication is successful" do it "finds the user if authentication is successful" do
user expect(user).not_to be_nil
# try only to fake the LDAP call # try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter, adapter = double('adapter', dn: dn).as_null_object
bind_as: double(:ldap_user, dn: dn) allow_any_instance_of(described_class).
)) to receive(:adapter).and_return(adapter)
expect(klass.login(login, password)).to be_truthy
expect(described_class.login(login, password)).to be_truthy
end end
it "is false if the user does not exist" do it "is false if the user does not exist" do
# try only to fake the LDAP call # try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter, adapter = double('adapter', dn: dn).as_null_object
bind_as: double(:ldap_user, dn: dn) allow_any_instance_of(described_class).
)) to receive(:adapter).and_return(adapter)
expect(klass.login(login, password)).to be_falsey
expect(described_class.login(login, password)).to be_falsey
end end
it "is false if authentication fails" do it "is false if authentication fails" do
user expect(user).not_to be_nil
# try only to fake the LDAP call # try only to fake the LDAP call
klass.any_instance.stub(adapter: double(:adapter, bind_as: nil)) adapter = double('adapter', bind_as: nil).as_null_object
expect(klass.login(login, password)).to be_falsey allow_any_instance_of(described_class).
to receive(:adapter).and_return(adapter)
expect(described_class.login(login, password)).to be_falsey
end end
it "fails if ldap is disabled" do it "fails if ldap is disabled" do
Gitlab::LDAP::Config.stub(enabled?: false) allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(false)
expect(klass.login(login, password)).to be_falsey expect(described_class.login(login, password)).to be_falsey
end end
it "fails if no login is supplied" do it "fails if no login is supplied" do
expect(klass.login('', password)).to be_falsey expect(described_class.login('', password)).to be_falsey
end end
it "fails if no password is supplied" do 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 end
end end
\ No newline at end of file
...@@ -14,7 +14,7 @@ describe Gitlab::LDAP::Config do ...@@ -14,7 +14,7 @@ describe Gitlab::LDAP::Config do
end end
it "raises an error if a unknow provider is used" do 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 end
end end
...@@ -16,31 +16,31 @@ describe Gitlab::LDAP::User do ...@@ -16,31 +16,31 @@ describe Gitlab::LDAP::User do
describe :changed? do describe :changed? do
it "marks existing ldap user as 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 expect(ldap_user.changed?).to be_truthy
end end
it "marks existing non-ldap user if the email matches as changed" do 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 expect(ldap_user.changed?).to be_truthy
end end
it "dont marks existing ldap user as changed" do 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 expect(ldap_user.changed?).to be_falsey
end end
end end
describe :find_or_create do describe :find_or_create do
it "finds the user if already existing" 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 end
it "connects to existing non-ldap user if the email matches" do it "connects to existing non-ldap user if the email matches" do
existing_user = create(:omniauth_user, email: 'john@example.com', provider: "twitter") 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 existing_user.reload
expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid' expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
...@@ -52,11 +52,15 @@ describe Gitlab::LDAP::User do ...@@ -52,11 +52,15 @@ describe Gitlab::LDAP::User do
end end
end end
describe 'blocking' do 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 'signup' do
context 'dont block on create' 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 it do
ldap_user.save ldap_user.save
...@@ -66,7 +70,7 @@ describe Gitlab::LDAP::User do ...@@ -66,7 +70,7 @@ describe Gitlab::LDAP::User do
end end
context 'block on create' do context 'block on create' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true } before { configure_block(true) }
it do it do
ldap_user.save ldap_user.save
...@@ -83,7 +87,7 @@ describe Gitlab::LDAP::User do ...@@ -83,7 +87,7 @@ describe Gitlab::LDAP::User do
end end
context 'dont block on create' 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 it do
ldap_user.save ldap_user.save
...@@ -93,7 +97,7 @@ describe Gitlab::LDAP::User do ...@@ -93,7 +97,7 @@ describe Gitlab::LDAP::User do
end end
context 'block on create' do context 'block on create' do
before { Gitlab::LDAP::Config.any_instance.stub block_auto_created_users: true } before { configure_block(true) }
it do it do
ldap_user.save ldap_user.save
......
...@@ -51,7 +51,7 @@ describe Gitlab::OAuth::AuthHash do ...@@ -51,7 +51,7 @@ describe Gitlab::OAuth::AuthHash do
it { expect(auth_hash.email).to eql email_utf8 } it { expect(auth_hash.email).to eql email_utf8 }
it { expect(auth_hash.username).to eql nickname_utf8 } it { expect(auth_hash.username).to eql nickname_utf8 }
it { expect(auth_hash.name).to eql name_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 end
context 'email not provided' do context 'email not provided' do
......
...@@ -19,23 +19,34 @@ describe Gitlab::OAuth::User do ...@@ -19,23 +19,34 @@ describe Gitlab::OAuth::User do
let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') } 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 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') auth = double(info: double(name: 'John'), uid: 'my-uid', provider: 'my-provider')
expect( oauth_user.persisted? ).to be_truthy expect( oauth_user.persisted? ).to be_truthy
end end
it "returns false if use is not found in database" do 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 expect( oauth_user.persisted? ).to be_falsey
end end
end end
describe :save do 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' } let(:provider) { 'twitter' }
describe 'signup' do describe 'signup' do
shared_examples "to verify compliance with allow_single_sign_on" do shared_examples "to verify compliance with allow_single_sign_on" do
context "with allow_single_sign_on enabled" 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 it "creates a user from Omniauth" do
oauth_user.save oauth_user.save
...@@ -48,7 +59,7 @@ describe Gitlab::OAuth::User do ...@@ -48,7 +59,7 @@ describe Gitlab::OAuth::User do
end end
context "with allow_single_sign_on disabled (Default)" do 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 it "throws an error" do
expect{ oauth_user.save }.to raise_error StandardError expect{ oauth_user.save }.to raise_error StandardError
end end
...@@ -56,36 +67,36 @@ describe Gitlab::OAuth::User do ...@@ -56,36 +67,36 @@ describe Gitlab::OAuth::User do
end end
context "with auto_link_ldap_user disabled (default)" do 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" include_examples "to verify compliance with allow_single_sign_on"
end end
context "with auto_link_ldap_user enabled" do 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 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" include_examples "to verify compliance with allow_single_sign_on"
end end
context "and at least one LDAP provider is defined" do 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 context "and a corresponding LDAP person" do
before do before do
ldap_user.stub(:uid) { uid } allow(ldap_user).to receive(:uid) { uid }
ldap_user.stub(:username) { uid } allow(ldap_user).to receive(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] } allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' } 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) allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
end end
context "and no account for the LDAP user" do context "and no account for the LDAP user" do
it "creates a user with dual LDAP and omniauth identities" do it "creates a user with dual LDAP and omniauth identities" do
oauth_user.save oauth_user.save
expect(gl_user).to be_valid expect(gl_user).to be_valid
expect(gl_user.username).to eql uid expect(gl_user.username).to eql uid
expect(gl_user.email).to eql 'johndoe@example.com' expect(gl_user.email).to eql 'johndoe@example.com'
...@@ -97,12 +108,12 @@ describe Gitlab::OAuth::User do ...@@ -97,12 +108,12 @@ describe Gitlab::OAuth::User do
]) ])
end end
end end
context "and LDAP user has an account already" do context "and LDAP user has an account already" do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
it "adds the omniauth identity to the LDAP account" do it "adds the omniauth identity to the LDAP account" do
oauth_user.save oauth_user.save
expect(gl_user).to be_valid expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john' expect(gl_user.username).to eql 'john'
expect(gl_user.email).to eql 'john@example.com' expect(gl_user.email).to eql 'john@example.com'
...@@ -115,10 +126,10 @@ describe Gitlab::OAuth::User do ...@@ -115,10 +126,10 @@ describe Gitlab::OAuth::User do
end end
end end
end end
context "and no corresponding LDAP person" do context "and no corresponding LDAP person" do
before { allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil) } before { allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil) }
include_examples "to verify compliance with allow_single_sign_on" include_examples "to verify compliance with allow_single_sign_on"
end end
end end
...@@ -128,11 +139,11 @@ describe Gitlab::OAuth::User do ...@@ -128,11 +139,11 @@ describe Gitlab::OAuth::User do
describe 'blocking' do describe 'blocking' do
let(:provider) { 'twitter' } 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 'signup with omniauth only' do
context 'dont block on create' 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 it do
oauth_user.save oauth_user.save
...@@ -142,7 +153,7 @@ describe Gitlab::OAuth::User do ...@@ -142,7 +153,7 @@ describe Gitlab::OAuth::User do
end end
context 'block on create' do 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 it do
oauth_user.save oauth_user.save
...@@ -154,17 +165,17 @@ describe Gitlab::OAuth::User do ...@@ -154,17 +165,17 @@ describe Gitlab::OAuth::User do
context 'signup with linked omniauth and LDAP account' do context 'signup with linked omniauth and LDAP account' do
before do before do
Gitlab.config.omniauth.stub auto_link_ldap_user: true stub_omniauth_config(auto_link_ldap_user: true)
ldap_user.stub(:uid) { uid } allow(ldap_user).to receive(:uid) { uid }
ldap_user.stub(:username) { uid } allow(ldap_user).to receive(:username) { uid }
ldap_user.stub(:email) { ['johndoe@example.com','john2@example.com'] } allow(ldap_user).to receive(:email) { ['johndoe@example.com','john2@example.com'] }
ldap_user.stub(:dn) { 'uid=user1,ou=People,dc=example' } allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
allow(oauth_user).to receive(:ldap_person).and_return(ldap_user) allow(oauth_user).to receive(:ldap_person).and_return(ldap_user)
end end
context "and no account for the LDAP user" do context "and no account for the LDAP user" do
context 'dont block on create (LDAP)' 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 it do
oauth_user.save oauth_user.save
...@@ -174,7 +185,7 @@ describe Gitlab::OAuth::User do ...@@ -174,7 +185,7 @@ describe Gitlab::OAuth::User do
end end
context 'block on create (LDAP)' do 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 it do
oauth_user.save oauth_user.save
...@@ -188,7 +199,7 @@ describe Gitlab::OAuth::User do ...@@ -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') } 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 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 it do
oauth_user.save oauth_user.save
...@@ -198,7 +209,7 @@ describe Gitlab::OAuth::User do ...@@ -198,7 +209,7 @@ describe Gitlab::OAuth::User do
end end
context 'block on create (LDAP)' do 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 it do
oauth_user.save oauth_user.save
...@@ -217,7 +228,7 @@ describe Gitlab::OAuth::User do ...@@ -217,7 +228,7 @@ describe Gitlab::OAuth::User do
end end
context 'dont block on create' 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 it do
oauth_user.save oauth_user.save
...@@ -227,7 +238,7 @@ describe Gitlab::OAuth::User do ...@@ -227,7 +238,7 @@ describe Gitlab::OAuth::User do
end end
context 'block on create' do 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 it do
oauth_user.save oauth_user.save
...@@ -237,7 +248,7 @@ describe Gitlab::OAuth::User do ...@@ -237,7 +248,7 @@ describe Gitlab::OAuth::User do
end end
context 'dont block on create (LDAP)' 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 it do
oauth_user.save oauth_user.save
...@@ -247,7 +258,7 @@ describe Gitlab::OAuth::User do ...@@ -247,7 +258,7 @@ describe Gitlab::OAuth::User do
end end
context 'block on create (LDAP)' do 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 it do
oauth_user.save oauth_user.save
......
...@@ -28,7 +28,7 @@ describe 'Gitlab::Popen', no_db: true do ...@@ -28,7 +28,7 @@ describe 'Gitlab::Popen', no_db: true do
context 'unsafe string command' do context 'unsafe string command' do
it 'raises an error when it gets called with a string argument' 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
end end
......
...@@ -27,7 +27,7 @@ describe 'Gitlab::Satellite::MergeAction' do ...@@ -27,7 +27,7 @@ describe 'Gitlab::Satellite::MergeAction' do
context 'between branches' do context 'between branches' do
it 'should raise exception -- not expected to be used by non forks' 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 end
end end
...@@ -75,7 +75,7 @@ describe 'Gitlab::Satellite::MergeAction' do ...@@ -75,7 +75,7 @@ describe 'Gitlab::Satellite::MergeAction' do
context 'between branches' do context 'between branches' do
it 'should get proper diffs' 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 end
end end
......
...@@ -10,14 +10,14 @@ describe Gitlab::Upgrader do ...@@ -10,14 +10,14 @@ describe Gitlab::Upgrader do
describe 'latest_version?' do describe 'latest_version?' do
it 'should be true if newest 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 expect(upgrader.latest_version?).to be_truthy
end end
end end
describe 'latest_version_raw' do describe 'latest_version_raw' do
it 'should be latest version for GitLab 5' 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") expect(upgrader.latest_version_raw).to eq("v5.4.2")
end end
......
require 'spec_helper' require 'spec_helper'
require 'email_spec'
describe Notify do describe Notify do
include EmailSpec::Helpers include EmailSpec::Helpers
......
...@@ -14,7 +14,7 @@ describe CommitRange do ...@@ -14,7 +14,7 @@ describe CommitRange do
let(:range2) { described_class.new("#{sha_from}..#{sha_to}") } let(:range2) { described_class.new("#{sha_from}..#{sha_to}") }
it 'raises ArgumentError when given an invalid range string' do 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 end
describe '#to_s' do describe '#to_s' do
......
...@@ -77,13 +77,13 @@ eos ...@@ -77,13 +77,13 @@ eos
let(:other_issue) { create :issue, project: other_project } let(:other_issue) { create :issue, project: other_project }
it 'detects issues that this commit is marked as closing' do 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]) expect(commit.closes_issues).to eq([issue])
end end
it 'does not detect issues from other projects' do it 'does not detect issues from other projects' do
ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}" 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 expect(commit.closes_issues).to be_empty
end end
end end
...@@ -93,7 +93,9 @@ eos ...@@ -93,7 +93,9 @@ eos
let(:author) { create(:user, email: commit.author_email) } let(:author) { create(:user, email: commit.author_email) }
let(:backref_text) { "commit #{subject.id}" } 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. # Include the subject in the repository stub.
let(:extra_commits) { [subject] } let(:extra_commits) { [subject] }
......
...@@ -11,7 +11,10 @@ describe Issue, "Issuable" do ...@@ -11,7 +11,10 @@ describe Issue, "Issuable" do
end end
describe "Validation" do 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(:project) }
it { is_expected.to validate_presence_of(:iid) } it { is_expected.to validate_presence_of(:iid) }
it { is_expected.to validate_presence_of(:author) } it { is_expected.to validate_presence_of(:author) }
......
...@@ -23,7 +23,7 @@ describe Issue, "Mentionable" do ...@@ -23,7 +23,7 @@ describe Issue, "Mentionable" do
end end
it 'correctly removes already-mentioned Commits' do 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]) issue.create_cross_references!(project, author, [commit2])
end end
......
...@@ -58,10 +58,10 @@ describe :forked_from_project do ...@@ -58,10 +58,10 @@ describe :forked_from_project do
end end
def fork_project(from_project, user) def fork_project(from_project, user)
context = Projects::ForkService.new(from_project, user) shell = double('gitlab_shell', fork_repository: true)
shell = double("gitlab_shell")
shell.stub(fork_repository: true) service = Projects::ForkService.new(from_project, user)
context.stub(gitlab_shell: shell) allow(service).to receive(:gitlab_shell).and_return(shell)
context.execute
end
service.execute
end
...@@ -52,7 +52,7 @@ describe ServiceHook do ...@@ -52,7 +52,7 @@ describe ServiceHook do
expect { expect {
@service_hook.execute(@data) @service_hook.execute(@data)
}.to raise_error }.to raise_error(RuntimeError)
end end
end end
end end
...@@ -73,7 +73,7 @@ describe ProjectHook do ...@@ -73,7 +73,7 @@ describe ProjectHook do
expect { expect {
@project_hook.execute(@data, 'push_hooks') @project_hook.execute(@data, 'push_hooks')
}.to raise_error }.to raise_error(RuntimeError)
end end
end end
end end
...@@ -70,7 +70,7 @@ describe Issue do ...@@ -70,7 +70,7 @@ describe Issue do
it_behaves_like 'an editable mentionable' do it_behaves_like 'an editable mentionable' do
subject { create(:issue, project: project) } 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 } } let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
end end
......
...@@ -24,8 +24,11 @@ describe GroupMember do ...@@ -24,8 +24,11 @@ describe GroupMember do
describe "#after_create" do describe "#after_create" do
it "should send email to user" do it "should send email to user" do
membership = build(:group_member) 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) expect(membership).to receive(:notification_service)
membership.save membership.save
end end
end end
...@@ -33,7 +36,8 @@ describe GroupMember do ...@@ -33,7 +36,8 @@ describe GroupMember do
describe "#after_update" do describe "#after_update" do
before do before do
@group_member = create :group_member @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 end
it "should send email to user" do it "should send email to user" do
......
...@@ -111,17 +111,18 @@ describe MergeRequest do ...@@ -111,17 +111,18 @@ describe MergeRequest do
let(:commit2) { double('commit2', closes_issues: [issue1]) } let(:commit2) { double('commit2', closes_issues: [issue1]) }
before do before do
subject.stub(commits: [commit0, commit1, commit2]) allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
end end
it 'accesses the set of issues that will be closed on acceptance' do 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)) expect(subject.closes_issues).to eq([issue0, issue1].sort_by(&:id))
end end
it 'only lists issues as to be closed if it targets the default branch' do 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' subject.target_branch = 'something-else'
expect(subject.closes_issues).to be_empty expect(subject.closes_issues).to be_empty
...@@ -130,7 +131,8 @@ describe MergeRequest do ...@@ -130,7 +131,8 @@ describe MergeRequest do
it 'detects issues mentioned in the description' do it 'detects issues mentioned in the description' do
issue2 = create(:issue, project: subject.project) issue2 = create(:issue, project: subject.project)
subject.description = "Closes #{issue2.to_reference}" 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) expect(subject.closes_issues).to include(issue2)
end end
...@@ -163,10 +165,10 @@ describe MergeRequest do ...@@ -163,10 +165,10 @@ describe MergeRequest do
end end
it_behaves_like 'an editable mentionable' do 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(:backref_text) { "merge request #{subject.to_reference}" }
let(:set_mentionable_text) { ->(txt){ subject.title = txt } } let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
end end
it_behaves_like 'a Taskable' do it_behaves_like 'a Taskable' do
......
...@@ -21,11 +21,11 @@ describe Milestone do ...@@ -21,11 +21,11 @@ describe Milestone do
it { is_expected.to have_many(:issues) } it { is_expected.to have_many(:issues) }
end end
describe "Mass assignment" do
end
describe "Validation" do 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(:title) } it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:project) } it { is_expected.to validate_presence_of(:project) }
end end
...@@ -66,7 +66,7 @@ describe Milestone do ...@@ -66,7 +66,7 @@ describe Milestone do
describe :expired? do describe :expired? do
context "expired" do context "expired" do
before do before do
milestone.stub(due_date: Date.today.prev_year) allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
end end
it { expect(milestone.expired?).to be_truthy } it { expect(milestone.expired?).to be_truthy }
...@@ -74,7 +74,7 @@ describe Milestone do ...@@ -74,7 +74,7 @@ describe Milestone do
context "not expired" do context "not expired" do
before do before do
milestone.stub(due_date: Date.today.next_year) allow(milestone).to receive(:due_date).and_return(Date.today.next_year)
end end
it { expect(milestone.expired?).to be_falsey } it { expect(milestone.expired?).to be_falsey }
...@@ -83,7 +83,7 @@ describe Milestone do ...@@ -83,7 +83,7 @@ describe Milestone do
describe :percent_complete do describe :percent_complete do
before do before do
milestone.stub( allow(milestone).to receive_messages(
closed_items_count: 3, closed_items_count: 3,
total_items_count: 4 total_items_count: 4
) )
......
...@@ -53,7 +53,7 @@ describe Namespace do ...@@ -53,7 +53,7 @@ describe Namespace do
describe :move_dir do describe :move_dir do
before do before do
@namespace = create :namespace @namespace = create :namespace
@namespace.stub(path_changed?: true) allow(@namespace).to receive(:path_changed?).and_return(true)
end end
it "should raise error when directory exists" do it "should raise error when directory exists" do
...@@ -62,8 +62,8 @@ describe Namespace do ...@@ -62,8 +62,8 @@ describe Namespace do
it "should move dir if path changed" do it "should move dir if path changed" do
new_path = @namespace.path + "_new" new_path = @namespace.path + "_new"
@namespace.stub(path_was: @namespace.path) allow(@namespace).to receive(:path_was).and_return(@namespace.path)
@namespace.stub(path: new_path) allow(@namespace).to receive(:path).and_return(new_path)
expect(@namespace.move_dir).to be_truthy expect(@namespace.move_dir).to be_truthy
end end
end end
......
...@@ -42,7 +42,7 @@ describe AsanaService, models: true do ...@@ -42,7 +42,7 @@ describe AsanaService, models: true do
before do before do
@asana = AsanaService.new @asana = AsanaService.new
@asana.stub( allow(@asana).to receive_messages(
project: project, project: project,
project_id: project.id, project_id: project.id,
service_hook: true, service_hook: true,
......
...@@ -32,7 +32,7 @@ describe AssemblaService, models: true do ...@@ -32,7 +32,7 @@ describe AssemblaService, models: true do
before do before do
@assembla_service = AssemblaService.new @assembla_service = AssemblaService.new
@assembla_service.stub( allow(@assembla_service).to receive_messages(
project_id: project.id, project_id: project.id,
project: project, project: project,
service_hook: true, service_hook: true,
......
...@@ -29,12 +29,10 @@ describe BuildkiteService do ...@@ -29,12 +29,10 @@ describe BuildkiteService do
describe 'commits methods' do describe 'commits methods' do
before do before do
@project = Project.new @project = Project.new
@project.stub( allow(@project).to receive(:default_branch).and_return('default-brancho')
default_branch: 'default-brancho'
)
@service = BuildkiteService.new @service = BuildkiteService.new
@service.stub( allow(@service).to receive_messages(
project: @project, project: @project,
service_hook: true, service_hook: true,
project_url: 'https://buildkite.com/account-name/example-project', project_url: 'https://buildkite.com/account-name/example-project',
......
...@@ -32,7 +32,7 @@ describe FlowdockService do ...@@ -32,7 +32,7 @@ describe FlowdockService do
before do before do
@flowdock_service = FlowdockService.new @flowdock_service = FlowdockService.new
@flowdock_service.stub( allow(@flowdock_service).to receive_messages(
project_id: project.id, project_id: project.id,
project: project, project: project,
service_hook: true, service_hook: true,
......
...@@ -32,7 +32,7 @@ describe GemnasiumService do ...@@ -32,7 +32,7 @@ describe GemnasiumService do
before do before do
@gemnasium_service = GemnasiumService.new @gemnasium_service = GemnasiumService.new
@gemnasium_service.stub( allow(@gemnasium_service).to receive_messages(
project_id: project.id, project_id: project.id,
project: project, project: project,
service_hook: true, service_hook: true,
......
...@@ -21,18 +21,15 @@ ...@@ -21,18 +21,15 @@
require 'spec_helper' require 'spec_helper'
describe GitlabCiService do describe GitlabCiService do
describe "Associations" do describe 'associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to(:project) }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one(:service_hook) }
end
describe "Mass assignment" do
end end
describe 'commits methods' do describe 'commits methods' do
before do before do
@service = GitlabCiService.new @service = GitlabCiService.new
@service.stub( allow(@service).to receive_messages(
service_hook: true, service_hook: true,
project_url: 'http://ci.gitlab.org/projects/2', project_url: 'http://ci.gitlab.org/projects/2',
token: 'verySecret' token: 'verySecret'
...@@ -56,9 +53,9 @@ describe GitlabCiService do ...@@ -56,9 +53,9 @@ describe GitlabCiService do
it "calls ci_yaml_file" do it "calls ci_yaml_file" do
service_hook = double service_hook = double
service_hook.should_receive(:execute) expect(service_hook).to receive(:execute)
@service.should_receive(:service_hook).and_return(service_hook) expect(@service).to receive(:service_hook).and_return(service_hook)
@service.should_receive(:ci_yaml_file).with(push_sample_data[:checkout_sha]) expect(@service).to receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
@service.execute(push_sample_data) @service.execute(push_sample_data)
end end
...@@ -72,7 +69,7 @@ describe GitlabCiService do ...@@ -72,7 +69,7 @@ describe GitlabCiService do
@user = create(:user) @user = create(:user)
@service = GitlabCiService.new @service = GitlabCiService.new
@service.stub( allow(@service).to receive_messages(
service_hook: true, service_hook: true,
project_url: 'http://ci.gitlab.org/projects/2', project_url: 'http://ci.gitlab.org/projects/2',
token: 'verySecret', token: 'verySecret',
......
...@@ -37,7 +37,7 @@ describe HipchatService do ...@@ -37,7 +37,7 @@ describe HipchatService do
let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
before(:each) do before(:each) do
hipchat.stub( allow(hipchat).to receive_messages(
project_id: project.id, project_id: project.id,
project: project, project: project,
room: 123456, room: 123456,
...@@ -48,7 +48,7 @@ describe HipchatService do ...@@ -48,7 +48,7 @@ describe HipchatService do
end end
it 'should use v1 if version is provided' do 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). expect(HipChat::Client).to receive(:new).
with(token, with(token,
api_version: 'v1', api_version: 'v1',
...@@ -59,7 +59,7 @@ describe HipchatService do ...@@ -59,7 +59,7 @@ describe HipchatService do
end end
it 'should use v2 as the version when nothing is provided' do 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). expect(HipChat::Client).to receive(:new).
with(token, with(token,
api_version: 'v2', api_version: 'v2',
...@@ -245,12 +245,12 @@ describe HipchatService do ...@@ -245,12 +245,12 @@ describe HipchatService do
end end
it "should set notfiy to true" do 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'}) expect(hipchat.send(:message_options)).to eq({notify: true, color: 'yellow'})
end end
it "should set the color" do 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'}) expect(hipchat.send(:message_options)).to eq({notify: false, color: 'red'})
end end
end end
......
...@@ -24,8 +24,8 @@ require 'json' ...@@ -24,8 +24,8 @@ require 'json'
describe IrkerService do describe IrkerService do
describe 'Associations' do describe 'Associations' do
it { should belong_to :project } it { is_expected.to belong_to :project }
it { should have_one :service_hook } it { is_expected.to have_one :service_hook }
end end
describe 'Validations' do describe 'Validations' do
...@@ -66,7 +66,7 @@ describe IrkerService do ...@@ -66,7 +66,7 @@ describe IrkerService do
let(:colorize_messages) { '1' } let(:colorize_messages) { '1' }
before do before do
irker.stub( allow(irker).to receive_messages(
active: true, active: true,
project: project, project: project,
project_id: project.id, project_id: project.id,
......
...@@ -52,7 +52,7 @@ describe PushoverService do ...@@ -52,7 +52,7 @@ describe PushoverService do
let(:api_url) { 'https://api.pushover.net/1/messages.json' } let(:api_url) { 'https://api.pushover.net/1/messages.json' }
before do before do
pushover.stub( allow(pushover).to receive_messages(
project: project, project: project,
project_id: project.id, project_id: project.id,
service_hook: true, service_hook: true,
......
...@@ -46,7 +46,7 @@ describe SlackService do ...@@ -46,7 +46,7 @@ describe SlackService do
let(:channel) { 'slack_channel' } let(:channel) { 'slack_channel' }
before do before do
slack.stub( allow(slack).to receive_messages(
project: project, project: project,
project_id: project.id, project_id: project.id,
service_hook: true, service_hook: true,
...@@ -96,7 +96,7 @@ describe SlackService do ...@@ -96,7 +96,7 @@ describe SlackService do
end end
it 'should use the username as an option for slack when configured' do 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). expect(Slack::Notifier).to receive(:new).
with(webhook_url, username: username). with(webhook_url, username: username).
and_return( and_return(
...@@ -106,7 +106,7 @@ describe SlackService do ...@@ -106,7 +106,7 @@ describe SlackService do
end end
it 'should use the channel as an option when it is configured' do 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). expect(Slack::Notifier).to receive(:new).
with(webhook_url, channel: channel). with(webhook_url, channel: channel).
and_return( and_return(
...@@ -130,11 +130,11 @@ describe SlackService do ...@@ -130,11 +130,11 @@ describe SlackService do
let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
before do before do
slack.stub( allow(slack).to receive_messages(
project: project, project: project,
project_id: project.id, project_id: project.id,
service_hook: true, service_hook: true,
webhook: webhook_url webhook: webhook_url
) )
WebMock.stub_request(:post, webhook_url) WebMock.stub_request(:post, webhook_url)
......
...@@ -127,7 +127,7 @@ describe Project do ...@@ -127,7 +127,7 @@ describe Project do
describe 'last_activity' do describe 'last_activity' do
it 'should alias last_activity to last_event' 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) expect(project.last_activity).to eq(last_event)
end end
end end
......
...@@ -39,9 +39,7 @@ describe Service do ...@@ -39,9 +39,7 @@ describe Service do
let (:project) { create :project } let (:project) { create :project }
before do before do
@service.stub( allow(@service).to receive(:project).and_return(project)
project: project
)
@testable = @service.can_test? @testable = @service.can_test?
end end
...@@ -54,9 +52,7 @@ describe Service do ...@@ -54,9 +52,7 @@ describe Service do
let (:project) { create :project } let (:project) { create :project }
before do before do
@service.stub( allow(@service).to receive(:project).and_return(project)
project: project
)
@testable = @service.can_test? @testable = @service.can_test?
end end
......
...@@ -40,7 +40,6 @@ describe Snippet do ...@@ -40,7 +40,6 @@ describe Snippet do
it { is_expected.to validate_presence_of(:title) } 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_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_length_of(:file_name).is_within(0..255) }
it { is_expected.to validate_presence_of(:content) } it { is_expected.to validate_presence_of(:content) }
......
...@@ -210,6 +210,30 @@ describe User do ...@@ -210,6 +210,30 @@ describe User do
end end
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 describe 'authentication token' do
it "should have authentication token" do it "should have authentication token" do
user = create(:user) user = create(:user)
...@@ -340,6 +364,31 @@ describe User do ...@@ -340,6 +364,31 @@ describe User do
end end
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 describe 'search' do
let(:user1) { create(:user, username: 'James', email: 'james@testing.com') } let(:user1) { create(:user, username: 'James', email: 'james@testing.com') }
let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') } let(:user2) { create(:user, username: 'jameson', email: 'jameson@example.com') }
...@@ -409,21 +458,25 @@ describe User do ...@@ -409,21 +458,25 @@ describe User do
it 'is false when LDAP is disabled' do it 'is false when LDAP is disabled' do
# Create a condition which would otherwise cause 'true' to be returned # 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 user.last_credential_check_at = nil
expect(user.requires_ldap_check?).to be_falsey expect(user.requires_ldap_check?).to be_falsey
end end
context 'when LDAP is enabled' do 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 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 expect(user.requires_ldap_check?).to be_falsey
end end
context 'and when the user is an LDAP user' do 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 it 'is true when the user has never had an LDAP check before' do
user.last_credential_check_at = nil user.last_credential_check_at = nil
......
require "spec_helper"
...@@ -47,7 +47,7 @@ describe API, api: true do ...@@ -47,7 +47,7 @@ describe API, api: true do
it "should return nil for a user without access" do it "should return nil for a user without access" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token 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 expect(current_user).to be_nil
end end
...@@ -72,13 +72,13 @@ describe API, api: true do ...@@ -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 it "should throw an error when the current user is not an admin and attempting to sudo" do
set_env(user, admin.id) set_env(user, admin.id)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
set_param(user, admin.id) set_param(user, admin.id)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
set_env(user, admin.username) set_env(user, admin.username)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
set_param(user, admin.username) set_param(user, admin.username)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
end end
it "should throw an error when the user cannot be found for a given id" do 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 ...@@ -86,10 +86,10 @@ describe API, api: true do
expect(user.id).not_to eq(id) expect(user.id).not_to eq(id)
expect(admin.id).not_to eq(id) expect(admin.id).not_to eq(id)
set_env(admin, id) set_env(admin, id)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
set_param(admin, id) set_param(admin, id)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
end end
it "should throw an error when the user cannot be found for a given username" do 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 ...@@ -97,10 +97,10 @@ describe API, api: true do
expect(user.username).not_to eq(username) expect(user.username).not_to eq(username)
expect(admin.username).not_to eq(username) expect(admin.username).not_to eq(username)
set_env(admin, username) set_env(admin, username)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
set_param(admin, username) set_param(admin, username)
expect { current_user }.to raise_error expect { current_user }.to raise_error(Exception)
end end
it "should handle sudo's to oneself" do it "should handle sudo's to oneself" do
......
...@@ -141,7 +141,9 @@ describe API::API, api: true do ...@@ -141,7 +141,9 @@ describe API::API, api: true do
end end
describe "DELETE /projects/:id/repository/branches/:branch" do 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 it "should remove branch" do
delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user)
......
...@@ -60,9 +60,8 @@ describe API::API, api: true do ...@@ -60,9 +60,8 @@ describe API::API, api: true do
end end
it "should return a 400 if editor fails to create file" do it "should return a 400 if editor fails to create file" do
Repository.any_instance.stub( allow_any_instance_of(Repository).to receive(:commit_file).
commit_file: false, and_return(false)
)
post api("/projects/#{project.id}/repository/files", user), valid_params post api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(400) expect(response.status).to eq(400)
...@@ -112,9 +111,7 @@ describe API::API, api: true do ...@@ -112,9 +111,7 @@ describe API::API, api: true do
end end
it "should return a 400 if satellite fails to create file" do it "should return a 400 if satellite fails to create file" do
Repository.any_instance.stub( allow_any_instance_of(Repository).to receive(:remove_file).and_return(false)
remove_file: false,
)
delete api("/projects/#{project.id}/repository/files", user), valid_params delete api("/projects/#{project.id}/repository/files", user), valid_params
expect(response.status).to eq(400) expect(response.status).to eq(400)
......
...@@ -167,7 +167,8 @@ describe API::API, api: true do ...@@ -167,7 +167,8 @@ describe API::API, api: true do
describe "POST /groups/:id/projects/:project_id" do describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:project) } let(:project) { create(:project) }
before(:each) do 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) allow(Project).to receive(:find).and_return(project)
end end
......
...@@ -49,9 +49,8 @@ describe API::API, api: true do ...@@ -49,9 +49,8 @@ describe API::API, api: true do
get api("/projects/#{project.id}/merge_requests?state=closed", user) get api("/projects/#{project.id}/merge_requests?state=closed", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.length).to eq(2) expect(json_response.length).to eq(1)
expect(json_response.second['title']).to eq(merge_request_closed.title) expect(json_response.first['title']).to eq(merge_request_closed.title)
expect(json_response.first['title']).to eq(merge_request_merged.title)
end end
it "should return an array of merged merge_requests" do it "should return an array of merged merge_requests" do
...@@ -301,14 +300,20 @@ describe API::API, api: true do ...@@ -301,14 +300,20 @@ describe API::API, api: true do
describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do
it "should return merge_request in case of success" 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) put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
end end
it "should return 405 if branch can't be merged" do 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) put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user)
expect(response.status).to eq(405) expect(response.status).to eq(405)
expect(json_response['message']).to eq('Branch cannot be merged') expect(json_response['message']).to eq('Branch cannot be merged')
end end
......
...@@ -132,7 +132,7 @@ describe API::API, api: true do ...@@ -132,7 +132,7 @@ describe API::API, api: true do
delete api("/projects/#{project.id}/members/#{user3.id}", user) delete api("/projects/#{project.id}/members/#{user3.id}", user)
expect { expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user) delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to_not change { ProjectMember.count } }.not_to change { ProjectMember.count }
end end
it "should return 200 if team member already removed" do it "should return 200 if team member already removed" do
......
...@@ -121,15 +121,13 @@ describe API::API, api: true do ...@@ -121,15 +121,13 @@ describe API::API, api: true do
get api('/projects/all', admin) get api('/projects/all', admin)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
project_name = project.name
expect(json_response.detect { expect(json_response).to satisfy do |response|
|project| project['name'] == project_name response.one? do |entry|
}['name']).to eq(project_name) entry['name'] == project.name &&
entry['owner']['username'] == user.username
expect(json_response.detect { end
|project| project['owner']['username'] == user.username end
}['owner']['username']).to eq(user.username)
end end
end end
end end
...@@ -138,9 +136,8 @@ describe API::API, api: true do ...@@ -138,9 +136,8 @@ describe API::API, api: true do
context 'maximum number of projects reached' do context 'maximum number of projects reached' do
it 'should not create new project and respond with 403' 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) allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
expect { expect { post api('/projects', user2), name: 'foo' }.
post api('/projects', user2), name: 'foo' to change {Project.count}.by(0)
}.to change {Project.count}.by(0)
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
...@@ -158,7 +155,7 @@ describe API::API, api: true do ...@@ -158,7 +155,7 @@ describe API::API, api: true do
end end
it 'should not create new project without name and return 400' do 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) expect(response.status).to eq(400)
end end
...@@ -257,7 +254,7 @@ describe API::API, api: true do ...@@ -257,7 +254,7 @@ describe API::API, api: true do
it 'should respond with 400 on failure and not project' do it 'should respond with 400 on failure and not project' do
expect { post api("/projects/user/#{user.id}", admin) }. 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(response.status).to eq(400)
expect(json_response['message']['name']).to eq([ expect(json_response['message']['name']).to eq([
......
...@@ -49,7 +49,7 @@ describe API::API, api: true do ...@@ -49,7 +49,7 @@ describe API::API, api: true do
it "should not create new hook without url" do it "should not create new hook without url" do
expect { expect {
post api("/hooks", admin) post api("/hooks", admin)
}.to_not change { SystemHook.count } }.not_to change { SystemHook.count }
end end
end end
......
...@@ -19,7 +19,7 @@ describe ArchiveRepositoryService do ...@@ -19,7 +19,7 @@ describe ArchiveRepositoryService do
it "raises an error" do it "raises an error" do
expect { expect {
subject.execute(timeout: 0.0) subject.execute(timeout: 0.0)
}.to raise_error }.to raise_error(RuntimeError)
end end
end end
......
...@@ -124,7 +124,9 @@ describe GitPushService do ...@@ -124,7 +124,9 @@ describe GitPushService do
end end
it "when pushing a branch for the first time with default branch protection disabled" do 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).to receive(:execute_hooks)
expect(project.default_branch).to eq("master") expect(project.default_branch).to eq("master")
...@@ -133,7 +135,9 @@ describe GitPushService do ...@@ -133,7 +135,9 @@ describe GitPushService do
end end
it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do 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).to receive(:execute_hooks)
expect(project.default_branch).to eq("master") expect(project.default_branch).to eq("master")
...@@ -154,32 +158,35 @@ describe GitPushService do ...@@ -154,32 +158,35 @@ describe GitPushService do
let(:commit) { project.commit } let(:commit) { project.commit }
before do before do
commit.stub({ allow(commit).to receive_messages(
safe_message: "this commit \n mentions ##{issue.id}", safe_message: "this commit \n mentions ##{issue.id}",
references: [issue], references: [issue],
author_name: commit_author.name, author_name: commit_author.name,
author_email: commit_author.email author_email: commit_author.email
}) )
project.repository.stub(commits_between: [commit]) allow(project.repository).to receive(:commits_between).and_return([commit])
end end
it "creates a note if a pushed commit mentions an issue" do 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) service.execute(project, user, @oldrev, @newrev, @ref)
end end
it "only creates a cross-reference note if one doesn't already exist" do 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) service.execute(project, user, @oldrev, @newrev, @ref)
end end
it "defaults to the pushing user if the commit's author is not known" do 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') allow(commit).to receive_messages(
expect(Note).to receive(:create_cross_reference_note).with(issue, commit, user) 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) service.execute(project, user, @oldrev, @newrev, @ref)
end end
...@@ -188,7 +195,7 @@ describe GitPushService do ...@@ -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(@blankrev, @newrev).and_return([])
allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit]) 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') service.execute(project, user, @blankrev, @newrev, 'refs/heads/other')
end end
...@@ -201,14 +208,15 @@ describe GitPushService do ...@@ -201,14 +208,15 @@ describe GitPushService do
let(:closing_commit) { project.commit } let(:closing_commit) { project.commit }
before do before do
closing_commit.stub({ allow(closing_commit).to receive_messages(
issue_closing_regex: /^([Cc]loses|[Ff]ixes) #\d+/, issue_closing_regex: /^([Cc]loses|[Ff]ixes) #\d+/,
safe_message: "this is some work.\n\ncloses ##{issue.iid}", safe_message: "this is some work.\n\ncloses ##{issue.iid}",
author_name: commit_author.name, author_name: commit_author.name,
author_email: commit_author.email author_email: commit_author.email
}) )
project.repository.stub(commits_between: [closing_commit]) allow(project.repository).to receive(:commits_between).
and_return([closing_commit])
end end
it "closes issues with commit messages" do it "closes issues with commit messages" do
...@@ -224,7 +232,7 @@ describe GitPushService do ...@@ -224,7 +232,7 @@ describe GitPushService do
end end
it "doesn't close issues when pushed to non-default branches" do 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. # The push still shouldn't create cross-reference notes.
expect { expect {
......
...@@ -58,7 +58,7 @@ describe NotificationService do ...@@ -58,7 +58,7 @@ describe NotificationService do
end end
it 'filters out "mentioned in" notes' do 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) expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note) notification.new_note(mentioned_note)
...@@ -130,7 +130,7 @@ describe NotificationService do ...@@ -130,7 +130,7 @@ describe NotificationService do
end end
it 'filters out "mentioned in" notes' do 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) expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note) notification.new_note(mentioned_note)
......
...@@ -2,7 +2,6 @@ ENV["RAILS_ENV"] ||= 'test' ...@@ -2,7 +2,6 @@ ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__) require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails' require 'rspec/rails'
require 'shoulda/matchers' require 'shoulda/matchers'
require 'email_spec'
require 'sidekiq/testing/inline' require 'sidekiq/testing/inline'
# Requires supporting ruby files with custom matchers and macros, etc, # Requires supporting ruby files with custom matchers and macros, etc,
......
...@@ -19,36 +19,3 @@ unless ENV['CI'] || ENV['CI_SERVER'] ...@@ -19,36 +19,3 @@ unless ENV['CI'] || ENV['CI_SERVER']
# Keep only the screenshots generated from the last failing test suite # Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run Capybara::Screenshot.prune_strategy = :keep_last_run
end 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| RSpec.configure do |config|
config.before(:suite) do config.before(:suite) do
DatabaseCleaner.clean_with(:truncation) DatabaseCleaner.clean_with(:truncation)
...@@ -36,15 +18,4 @@ RSpec.configure do |config| ...@@ -36,15 +18,4 @@ RSpec.configure do |config|
config.after(:each) do config.after(:each) do
DatabaseCleaner.clean DatabaseCleaner.clean
end 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 end
...@@ -39,4 +39,9 @@ module LoginHelpers ...@@ -39,4 +39,9 @@ module LoginHelpers
def logout def logout
find(:css, ".fa.fa-sign-out").click find(:css, ".fa.fa-sign-out").click
end end
# Logout without JavaScript driver
def logout_direct
page.driver.submit :delete, '/users/sign_out', {}
end
end end
RSpec::Matchers.define :be_valid_commit do RSpec::Matchers.define :be_valid_commit do
match do |actual| match do |actual|
actual != nil actual &&
actual.id == ValidCommit::ID actual.id == ValidCommit::ID &&
actual.message == ValidCommit::MESSAGE actual.message == ValidCommit::MESSAGE &&
actual.author_name == ValidCommit::AUTHOR_FULL_NAME actual.author_name == ValidCommit::AUTHOR_FULL_NAME
end end
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| RSpec::Matchers.define :be_allowed_for do |user|
match do |url| match do |url|
include UrlAccess emulate_user(user)
url_allowed?(user, url) visit url
status_code != 404 && current_path != new_user_session_path
end end
end end
RSpec::Matchers.define :be_denied_for do |user| RSpec::Matchers.define :be_denied_for do |user|
match do |url| match do |url|
include UrlAccess emulate_user(user)
url_denied?(user, url) visit url
status_code == 404 || current_path == new_user_session_path
end end
end end
RSpec::Matchers.define :be_404_for do |user| RSpec::Matchers.define :be_not_found_for do |user|
match do |url| match do |url|
include UrlAccess emulate_user(user)
url_404?(user, url) visit url
status_code == 404
end end
end end
...@@ -33,38 +46,12 @@ RSpec::Matchers.define :include_module do |expected| ...@@ -33,38 +46,12 @@ RSpec::Matchers.define :include_module do |expected|
described_class.included_modules.include?(expected) described_class.included_modules.include?(expected)
end end
failure_message_for_should do description do
"expected #{described_class} to include the #{expected} module" "includes the #{expected} module"
end end
end
module UrlAccess failure_message do
def url_allowed?(user, url) "expected #{described_class} to include the #{expected} module"
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
end end
end end
......
...@@ -80,7 +80,7 @@ shared_examples 'a mentionable' do ...@@ -80,7 +80,7 @@ shared_examples 'a mentionable' do
ext_issue, ext_mr, ext_commit] ext_issue, ext_mr, ext_commit]
mentioned_objects.each do |referenced| 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) with(referenced, subject.local_reference, author)
end end
...@@ -88,7 +88,7 @@ shared_examples 'a mentionable' do ...@@ -88,7 +88,7 @@ shared_examples 'a mentionable' do
end end
it 'detects existing cross-references' do 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).to have_mentioned(mentioned_issue)
expect(subject).not_to have_mentioned(mentioned_mr) expect(subject).not_to have_mentioned(mentioned_mr)
...@@ -132,13 +132,13 @@ shared_examples 'an editable mentionable' do ...@@ -132,13 +132,13 @@ shared_examples 'an editable mentionable' do
# These three objects were already referenced, and should not receive new # These three objects were already referenced, and should not receive new
# notes # notes
[mentioned_issue, mentioned_commit, ext_issue].each do |oldref| [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) with(oldref, any_args)
end end
# These two issues are new and should receive reference notes # These two issues are new and should receive reference notes
new_issues.each do |newref| 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) with(newref, subject.local_reference, author)
end end
......
...@@ -41,11 +41,13 @@ module TestEnv ...@@ -41,11 +41,13 @@ module TestEnv
end end
def disable_mailer 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 end
def enable_mailer 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 end
# Clean /tmp/tests # Clean /tmp/tests
......
...@@ -23,30 +23,33 @@ describe 'gitlab:app namespace rake task' do ...@@ -23,30 +23,33 @@ describe 'gitlab:app namespace rake task' do
context 'gitlab version' do context 'gitlab version' do
before do before do
Dir.stub glob: [] allow(Dir).to receive(:glob).and_return([])
allow(Dir).to receive :chdir allow(Dir).to receive(:chdir)
File.stub exists?: true allow(File).to receive(:exists?).and_return(true)
Kernel.stub system: true allow(Kernel).to receive(:system).and_return(true)
FileUtils.stub cp_r: true allow(FileUtils).to receive(:cp_r).and_return(true)
FileUtils.stub mv: true allow(FileUtils).to receive(:mv).and_return(true)
Rake::Task["gitlab:shell:setup"].stub invoke: true allow(Rake::Task["gitlab:shell:setup"]).
to receive(:invoke).and_return(true)
end end
let(:gitlab_version) { Gitlab::VERSION } let(:gitlab_version) { Gitlab::VERSION }
it 'should fail on mismatch' do it 'should fail on mismatch' do
YAML.stub load_file: {gitlab_version: "not #{gitlab_version}" } allow(YAML).to receive(:load_file).
expect { run_rake_task('gitlab:backup:restore') }.to( and_return({gitlab_version: "not #{gitlab_version}" })
raise_error SystemExit
) expect { run_rake_task('gitlab:backup:restore') }.
to raise_error(SystemExit)
end end
it 'should invoke restoration on mach' do it 'should invoke restoration on mach' do
YAML.stub load_file: {gitlab_version: gitlab_version} allow(YAML).to receive(:load_file).
expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke and_return({gitlab_version: gitlab_version})
expect(Rake::Task["gitlab:backup:repo:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke)
expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke expect(Rake::Task["gitlab:backup:repo:restore"]).to receive(:invoke)
expect { run_rake_task('gitlab:backup:restore') }.to_not raise_error expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke)
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end end
end end
...@@ -140,13 +143,14 @@ describe 'gitlab:app namespace rake task' do ...@@ -140,13 +143,14 @@ describe 'gitlab:app namespace rake task' do
end end
it 'does not invoke repositories restore' do 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 allow($stdout).to receive :write
expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke 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:backup:repo:restore"]).not_to receive :invoke
expect(Rake::Task["gitlab:shell:setup"]).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 end
end # gitlab:app namespace end # gitlab:app namespace
...@@ -21,7 +21,7 @@ describe 'gitlab:mail_google_schema_whitelisting rake task' do ...@@ -21,7 +21,7 @@ describe 'gitlab:mail_google_schema_whitelisting rake task' do
end end
it 'should run the task without errors' do 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 end
end end
...@@ -30,7 +30,7 @@ describe PostReceive do ...@@ -30,7 +30,7 @@ describe PostReceive do
end end
it "asks the project to trigger all hooks" do 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_hooks).twice
expect(project).to receive(:execute_services).twice expect(project).to receive(:execute_services).twice
expect(project).to receive(:update_merge_requests) 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