Merge branch 'dev' into fix/21725-cursor-offset-IE

pull/3632/head
Henriette Dinger 9 years ago
commit 3ccbea2510
  1. 8
      .teatro.yml
  2. 70
      .travis.yml
  3. 10
      Gemfile
  4. 22
      Gemfile.lock
  5. 3
      app/assets/javascripts/danger_zone_validation.js
  6. 5
      app/assets/stylesheets/_misc_legacy.sass
  7. 2
      app/assets/stylesheets/_settings.scss
  8. 12
      app/assets/stylesheets/content/_advanced_filters.sass
  9. 20
      app/assets/stylesheets/content/_forms.sass
  10. 1
      app/assets/stylesheets/content/_legacy_actions.sass
  11. 4
      app/assets/stylesheets/content/_select2.scss
  12. 14
      app/assets/stylesheets/content/_simple_filters.sass
  13. 5
      app/assets/stylesheets/content/_table.sass
  14. 2
      app/helpers/application_helper.rb
  15. 5
      app/models/repository.rb
  16. 4
      app/models/user.rb
  17. 66
      app/models/user_preference.rb
  18. 23
      app/services/scm/create_managed_repository_service.rb
  19. 27
      app/services/scm/delete_managed_repository_service.rb
  20. 12
      app/views/copy_projects/copy_from_admin.html.erb
  21. 5
      app/views/projects/_edit.html.erb
  22. 8
      app/views/projects/_form.html.erb
  23. 26
      app/views/projects/form/_project_attributes.html.erb
  24. 8
      app/views/projects/new.html.erb
  25. 16
      app/views/repositories/settings/git/_managed.html.erb
  26. 54
      app/views/repositories/settings/shared/_managed.html.erb
  27. 15
      app/views/repositories/settings/subversion/_managed.html.erb
  28. 9
      app/workers/scm/create_local_repository_job.rb
  29. 42
      app/workers/scm/create_remote_repository_job.rb
  30. 2
      app/workers/scm/delete_local_repository_job.rb
  31. 41
      app/workers/scm/delete_remote_repository_job.rb
  32. 93
      app/workers/scm/remote_repository_job.rb
  33. 27
      config/configuration.yml.example
  34. 2
      config/initializers/enforce_isolation_level.rb
  35. 14
      config/locales/en.yml
  36. 75
      doc/operation_guides/manual/repository-integration.md
  37. 2
      extra/Apache/OpenProjectAuthentication.pm
  38. 182
      extra/Apache/OpenProjectRepoman.pm
  39. 2
      features/admin/user.feature
  40. 2
      features/custom_fields/create_bool.feature
  41. 2
      features/custom_fields/create_list.feature
  42. 2
      features/custom_fields/edit_text.feature
  43. 2
      features/issues/issue_edit.feature
  44. 2
      features/issues/time_entries.feature
  45. 4
      features/menu_items/query_menu_items.feature
  46. 8
      features/menu_items/wiki_menu_items.feature
  47. 62
      features/project_types/project_creation_with_type.feature
  48. 2
      features/projects/archive.feature
  49. 64
      features/projects/default_settings.feature
  50. 2
      features/search/pagination.feature
  51. 2
      features/search/search.feature
  52. 27
      features/support/env.rb
  53. 14
      features/timelines/timeline_view_custom_fields.feature
  54. 2
      features/timelines/timeline_view_with_reporters.feature
  55. 3
      features/timelines/timeline_work_package_show_view.feature
  56. 38
      features/users/user_auth.feature
  57. 2
      features/work_packages/attachments.feature
  58. 2
      features/work_packages/bulk.feature
  59. 2
      features/work_packages/destroy.feature
  60. 2
      features/work_packages/index_move_columns.feature
  61. 15
      features/work_packages/moves/work_package_moves_new_copy.feature
  62. 2
      features/work_packages/preview.feature
  63. 2
      features/work_packages/work_package_show.feature
  64. 1
      lib/api/v3/root.rb
  65. 13
      lib/api/v3/root_representer.rb
  66. 47
      lib/api/v3/user_preferences/user_preferences_api.rb
  67. 78
      lib/api/v3/user_preferences/user_preferences_representer.rb
  68. 4
      lib/api/v3/utilities/path_helper.rb
  69. 3
      lib/api/v3/work_packages/work_packages_shared_helpers.rb
  70. 16
      lib/open_project/scm/manageable_repository.rb
  71. 123
      lib/tasks/ci.rake
  72. 73
      script/ci_runner.sh
  73. 53
      script/ci_setup.sh
  74. 102
      script/files/parallel_runtime_cucumber.log
  75. 443
      script/files/parallel_runtime_rspec.log
  76. 33
      script/templates/database.travis.mysql.yml
  77. 32
      script/templates/database.travis.postgres.yml
  78. 4
      spec/features/accessibility/custom_fields_spec.rb
  79. 5
      spec/features/accessibility/work_packages/work_package_query_spec.rb
  80. 8
      spec/features/menu_items/query_menu_item_spec.rb
  81. 2
      spec/features/menu_items/top_menu_item_spec.rb
  82. 126
      spec/features/projects/projects_spec.rb
  83. 18
      spec/features/repositories/create_repository_spec.rb
  84. 27
      spec/features/repositories/repository_settings_spec.rb
  85. 2
      spec/features/users/create_spec.rb
  86. 4
      spec/features/users/delete_spec.rb
  87. 6
      spec/features/work_packages/details/activity_comments_spec.rb
  88. 4
      spec/features/work_packages/details/inplace_editor/description_editor_spec.rb
  89. 4
      spec/features/work_packages/details/inplace_editor/subject_editor_spec.rb
  90. 2
      spec/features/work_packages/navigation_spec.rb
  91. 12
      spec/features/work_packages/select_work_package_row_spec.rb
  92. 13
      spec/features/work_packages/shared_contexts.rb
  93. 26
      spec/lib/api/v3/root_representer_spec.rb
  94. 103
      spec/lib/api/v3/user_preferences/user_preferences_representer_spec.rb
  95. 6
      spec/lib/api/v3/utilities/path_helper_spec.rb
  96. 4
      spec/lib/api/v3/work_packages/work_packages_shared_helpers_spec.rb
  97. 102
      spec/models/user_preference_spec.rb
  98. 11
      spec/requests/api/v3/root_resource_spec.rb
  99. 141
      spec/requests/api/v3/user_preferences/user_preferences_resource_spec.rb
  100. 2
      spec/services/scm/checkout_instructions_service_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,10 +1,10 @@
stage:
before:
- npm install -g npm
- cd frontend;
/usr/local/bin/npm install --unsafe-perm --ignore-scripts &&
- npm install npm
- pushd frontend;
npm install --unsafe-perm --ignore-scripts;
bower install --allow-root;
cd ..
popd
- cp config/configuration.yml.example config/configuration.yml
- cp config/database.teatro.yml config/database.yml
- bundle exec rake generate_secret_token

@ -39,49 +39,75 @@ cache:
- frontend/node_modules
- frontend/bower_components
bundler_args: --without development production
branches:
only:
- master
- dev
- /^(stable|release)\/.*$/
env:
global:
- CI=true
- RAILS_ENV=test
- BUNDLE_WITHOUT=development
- COVERAGE=true
matrix:
# Frontend
- "TEST_SUITE=karma"
- "TEST_SUITE=protractor"
# mysql2
- "TEST_SUITE=cucumber DB=mysql2"
- "TEST_SUITE=spec DB=mysql2"
- "TEST_SUITE=spec:legacy DB=mysql2"
# postgres
- "TEST_SUITE=cucumber DB=postgres"
- "TEST_SUITE=spec DB=postgres"
- "TEST_SUITE=spec:legacy DB=postgres"
- "TEST_SUITE=npm"
- "TEST_SUITE=legacy DB=mysql $GROUP_SIZE=2 $GROUP=1"
- "TEST_SUITE=legacy DB=mysql $GROUP_SIZE=2 $GROUP=2"
- "TEST_SUITE=cucumber DB=mysql $GROUP_SIZE=3 $GROUP=1"
- "TEST_SUITE=cucumber DB=mysql $GROUP_SIZE=3 $GROUP=2"
- "TEST_SUITE=cucumber DB=mysql $GROUP_SIZE=3 $GROUP=3"
- "TEST_SUITE=spec DB=mysql $GROUP_SIZE=6 $GROUP=1"
- "TEST_SUITE=spec DB=mysql $GROUP_SIZE=6 $GROUP=2"
- "TEST_SUITE=spec DB=mysql $GROUP_SIZE=6 $GROUP=3"
- "TEST_SUITE=spec DB=mysql $GROUP_SIZE=6 $GROUP=4"
- "TEST_SUITE=spec DB=mysql $GROUP_SIZE=6 $GROUP=5"
- "TEST_SUITE=spec DB=mysql $GROUP_SIZE=6 $GROUP=6"
- "TEST_SUITE=legacy DB=postgres $GROUP_SIZE=2 $GROUP=1"
- "TEST_SUITE=legacy DB=postgres $GROUP_SIZE=2 $GROUP=2"
- "TEST_SUITE=cucumber DB=postgres $GROUP_SIZE=3 $GROUP=1"
- "TEST_SUITE=cucumber DB=postgres $GROUP_SIZE=3 $GROUP=2"
- "TEST_SUITE=cucumber DB=postgres $GROUP_SIZE=3 $GROUP=3"
- "TEST_SUITE=spec DB=postgres $GROUP_SIZE=6 $GROUP=1"
- "TEST_SUITE=spec DB=postgres $GROUP_SIZE=6 $GROUP=2"
- "TEST_SUITE=spec DB=postgres $GROUP_SIZE=6 $GROUP=3"
- "TEST_SUITE=spec DB=postgres $GROUP_SIZE=6 $GROUP=4"
- "TEST_SUITE=spec DB=postgres $GROUP_SIZE=6 $GROUP=5"
- "TEST_SUITE=spec DB=postgres $GROUP_SIZE=6 $GROUP=6"
before_install:
- "echo `firefox -v`"
- "export DISPLAY=:99.0"
- "/sbin/start-stop-daemon --start -v --pidfile ./tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1920x1080x16"
- "echo `xdpyinfo -display :99 | grep 'dimensions' | awk '{ print $2 }'`"
- "nvm install 0.10 && nvm use 0.10"
- "cd frontend && travis_retry npm install && cd .."
- travis_retry npm install
# We need phantomjs 2.0 to get tests passing
- mkdir travis-phantomjs
- wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2
- tar -xvf $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -C $PWD/travis-phantomjs
- export PATH=$PWD/travis-phantomjs:$PATH
before_script:
- if [[ ! $TEST_SUITE =~ ^(karma|protractor)$ ]]; then
RAILS_ENV=production bundle exec rake ci:travis:prepare;
fi
- sh script/ci_setup.sh $DB
script:
- if [[ $TEST_SUITE =~ ^(karma|protractor)$ ]]; then
cd frontend && npm run $TEST_SUITE;
else
bundle exec rake $TEST_SUITE;
fi
- sh script/ci_runner.sh $TEST_SUITE $GROUP_SIZE $GROUP
notifications:
email: false
slack:
secure: "a+I0uMgXgrDd3aitr2yhXrh7g/UOUTwoDVElunY7gYdrM+gpZ6RE1AP4/Q++hERBCs7rUBzmb//zxGTcc8Nw4nGqZOmPOMIsAoD49UupGLUzHbxzKlpwdBcwh77fq3rYwkjZjE/H1qiElPT7v6qyWMSdNGlj/bAB74eD7Zl3S5cMRvZ1whbSg2GA2v6ZqtXaKfrSFrPRzsIOBXs99OxWNWAsUGpEwTYac7wb6rdMJkBbzosp4gP99mGvQArEzo0nrIQgRH8W4Q6iLnrpX0g5uKccWl1u/G2bmH8L4F50ce4uuUE+TtHO/nfNFnb2KuDR4QyoccQQbGHXL/jaaAZXG/gzs5Hmru2Thaym43fSwxos80xmZs1vqB/rXE+Rg9qXcCKyyX31zjSI/iW4wS015fz8MKVX6qDg49epaw1ovn0AOYrvTd94GV6RX6eJ3/l+KJJHSKaaLP/713h11LWx/S27tiB40fboXQ68YzIQCuahRUEHUfhU3P10Wf9y2fdDsthtHHSrOJMQ3Ii/Jm3KQm6bE5RWORdHvc/sF2WLfLmJ627j9JhWYYi5mDKJ9AeMWtZNHreU0mM27OUgfhiW11ItKgpwQPEiiicrlYRrMmK+9hc9cym+8tRM+wEth1xhIkfgQFtngONKjv361Wt3JifxM79+bn0IyF72vAVNy8k="
addons:
firefox: "38.0esr"
postgresql: "9.3"
# Disabling coverage reporting until CodeClimate supports merging results from multiple partial tests
# code_climate:
# repo_token:

@ -153,13 +153,15 @@ group :test do
gem 'rspec-rails', '~> 3.3.0', group: :development
gem 'rspec-activemodel-mocks', '~> 1.0.2', git: 'https://github.com/rspec/rspec-activemodel-mocks'
gem 'rspec-example_disabler', git: 'https://github.com/finnlabs/rspec-example_disabler.git'
gem 'rspec-legacy_formatters'
gem 'rspec-legacy_formatters', require: false
gem 'capybara', '~> 2.4.4'
gem 'capybara-screenshot', '~> 1.0.4'
gem 'capybara-select2', github: 'goodwill/capybara-select2'
gem 'capybara-ng', '~> 0.2.1'
gem 'selenium-webdriver', '~> 2.47.1'
gem 'poltergeist'
gem 'timecop', '~> 0.7.1'
gem 'webmock', '~> 1.21.0', require: false
gem 'rb-readline', '~> 0.5.1' # ruby on CI needs this
# why in Gemfile? see: https://github.com/guard/guard-test
@ -170,8 +172,6 @@ group :test do
gem 'activerecord-tableless', '~> 1.0'
gem 'codecov', require: nil
gem 'equivalent-xml', '~> 0.5.1'
gem 'rspec-retry'
end
group :ldap do
@ -183,8 +183,6 @@ group :development do
gem 'thin'
gem 'faker'
gem 'quiet_assets'
gem 'rubocop', '~> 0.32'
gem 'parallel_tests'
end
group :development, :test do
@ -193,6 +191,8 @@ group :development, :test do
gem 'pry-rescue'
gem 'pry-byebug', platforms: [:mri]
gem 'pry-doc'
gem 'parallel_tests'
gem 'rubocop', '~> 0.32'
end
# API gems

@ -45,7 +45,7 @@ GIT
GIT
remote: https://github.com/opf/openproject-translations.git
revision: 33df82fe1466fc692ab7e5c27935e3ce88ba1d87
revision: 01e01ea286e2cb86525df7a099be9260de2dc143
branch: dev
specs:
openproject-translations (5.0.0.pre.alpha)
@ -170,6 +170,7 @@ GEM
ffi (~> 1.0, >= 1.0.11)
climate_control (0.0.3)
activesupport (>= 3.0)
cliver (0.3.2)
cocaine (0.5.7)
climate_control (>= 0.0.3, < 1.0)
codecov (0.0.8)
@ -181,6 +182,8 @@ GEM
descendants_tracker (~> 0.0.1)
color-tools (1.3.0)
columnize (0.9.0)
crack (0.4.2)
safe_yaml (~> 1.0.0)
crowdin-api (0.2.8)
rest-client (~> 1.6.8)
cucumber (1.3.19)
@ -324,6 +327,11 @@ GEM
parser (2.2.2.5)
ast (>= 1.1, < 3.0)
pg (0.18.3)
poltergeist (1.7.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
powerpack (0.1.1)
protected_attributes (1.0.9)
activemodel (>= 4.0.1, < 5.0)
@ -434,8 +442,6 @@ GEM
rspec-expectations (~> 3.3.0)
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-retry (0.4.4)
rspec-core
rspec-support (3.3.0)
rubocop (0.32.1)
astrolabe (~> 1.3)
@ -453,6 +459,7 @@ GEM
json (>= 1.7.5)
structured_warnings (>= 0.1.3)
rubyzip (1.1.7)
safe_yaml (1.0.4)
sass (3.4.13)
sass-rails (5.0.3)
railties (>= 4.0.0, < 5.0)
@ -511,7 +518,13 @@ GEM
rack (>= 1.0)
warden-basic_auth (0.2.1)
warden (~> 1.2)
webmock (1.21.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
websocket (1.2.2)
websocket-driver (0.6.2)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
will_paginate (3.0.7)
xpath (2.0.0)
nokogiri (~> 1.3)
@ -572,6 +585,7 @@ DEPENDENCIES
openproject-translations!
parallel_tests
pg (~> 0.18.3)
poltergeist
protected_attributes
prototype-rails!
prototype_legacy_helper (= 0.0.0)!
@ -600,7 +614,6 @@ DEPENDENCIES
rspec-example_disabler!
rspec-legacy_formatters
rspec-rails (~> 3.3.0)
rspec-retry
rubocop (~> 0.32)
ruby-duration (~> 3.2.0)
ruby-prof
@ -622,6 +635,7 @@ DEPENDENCIES
unicorn
warden (~> 1.2)
warden-basic_auth (~> 0.2.1)
webmock (~> 1.21.0)
will_paginate (~> 3.0)
BUNDLED WITH

@ -34,7 +34,7 @@
dangerZoneVerification.find('input').on('input', function(){
var actualValue = dangerZoneVerification.find('input').val();
if (expectedValue === actualValue) {
if (expectedValue.toLowerCase() === actualValue.toLowerCase()) {
dangerZoneVerification.find('button').prop('disabled', false);
} else {
dangerZoneVerification.find('button').prop('disabled', true);
@ -42,4 +42,3 @@
});
});
}(jQuery));

@ -105,7 +105,6 @@ div.issue div.subject
margin-top: 0.5em
.buttons
margin-bottom: 1.4em
margin-top: 1em
div
@ -498,7 +497,9 @@ a.has-thumb
-o-text-overflow: ellipsis
-ms-text-overflow: ellipsis
label.label-with-input
label
margin-bottom: 0rem
&.label-with-input
display: block
white-space: nowrap
zoom: 1

@ -372,7 +372,7 @@ $form-padding: 0.375rem;
// Labels
// $form-label-fontsize: 0.9rem;
// $form-label-margin: 0.5rem;
// $form-label-margin: 0rem;
// $form-label-color: #333;
// Inline labels

@ -71,28 +71,17 @@
.advanced-filters--number-field
@extend .form--text-field, .form--text-field.-small
%advanced-filters--filter-part
.inline-label
margin-bottom: 0
// adding the input rules to be more specific than the resets of
// foundation.
input, select
margin-bottom: 0
.advanced-filters--filter-name
@include grid-content(3)
@extend %advanced-filters--filter-part
overflow-y: hidden
white-space: nowrap
text-overflow: ellipsis
.advanced-filters--filter-operator
@include grid-content(3)
@extend %advanced-filters--filter-part
.advanced-filters--filter-value
@include grid-content(5)
@extend %advanced-filters--filter-part
.advanced-filters--remove-filter
@include grid-content($size: shrink)
@ -106,7 +95,6 @@
.advanced-filters--add-filter
@include advanced-filters--sizing
@extend %advanced-filters--filter-part
align-items: center
padding: 0.25rem 0

@ -36,6 +36,7 @@ $form--field-types: (text-field, text-area, select, check-box, radio-button, ran
border: $content-form-input-hover-border
vertical-align: middle
margin-bottom: 0rem
%label-style
text-align: left
@ -272,6 +273,7 @@ fieldset.form--fieldset
@include grid-visible-overflow
align-items: center
margin-bottom: 0.825rem
height: 100%
&.-vertical,
.form.-vertical &
@ -453,8 +455,10 @@ fieldset.form--fieldset
@extend %input-style
.form--text-field,
#{$text-input-selectors}
#{$text-input-selectors},
select
line-height: 100%
margin-bottom: 0rem
input[readonly].-clickable
cursor: pointer
@ -479,6 +483,12 @@ input[readonly].-clickable
border: none
padding-right: 0
&.select2-container
// styles adapted to input fields to align them
margin-bottom: 0.5rem
.select2-choice
height: 2.15rem
.form--text-field,
.form--select
&.-tiny
@ -495,13 +505,13 @@ input[readonly].-clickable
max-width: 100%
.form &
margin-bottom: 0.5rem
margin-bottom: 0rem
.form--text-area
@extend %input-style
.form &
margin-bottom: 0.5rem
margin-bottom: 0rem
.form--radio-button-container
//prevent radio-buttons from being cut at the border
@ -513,6 +523,7 @@ input[readonly].-clickable
.form--grouping-row
@include grid-block(10)
align-items: center
.form--grouping-row + .form--grouping-row
@include grid-offset(2)
@ -567,6 +578,7 @@ input[readonly].-clickable
flex-basis: auto
.inline-label
margin: 0rem
> .form-label.-transparent
margin-bottom: 0
font-size: 1em
@ -593,7 +605,7 @@ input[readonly].-clickable
// OR $inlinelabel-border
border-radius: 2px
padding: 0 $form-padding
margin-bottom: 0.5rem
margin-bottom: 0rem
align-items: center
line-height: 1

@ -97,6 +97,7 @@ ul.legacy-actions-more
.legacy-actions--inline-label
@extend .inline-label
margin-bottom: 1rem
height: 1.5em
.form-label

@ -278,7 +278,8 @@ $se2-arrow-button-width: 32px;
}
}
.select2-drop:not(.project-search-results):not(.select2-display-none) {
.select2:not(.select2-container-multi) {
.select2-drop:not(.project-search-results):not(.select2-display-none) {
background: transparent;
border: none;
box-shadow: none;
@ -320,4 +321,5 @@ $se2-arrow-button-width: 32px;
min-height: 38px;
padding: 9px;
}
}
}

@ -38,7 +38,7 @@ $filters--border-color: $gray !default
.simple-filters--container
@extend %filters--container
padding: 1rem 1rem 0
padding: 1rem 1rem 1rem 1rem
margin: 0.6em 0
.simple-filter--trailing-labels
@ -70,6 +70,7 @@ $filters--border-color: $gray !default
.simple-filters--filter,
padding: 0
display: flex
align-items: center
@include breakpoint(large)
.simple-filters--filter-name
@ -83,10 +84,14 @@ $filters--border-color: $gray !default
.simple-filters--controls
padding: 0
align-self: flex-start
align-self: center
@include breakpoint(large)
flex: 1
button,
.button
margin-bottom: 0rem
.simple-filters--filters.-columns-3
justify-content: flex-start
@include breakpoint(large)
@ -100,4 +105,9 @@ $filters--border-color: $gray !default
.simple-filters--filter,
.simple-filters--controls
padding: 0
align-self: center
@include breakpoint(large)
button,
.button
margin-bottom: 0rem

@ -142,14 +142,15 @@ table.generic-table
overflow: hidden
text-overflow: ellipsis
text-align: left
line-height: 34px
line-height: 2rem
vertical-align: middle
// Center input fields and select boxes vertically in tables
.form--field
margin: 0px
@each $inputElement in $input-elements
#{$inputElement}
#{$inputElement},
#{$inputElement}~.form-label
margin-top: 0.5rem
margin-bottom: 0.5rem

@ -559,7 +559,7 @@ module ApplicationHelper
I18n.defaultLocale = "#{I18n.default_locale}";
I18n.locale = "#{I18n.locale}";
})
unless User.current.pref.warn_on_leaving_unsaved == '0'
if User.current.pref.warn_on_leaving_unsaved?
tags += javascript_tag(%{
jQuery(document).ready(function(){
warnLeavingUnsaved('#{escape_javascript(l(:text_warn_on_leaving_unsaved))}');

@ -435,13 +435,10 @@ class Repository < ActiveRecord::Base
# is managed by OpenProject
def delete_managed_repository
service = Scm::DeleteManagedRepositoryService.new(self)
# Even if the service can't remove the physical repository,
# we should continue removing the associated instance.
if service.call
true
else
errors.add(:base, I18n.t('repositories.errors.filesystem_access_failed',
message: I18n.t('repositories.errors.deletion_failed')))
errors.add(:base, service.localized_rejected_reason)
raise ActiveRecord::Rollback
end
end

@ -442,7 +442,7 @@ class User < Principal
end
def impaired
(anonymous? && Setting.accessibility_mode_for_anonymous?) || !!pref.impaired
(anonymous? && Setting.accessibility_mode_for_anonymous?) || pref.impaired?
end
def impaired?
@ -450,7 +450,7 @@ class User < Principal
end
def wants_comments_in_reverse_order?
pref[:comments_sorting] == 'desc'
pref.comments_in_reverse_order?
end
# Return user's RSS key (a 40 chars long string), used to access feeds

@ -32,6 +32,8 @@ class UserPreference < ActiveRecord::Base
serialize :others
validates_presence_of :user
validate :time_zone_correctness, if: -> { time_zone.present? }
validate :theme_correctness, if: -> { theme.present? }
attr_accessible :user
@ -59,25 +61,81 @@ class UserPreference < ActiveRecord::Base
others[:comments_sorting] = order
end
def comments_in_reverse_order?
comments_sorting == 'desc'
end
def warn_on_leaving_unsaved?
# Need to cast here as previous values were '0' / '1'
to_boolean(others.fetch(:warn_on_leaving_unsaved) { true })
end
def warn_on_leaving_unsaved=(value)
others[:warn_on_leaving_unsaved] = to_boolean(value)
end
# Provide an alias to form builders
alias :comments_in_reverse_order :comments_in_reverse_order?
alias :warn_on_leaving_unsaved :warn_on_leaving_unsaved?
def comments_in_reverse_order=(value)
others[:comments_sorting] = to_boolean(value) ? 'desc' : 'asc'
end
def theme
others[:theme] || OpenProject::Themes.application_theme_identifier
end
def theme=(order)
others[:theme] = order
def theme=(identifier)
others[:theme] = identifier.nil? ? nil : identifier.to_sym
end
def canonical_time_zone
return if time_zone.nil?
zone = ActiveSupport::TimeZone.new(time_zone)
unless zone.nil?
zone.tzinfo.canonical_identifier
end
end
def impaired?
!!impaired
end
def warn_on_leaving_unsaved?
# Need to cast here as previous values were '0' / '1'
to_boolean(others.fetch(:warn_on_leaving_unsaved) { true })
end
def warn_on_leaving_unsaved
others.fetch(:warn_on_leaving_unsaved) { '1' }
warn_on_leaving_unsaved?
end
def warn_on_leaving_unsaved=(value)
others[:warn_on_leaving_unsaved] = value
others[:warn_on_leaving_unsaved] = to_boolean(value)
end
private
def to_boolean(value)
ActiveRecord::Type::Boolean.new.type_cast_from_user(value)
end
def init_other_preferences
self.others ||= { no_self_notified: true }
end
def time_zone_correctness
errors.add(:time_zone, :inclusion) if time_zone.present? && canonical_time_zone.nil?
end
def theme_correctness
return true if theme == OpenProject::Themes.application_theme_identifier
themes = OpenProject::Themes.all.map(&:identifier)
unless themes.any? { |identifier| theme.to_sym == identifier }
errors.add(:theme, :inclusion)
end
end
end

@ -38,9 +38,6 @@ Scm::CreateManagedRepositoryService = Struct.new :repository do
def call
if repository.managed? && repository.manageable?
# Cowardly refusing to override existing local repository
return false if repository_exists?
##
# We want to move this functionality in a Delayed Job,
# but this heavily interferes with the error handling of the whole
@ -48,7 +45,11 @@ Scm::CreateManagedRepositoryService = Struct.new :repository do
# Instead, this will be refactored into a single service wrapper for
# creating and deleting repositories, which provides transactional DB access
# as well as filesystem access.
Scm::CreateRepositoryJob.new(repository).perform
if repository.class.manages_remote?
Scm::CreateRemoteRepositoryJob.new(repository).perform
else
Scm::CreateLocalRepositoryJob.new(repository).perform
end
return true
end
@ -61,6 +62,9 @@ Scm::CreateManagedRepositoryService = Struct.new :repository do
@rejected = I18n.t('repositories.errors.filesystem_access_failed',
message: e.message)
false
rescue OpenProject::Scm::Exceptions::ScmError => e
@rejected = e.message
false
end
##
@ -68,15 +72,4 @@ Scm::CreateManagedRepositoryService = Struct.new :repository do
def localized_rejected_reason
@rejected ||= I18n.t('repositories.errors.not_manageable')
end
private
##
# Test if the repository exists already on filesystem.
def repository_exists?
if File.directory?(repository.root_url)
@rejected = I18n.t('repositories.errors.exists_on_filesystem')
return true
end
end
end

@ -35,14 +35,20 @@ Scm::DeleteManagedRepositoryService = Struct.new :repository do
# Registers an asynchronous job to delete the repository on disk.
#
def call
if repository.managed?
delete_repository
return false unless repository.managed?
if repository.class.manages_remote?
Scm::DeleteRemoteRepositoryJob.new(repository).perform
true
else
false
delete_local_repository
end
rescue OpenProject::Scm::Exceptions::ScmError => e
@rejected = e.message
false
end
def delete_repository
def delete_local_repository
# Create necessary changes to repository to mark
# it as managed by OP, but delete asynchronously.
managed_path = repository.root_url
@ -55,13 +61,20 @@ Scm::DeleteManagedRepositoryService = Struct.new :repository do
# Instead, this will be refactored into a single service wrapper for
# creating and deleting repositories, which provides transactional DB access
# as well as filesystem access.
Scm::DeleteRepositoryJob.new(managed_path).perform
Scm::DeleteLocalRepositoryJob.new(managed_path).perform
end
true
rescue SystemCallError => e
Rails.logger.error("An error occurred while accessing the repository '#{repository.root_url}'" +
" on filesystem: #{e.message}")
@rejected = I18n.t('repositories.errors.managed_delete_local',
path: repository.root_url,
error_message: e.message)
false
end
##
# Returns the error symbol
def localized_rejected_reason
@rejected ||= I18n.t('repositories.errors.managed_delete')
end
end

@ -34,9 +34,15 @@ See doc/COPYRIGHT.rdoc for more details.
<%= labelled_tabular_form_for @copy_project, url: { action: 'copy', coming_from: 'admin' } do |f| %>
<%= hidden_field_tag :coming_from, 'admin' %>
<%= render :partial => 'projects/form', :locals => { :f => f, :project => @copy_project, :renderTypes => true } %>
<%= render :partial => "copy_projects/copy_settings/copy_associations", :locals => { :project => @project } %>
<%= render partial: 'projects/form', locals: { f: f,
project: @copy_project,
render_advanced: false,
render_types: true,
render_modules: true,
render_custom_fields: true
} %>
<%= render partial: "copy_projects/copy_settings/copy_associations", locals: { project: @project } %>
<%= submit_tag l(:button_copy), class: 'button -highlight' %>
<% end %>

@ -30,7 +30,10 @@ See doc/COPYRIGHT.rdoc for more details.
<%= render partial: 'form', locals: {
f: f,
project: @altered_project,
renderTypes: false
render_advanced: false,
render_types: false,
render_modules: false,
render_custom_fields: false
} %>
<%= f.button l(:button_update), class: 'button -highlight -with-icon icon-yes' %>

@ -33,7 +33,7 @@ See doc/COPYRIGHT.rdoc for more details.
<section class="form--section">
<%= render partial: "projects/form/project_attributes",
locals: { project: project, form: f } %>
locals: { project: project, form: f, render_advanced: render_advanced } %>
<%= call_hook(:view_projects_form, project: project, form: f) %>
</section>
@ -45,7 +45,7 @@ See doc/COPYRIGHT.rdoc for more details.
</section>
<% end %>
<% if project.new_record? %>
<% if render_modules %>
<section class="form--section">
<%= render partial: "projects/form/modules", locals: { form: f } %>
</section>
@ -53,14 +53,14 @@ See doc/COPYRIGHT.rdoc for more details.
<%= javascript_tag 'observeProjectModules();' %>
<% end %>
<% if (project.new_record? || project.module_enabled?('work_package_tracking')) && renderTypes %>
<% if ((project.module_enabled?('work_package_tracking')) && render_types) %>
<section class="form--section">
<%= render partial: 'projects/form/types',
locals: { f: f, project: project } %>
</section>
<% end %>
<% if project.new_record? %>
<% if render_custom_fields %>
<section class="form--section">
<%= render partial: 'projects/form/custom_fields',
locals: {

@ -29,17 +29,29 @@ See doc/COPYRIGHT.rdoc for more details.
<%= render partial: "projects/form/attributes/name",
locals: { form: form } %>
<%= render partial: "projects/form/attributes/parent_id",
<%= render partial: "customizable/form",
locals: { form: form } %>
<%= render partial: "projects/form/attributes/description",
<%= render partial: "projects/form/attributes/responsible_id",
locals: { form: form } %>
<%= render partial: "projects/form/attributes/identifier",
<% if render_advanced %>
<fieldset id="advanced-settings" class="form--fieldset -collapsible collapsed">
<legend class="form--fieldset-legend" title="<%=l(:label_show_hide)%>" onclick="toggleFieldset(this);">
<a href="javascript:"><%= l(:label_advanced_settings) %></a>
</legend>
<div style="display:none;">
<% end %>
<%= render partial: "projects/form/attributes/parent_id",
locals: { form: form } %>
<%= render partial: "customizable/form",
<%= render partial: "projects/form/attributes/description",
locals: { form: form } %>
<%= render partial: "projects/form/attributes/is_public",
<%= render partial: "projects/form/attributes/identifier",
locals: { form: form } %>
<%= render partial: "projects/form/attributes/project_type_id",
<%= render partial: "projects/form/attributes/project_type_id",
locals: { form: form } %>
<%= render partial: "projects/form/attributes/responsible_id",
<%= render partial: "projects/form/attributes/is_public",
locals: { form: form } %>
<% if render_advanced %>
</div>
</fieldset>
<% end %>

@ -29,7 +29,13 @@ See doc/COPYRIGHT.rdoc for more details.
<% html_title l("label_project_new") %>
<%= toolbar title: l(:label_project_new) %>
<%= labelled_tabular_form_for @project do |f| %>
<%= render :partial => 'form', :locals => { :f => f, :project => @project, :renderTypes => true } %>
<%= render partial: 'form', locals: { f: f,
project: @project,
render_advanced: true,
render_types: false,
render_modules: false,
render_custom_fields: false
} %>
<%= styled_button_tag l(:button_create), class: '-highlight -with-icon icon-yes' %>
<%= javascript_tag "Form.Element.focus('project_name');" %>
<% end %>

@ -26,17 +26,5 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.rdoc for more details.
++#%>
<div class="form--field">
<%= form.text_field(:url,
label: l('repositories.git.managed_path'),
value: repository.new_record? ? repository.managed_repository_path :
repository.root_url,
size: 60,
disabled: true,
'aria-disabled' => true
) %>
<div class="form--field-instructions">
<%= l('repositories.git.instructions.managed_url') %>
</div>
</div>
<%= render partial: '/repositories/settings/shared/managed',
locals: { vendor: 'git', form: form, repository: repository } %>

@ -0,0 +1,54 @@
<%#-- copyright
OpenProject is a project management system.
Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.rdoc for more details.
++#%>
<% if repository.class.manages_remote? %>
<div class="form--field">
<%= form.text_field(:url,
label: l('repositories.managed_url'),
value: l('repositories.managed_remote'),
disabled: true,
'aria-disabled' => true
) %>
<div class="form--field-instructions">
<p><%= l('repositories.managed_remote_note') %></p>
</div>
</div>
<% else %>
<div class="form--field">
<%= form.text_field(:url,
label: l('repositories.managed_url'),
value: repository.new_record? ? repository.managed_repository_url :
repository.url,
disabled: true,
'aria-disabled' => true
) %>
<div class="form--field-instructions">
<%= l("repositories.#{vendor}.instructions.managed_url") %>
</div>
</div>
<% end %>

@ -26,16 +26,5 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See doc/COPYRIGHT.rdoc for more details.
++#%>
<div class="form--field">
<%= form.text_field(:url,
label: l('repositories.subversion.managed_url'),
value: repository.new_record? ? repository.managed_repository_url :
repository.url,
size: 60,
disabled: true,
'aria-disabled' => true
) %>
<div class="form--field-instructions">
<%= l('repositories.subversion.instructions.managed_url') %>
</div>
</div>
<%= render partial: '/repositories/settings/shared/managed',
locals: { vendor: 'subversion', form: form, repository: repository } %>

@ -34,10 +34,17 @@
# We envision a repository management wrapper that covers transactional
# creation and deletion of repositories BOTH on the database and filesystem.
# Until then, a synchronous process is more failsafe.
class Scm::CreateRepositoryJob
class Scm::CreateLocalRepositoryJob
include OpenProject::BeforeDelayedJob
def initialize(repository)
# Cowardly refusing to override existing local repository
if File.directory?(repository.root_url)
raise OpenProject::Scm::Exceptions::ScmError.new(
I18n.t('repositories.errors.exists_on_filesystem')
)
end
# TODO currently uses the full repository object,
# as the Job is performed synchronously.
# Change this to serialize the ID once its turned to process asynchronously.

@ -0,0 +1,42 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
##
# Provides an asynchronous job to create a managed repository on a remote system
# using a simple HTTP callback
# Currently, this is run synchronously due to potential issues
# with error handling.
# We envision a repository management wrapper that covers transactional
# creation and deletion of repositories BOTH on the database and filesystem.
# Until then, a synchronous process is more failsafe.
class Scm::CreateRemoteRepositoryJob < Scm::RemoteRepositoryJob
def perform
send(repository_request.merge(action: :create))
end
end

@ -34,7 +34,7 @@
# We envision a repository management wrapper that covers transactional
# creation and deletion of repositories BOTH on the database and filesystem.
# Until then, a synchronous process is more failsafe.
class Scm::DeleteRepositoryJob
class Scm::DeleteLocalRepositoryJob
include OpenProject::BeforeDelayedJob
def initialize(managed_path)

@ -0,0 +1,41 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
##
# Provides an asynchronous job to delete a managed repository on the filesystem.
# Currently, this is run synchronously due to potential issues
# with error handling.
# We envision a repository management wrapper that covers transactional
# creation and deletion of repositories BOTH on the database and filesystem.
# Until then, a synchronous process is more failsafe.
class Scm::DeleteRemoteRepositoryJob < Scm::RemoteRepositoryJob
def perform
send(repository_request.merge(action: :delete))
end
end

@ -0,0 +1,93 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
##
# Provides an asynchronous job to create a managed repository on the filesystem.
# Currently, this is run synchronously due to potential issues
# with error handling.
# We envision a repository management wrapper that covers transactional
# creation and deletion of repositories BOTH on the database and filesystem.
# Until then, a synchronous process is more failsafe.
class Scm::RemoteRepositoryJob
include OpenProject::BeforeDelayedJob
def initialize(repository)
# TODO currently uses the full repository object,
# as the Job is performed synchronously.
# Change this to serialize the ID once its turned to process asynchronously.
@repository = repository
end
protected
##
# Submits the request to the configured managed remote as JSON.
def send(request)
uri = @repository.class.managed_remote
req = ::Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req.body = request.to_json
response = ::Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
unless response.is_a? ::Net::HTTPSuccess
info = try_to_parse_response(response.body)
raise OpenProject::Scm::Exceptions::ScmError.new(
I18n.t('repositories.errors.remote_call_failed',
code: response.code,
message: info['message']
)
)
end
end
def try_to_parse_response(body)
JSON.parse(body)
rescue JSON::JSONError => e
raise OpenProject::Scm::Exceptions::ScmError.new(
I18n.t('repositories.errors.remote_invalid_response')
)
end
def repository_request
project = @repository.project
{
identifier: @repository.repository_identifier,
vendor: @repository.vendor,
scm_type: @repository.scm_type,
project: {
id: project.id,
name: project.name,
identifier: project.identifier,
}
}
end
end

@ -218,22 +218,25 @@ default:
# Absolute path (e.g. /usr/local/bin/hg) or command name (e.g. hg.exe, bzr.exe)
# On Windows, *.cmd, *.bat (e.g. hg.cmd, bzr.bat) does not work.
# manages:
# Enable managed repositories for this vendor. This allows OpenProject to
# take control over the given path to create and delete repositories directly
# when created in the frontend.
# Enable managed repositories for this vendor.
# You may either specify a local path on the filesystem or an absolute URL to call when
# repositories are to be created or deleted.
# This allows OpenProject to take control over the given path to create and delete repositories
# directly when created in the frontend.
#
# When entering a URL, OpenProject will POST to this resource when repositories are created
# using the following JSON-encoded payload:
# - action: The action to perform (create, delete)
# - identifier: The repository identifier name
# - vendor: The SCM vendor of the repository to create
# - project: identifier, name and ID of the associated project
#
# NOTE: Disabling :managed repositories using disabled_types takes precedence over this setting.
# mode:
# The octal file mode to set the repository folder to (defaults to 0700).
#
# group:
# The group that should own the repository folder.
# Important: The user running OpenProject must be a member of this group in order to have the
# privilege to set the group ownership.
# Note: The owner is always the user that runs OpenProject!
# disabled_types:
# Disable specific repository types for this particular vendor. This allows
# to restrict the available choices a project administrator has for creating repositories
# See the example below for available types
# See the example below for available types.
#
# Available types for git:
# - :local (Local repositories, registered using a local path)
@ -243,7 +246,7 @@ default:
# using one of the supported URL schemes (e.g., https://, svn+ssh:// )
# - :managed (Managed repositores, available IF :manages path is set below)
#
# Examplary configuration
# Exemplary configuration
#
# scm:
# git:

@ -46,7 +46,7 @@ module ConnectionIsolationLevel
isolation_level = 'ISOLATION LEVEL READ COMMITTED'
if OpenProject::Database.mysql?(connection)
connection.execute("SET SESSION TRANSACTION #{isolation_level}")
else
elsif OpenProject::Database.postgresql?(connection)
connection.execute("SET SESSION CHARACTERISTICS AS TRANSACTION #{isolation_level}")
end
end

@ -687,6 +687,7 @@ en:
label_additional_workflow_transitions_for_assignee: "Additional transitions allowed when the user is the assignee"
label_additional_workflow_transitions_for_author: "Additional transitions allowed when the user is the author"
label_administration: "Administration"
label_advanced_settings: "Advanced settings"
label_age: "Age"
label_ago: "days ago"
label_all: "all"
@ -842,6 +843,7 @@ en:
label_integer: "Integer"
label_internal: "Internal"
label_invite_user: "Invite user"
label_show_hide: "Show/hide"
label_work_package_added: "Work package added"
label_work_package_category_new: "New category"
label_work_package_category_plural: "Work package categories"
@ -1079,7 +1081,7 @@ en:
label_user_mail_option_only_assigned: "Only for things I am assigned to"
label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in"
label_user_mail_option_only_owner: "Only for things I am the owner of"
label_user_mail_option_selected: "For any event on the selected projects only..."
label_user_mail_option_selected: "For any event on the selected projects only"
label_user_new: "New user"
label_user_plural: "Users"
label_user_search: "Search for user"
@ -1400,10 +1402,11 @@ en:
title: "Do you really want to delete the %{repository_type} of the project %{project_name}?"
errors:
build_failed: "Unable to create the repository with the selected configuration. %{reason}"
managed_delete: "Unable to delete the managed repository."
managed_delete_local: "Unable to delete the local repository on filesystem at '%{path}': %{error_message}"
empty_repository: "The repository exists, but is empty. It does not contain any revisions yet."
exists_on_filesystem: "The repository directory already exists in the filesystem."
filesystem_access_failed: "An error occurred while accessing the repository in the filesystem: %{message}"
deletion_failed: "The repository could not be deleted from disk due to lacking permissions. Please contact your administrator to resolve this issue."
not_manageable: "This repository vendor cannot be managed by OpenProject."
path_permission_failed: "An error occurred trying to create the following path: %{path}. Please ensure that OpenProject may write to that folder."
unauthorized: "You're not authorized to access the repository or the credentials are invalid."
@ -1411,6 +1414,8 @@ en:
exception_title: "Cannot access the repository: %{message}"
disabled_or_unknown_type: "The selected type %{type} is disabled or no longer available for the SCM vendor %{vendor}."
disabled_or_unknown_vendor: "The SCM vendor %{vendor} is disabled or no longer available."
remote_call_failed: "Calling the managed remote failed with message '%{message}' (Code: %{code})"
remote_invalid_response: "Received an invalid response from the managed remote."
git:
instructions:
managed_url: "This is the URL of the managed (local) Git repository."
@ -1418,12 +1423,14 @@ en:
path_encoding: "Override Git path encoding (Default: UTF-8)"
local_title: "Link existing local Git repository"
local_introduction: "If you have an existing local Git repository, you can link it with OpenProject to access it from within the application."
managed_path: "Managed Git path (preview)"
managed_introduction: "Let OpenProject create and integrate a local Git repository automatically."
managed_title: "Git repository integrated into OpenProject"
path: "Path to Git repository"
path_encoding: "Path encoding"
go_to_revision: "Go to revision"
managed_remote: "Managed repositories for this vendor are handled remotely."
managed_remote_note: "Information on the URL and path of this repository is not available prior to its creation."
managed_url: "Managed URL"
settings:
automatic_managed_repos_disabled: "Disable automatic creation"
automatic_managed_repos: "Automatic creation of managed repositories"
@ -1445,7 +1452,6 @@ en:
url: "Enter the repository URL. This may either target a local repository (starting with %{local_proto} ), or a remote repository.\nThe following URL schemes are supported:"
managed_title: "Subversion repository integrated into OpenProject"
managed_introduction: "Let OpenProject create and integrate a local Subversion repository automatically."
managed_url: "Managed URL"
password: "Repository Password"
username: "Repository username"
named_repository: "%{vendor_name} repository"

@ -26,10 +26,20 @@ The following is an excerpt of the configuration and contains all required infor
# Absolute path (e.g. /usr/local/bin/hg) or command name (e.g. hg.exe, bzr.exe)
# On Windows, *.cmd, *.bat (e.g. hg.cmd, bzr.bat) does not work.
# manages:
# Enable managed repositories for this vendor. This allows OpenProject to
# take control over the given path to create and delete repositories directly
# when created in the frontend.
# You may either specify a local path on the filesystem or an absolute URL to call when
# repositories are to be created or deleted.
# This allows OpenProject to take control over the given path to create and delete repositories
# directly when created in the frontend.
#
# When entering a URL, OpenProject will POST to this resource when repositories are created
# using the following JSON-encoded payload:
# - action: The action to perform (create, delete)
# - identifier: The repository identifier name
# - vendor: The SCM vendor of the repository to create
# - project: identifier, name and ID of the associated project
#
# NOTE: Disabling :managed repositories using disabled_types takes precedence over this setting.
#
# disabled_types:
# Disable specific repository types for this particular vendor. This allows
# to restrict the available choices a project administrator has for creating repositories
@ -51,7 +61,7 @@ The following is an excerpt of the configuration and contains all required infor
With this configuration, you can create managed repositories by selecting the `managed` Git repository in the Project repository settings tab.
### reposman.rb
### Reposman.rb
Part of the managed repositories functionality was previously provided with reposman.rb.
Reposman periodically checked for new projects and automatically created a repository of a given type.
@ -59,6 +69,55 @@ It never deleted repositories on the filesystem when their associated project wa
This script has been integrated into OpenProject and extended. If you previously used reposman, please see the [upgrade guide to 5.0](./upgrade-guide.md) for further guidance on how to migrate to managed repositories.
### Managing Repositories Remotely
OpenProject comes with a simple webhook to call other services rather than management repositories itself.
To enable remote managed repositories, simply pass an absolute URL to the `manages` key of a vendor in the `configuration.yml`. The following excerpt shows that configuration for Subversion, assuming your callback is `https://example.org/repos`.
scm:
subversion:
manages: https://example.org/repos
accesstoken: <Fixed access token passed to the endpoint>
Upon creating and deleting repositories in the frontend, OpenProject will POST to this endpoint a JSON object containg information on the repository.
{
"identifier": "seeded_project.git",
"vendor": "git",
"scm_type": "managed",
"project": {
"id": 1,
"name": "Seeded Project",
"identifier": "seeded_project"
},
"action": "create",
"token": <Fixed access token passed to the endpoint>
}
Our main use-case for this feature is to reduce the complexity of permission issues around Subversion mainly in packager, for which a simple Apache wrapper script is used in `extra/Apache/OpenProjectRepoman.pm`.
This functionality is very limited, but may be extended when other use cases arise.
If you're interested in setting up the integration manually outside the context of packager, the following excerpt will help you:
PerlSwitches -I/srv/www/perl-lib -T
PerlLoadModule Apache::OpenProjectRepoman
<Location /repos>
SetHandler perl-script
# Sets the access token secret to check against
AccessSecret "<Fixed access token passed to the endpoint>"
# Configure pairs of (vendor, path) to the wrapper
PerlAddVar ScmVendorPaths "git"
PerlAddVar ScmVendorPaths "/srv/repositories/git"
PerlAddVar ScmVendorPaths "subversion"
PerlAddVar ScmVendorPaths "/srv/repositories/subversion"
PerlResponseHandler Apache::OpenProjectRepoman
</Location>
## Other Features
OpenProject 5.0 introduces more features regarding repository management that we briefly outline in the following.
@ -138,7 +197,7 @@ The following workarounds exist:
This is a simple solution, but theoretically less secure when the server provides more than just SVN and OpenProject.
#### Use filesystem ACLs
#### Use Filesystem ACLs
You can define ACLs on the managed repository root (requires compatible FS).
You'll need the the `acl` package and define the ACL.
@ -178,6 +237,12 @@ On many file systems, ACLS are enabled by default. On others, you might need to
Note that this issue applies to mod_dav_svn only.
### Use the Apache wrapper script
Similar to the integration we use ourselves for the packager-based installation, you can set up Apache to manage repositories using the remote hook in OpenProject.
For more information, see the section 'Managing Repositories Remotely'.
### Exemplary Apache Configuration
We provide an example apache configuration. Some details are explained inline as comments.

@ -1,4 +1,4 @@
package Apache::Authn::OpenProject;
package Apache::OpenProjectAuthentication;
use strict;
use warnings FATAL => 'all', NONFATAL => 'redefine';

@ -0,0 +1,182 @@
package Apache::OpenProjectRepoman;
use strict;
use warnings FATAL => 'all', NONFATAL => 'redefine';
use File::Path qw(remove_tree);
use File::Spec ();
use Apache2::Module;
use Apache2::Module;
use Apache2::Access;
use Apache2::ServerRec qw();
use Apache2::Response ();
use Apache2::RequestRec qw();
use Apache2::RequestUtil qw();
use Apache2::RequestIO qw();
use Apache2::Const -compile => qw(FORBIDDEN OK OR_AUTHCFG TAKE1 HTTP_UNPROCESSABLE_ENTITY HTTP_BAD_REQUEST OK);
use APR::Table ();
use JSON::PP;
use Carp;
##
# Add AccessSecret directive to Apache, which is checked during configtest
my @directives = (
{
name => 'AccessSecret',
req_override => Apache2::Const::OR_AUTHCFG,
args_how => Apache2::Const::TAKE1,
errmsg => 'Secret access token used to access the repository wrapper.',
}
);
Apache2::Module::add(__PACKAGE__, \@directives);
##
# Accepts and tests the access secret value given in the Apache configuration
sub AccessSecret {
my ($self, $parms, @args) = @_;
$self->{token} = $args[0];
unless (length($self->{token}) >= 8) {
die "Use at least 8 characters for the repoman access token!";
}
}
##
# Creates an actual repository on disk for Subversion and Git.
sub create_repository {
my ($r, $vendor, $repository) = @_;
my $command = {
git => "git init $repository --shared --bare",
subversion => "svnadmin create $repository"
}->{$vendor};
die "No create command known for vendor '$vendor'\n" unless defined($command);
die "Could not create repository.\n" unless system($command) == 0;
}
##
# Removes the repository with a given identifier on disk.
sub delete_repository {
my ($r, $vendor, $repository) = @_;
remove_tree($repository, { safe => 1 }) if -d $repository;
}
##
# Extract and return JSON request from the Apache request handler.
sub parse_request {
my $r = shift;
my $len = $r->headers_in->{'Content-Length'};
die "Request invalid.\n" unless (defined($len) && $len > 0);
die "Request too large.\n" if ($len > (2**13));
my ($buf, $content);
while($r->read($buf, $len)) {
$content .= $buf;
}
return decode_json($content);
}
##
# Returns a JSON error and sets the HTTP response code to $type.
sub make_error {
my ($r, $type, $msg) = @_;
my $response = {
success => JSON::PP::false,
message => $msg
};
$r->status($type) ;
return $response;
}
##
# Actual incoming request handler, that receives the JSON request
# and determines the necessary local action from the request.
sub _handle_request {
my $r = shift;
# Parse JSON request
my $request = parse_request($r);
# Get repository root for the current vendor
my %paths = $r->dir_config->get('ScmVendorPaths');
my $vendor = $request->{vendor};
my $repository_root = $paths{$vendor};
# Compare access token
my $passed_token = $request->{token};
my $cfg = Apache2::Module::get_config( __PACKAGE__, $r->server, $r->per_dir_config );
unless (length($passed_token) >= 8 && ($passed_token eq $cfg->{token})) {
return make_error($r, Apache2::Const::FORBIDDEN, 'Invalid access token');
}
# Abort unless repository root is configured in the Apache configuration
unless (defined($repository_root)) {
return make_error($r,
Apache2::Const::HTTP_UNPROCESSABLE_ENTITY,
"Vendor '$vendor' not configured.");
}
# Abort unless the repository root actually exists
unless (-d $repository_root) {
return make_error($r,
Apache2::Const::HTTP_UNPROCESSABLE_ENTITY,
"Repository path for vendor '$vendor' does not exist.");
}
# Determine validity of the identifier as a dir name
my $repository_identifier = $request->{identifier};
if ($repository_identifier =~ m{[\\/:*?"<>|]}) {
return make_error($r,
Apache2::Const::HTTP_UNPROCESSABLE_ENTITY,
"Repository identifier is an invalid filename");
}
# Call the necessary action on disk
my $target = File::Spec->catdir($repository_root, $repository_identifier);
my %actions = (
'create' => \&create_repository,
'delete' => \&delete_repository
);
my $action = $actions{$request->{action}};
die "Unknown action.\n" unless defined($action);
$action->($r, $vendor, $target);
return {
success => JSON::PP::true,
message => "The action has completed sucessfully.",
repository => $target
};
}
##
# Handler subroutine that is called for each request by Apache
sub handler {
my $r = shift;
my $response;
$r->content_type('application/json');
eval {
$response = _handle_request($r);
1;
} or do {
my $err = $@;
chomp $err;
$response = make_error($r, Apache2::Const::HTTP_BAD_REQUEST, $err);
};
print encode_json($response);
return Apache2::Const::OK;
}
1;

@ -110,7 +110,7 @@ Feature: User
| beta |
| gamma |
@javascript
@javascript @selenium
Scenario: re-adding a Member inside Admin Panel
When the user "peter" is a "alpha" in the project "project1"
And I go to the memberships tab of the edit page for the user peter

@ -41,7 +41,7 @@ Feature: Localized boolean custom fields can be created
When I go to the custom fields page
When I follow "New custom field" within "#tab-content-WorkPackageCustomField"
@javascript
@javascript @selenium
Scenario: Available fields
When I select "Boolean" from "custom_field_field_format"
Then there should be the following localizations:

@ -36,7 +36,7 @@ Feature: Localized list custom fields can be created
When I go to the custom fields page
When I follow "New custom field" within "#tab-content-WorkPackageCustomField"
@javascript
@javascript @selenium
Scenario: Creating a list custom field
When I select "List" from "custom_field_field_format"
And I set the english localization of the "name" attribute to "New Field"

@ -51,7 +51,7 @@ Feature: Editing text custom fields
| en | default | My Custom Field |
| de | Standard | My Custom Field |
@javascript
@javascript @selenium
Scenario: Changing a localization which is not present for any other attribute to a locale existing in another attribute deletes the localization completely
When the Custom Field called "My Custom Field" has the following localizations:
| locale | name | default_value |

@ -78,7 +78,7 @@ Feature: Issue edit
Given the user "bob" has 1 issue with the following:
| subject | child1 |
When I go to the edit page of the work package "issue1"
Then there should not be a "Progress (%)" field
Then there should not be a "Progress \(%\)" field
And there should be a disabled "Priority" field
And there should be a disabled "Start date" field
And there should be a disabled "Due date" field

@ -61,7 +61,7 @@ Feature: Tracking Time
Then I should see a time entry with 2 hours and comment "test"
And I should see a total spent time of 6 hours
@javascript
@javascript @selenium
Scenario: Editing a time entry
When I update the first time entry with 4 hours and the comment "updated test"
Then I should see a time entry with 4 hours and comment "updated test"

@ -53,7 +53,7 @@ Feature: Query menu items
| Features | Feature |
And I am already logged in as "bob"
@javascript
@javascript @selenium
Scenario: Create a query menu item
When I go to the applied query "Bugs" on the work packages index page of the project "Awesome Project"
And the work package table has finished loading
@ -64,7 +64,7 @@ Feature: Query menu items
And I click "Work packages" within "#main-menu"
Then I should see "Bugs" within "#main-menu"
@javascript
@javascript @selenium
Scenario: Delete a query menu item
Given the user "bob" has the following query menu items in the project "Awesome Project":
| name | title | navigatable |

@ -56,7 +56,7 @@ Feature: Wiki menu items
And I press "Save"
And I should see "Avocado Wuaärst" within "#main-menu"
@javascript
@javascript @selenium
Scenario: Adding a main menu entry with index and toc links
When I go to the wiki page "AwesomePage" for the project called "Awesome Project"
And I click on "More functions"
@ -71,7 +71,7 @@ Feature: Wiki menu items
Then I should see "Table of Contents" within "#main-menu"
Then I should see "Create new child page" within "#main-menu"
@javascript
@javascript @selenium
Scenario: Change existing entry
When I go to the wiki page "Wiki" for the project called "Awesome Project"
Then I should see "Table of Contents" within "#main-menu"
@ -115,7 +115,7 @@ Feature: Wiki menu items
When I go to the wiki page "Wiki" for the project called "Awesome Project"
Then I should see "Wiki" within ".menu-children"
@javascript
@javascript @selenium
Scenario: Removing a menu item
Given the project "Awesome Project" has a wiki menu item with the following:
| title | DontKillMe |
@ -127,7 +127,7 @@ Feature: Wiki menu items
And I press "Save"
Then I should not see "Wiki" within "#main-menu"
@javascript
@javascript @selenium
Scenario: When I delete the last wiki page with a menu item I can select a new menu item and the menu item is replaced
Given the project "Awesome Project" has a wiki menu item with the following:
| title | AwesomePage |

@ -1,62 +0,0 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
Feature: Project creation with support for project type
As a ChiliProject Admin
I want to set a project type when creating a project
So that the default planning element types are enabled automatically
Background:
Given there are the following types:
| Name | Is default |
| Phase | true |
| Milestone | false |
| Something else | false |
And there are the following project types:
| Name |
| Standard Project |
| Extraordinary Project |
Scenario: The admin may create a project with a project type
Given I am already admin
When I go to the admin page
And I follow the first link matching "Projects"
And I follow "New project"
Then I fill in "Fancy Pants" for "Name"
And I fill in "fancy-pants" for "Identifier"
And I check "Timelines"
And I select "Standard Project" from "Project type"
And I press "Create"
Then I should see a notice flash stating "Successful creation."
When the following types are enabled for projects of type "Standard Project"
| Phase |
| Milestone |
And I go to the "types" tab of the settings page of the project called "Fancy Pants"
Then the "Phase" checkbox should be checked
And the "Milestone" checkbox should be checked

@ -35,7 +35,7 @@ Feature: Navigating to reports page
| name | SubProject |
| identifier | parent_project_1_sub_1 |
@javascript
@javascript @selenium
Scenario: Archiving and unarchiving a project with a subproject
Given I am already admin
When I go to the projects admin page

@ -1,64 +0,0 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
Feature: Project Default Settings
Background:
Given I am already admin
Scenario: Setting project defaults
When I go to the projects tab of the settings page
And I uncheck "Activity"
And I uncheck "Wiki"
And I press "Save"
And I go to the projects admin page
And I follow "New project"
Then the "Wiki" checkbox should not be checked
And the "Activity" checkbox should not be checked
Scenario: Setting project defaults without activity
When I go to the projects tab of the settings page
And I uncheck "Activity"
And I check "Wiki"
And I check "Forums"
And I press "Save"
And I go to the projects admin page
And I follow "New project"
Then the "Wiki" checkbox should be checked
And the "Activity" checkbox should not be checked
And the "Forums" checkbox should be checked
Scenario: Creating a new project with default settings
When I go to the projects admin page
And I follow "New project"
And I fill in "project_name" with "A New Hope"
And I fill in "project_identifier" with "a-new-hope"
And I uncheck "Wiki"
And I press "Create"
And I go to the settings page of the project called "A New Hope"
And I click on "tab-modules"
Then the "Wiki" checkbox should not be checked

@ -31,7 +31,7 @@ Feature: Pagination
Given there are 15 work packages with "wurst" in their description
And I am admin
@javascript
@javascript @selenium
Scenario: The search result pages do not change while going back and forth
When I search globally for "wurst"
Then I should see "Results (15)"

@ -36,7 +36,7 @@ Feature: Searching
| wp1 |
And I am admin
@javascript
@javascript @selenium
Scenario: Searching stuff retains a project's scope
When I am on the overview page for the project called "test-project"
And I search globally for "stuff"

@ -60,7 +60,7 @@ require_relative 'paths.rb'
# steps to use the XPath syntax.
Capybara.configure do |config|
config.default_selector = :css
config.default_wait_time = 10
config.default_wait_time = 5
config.exact_options = true
config.ignore_hidden_elements = true
config.match = :one
@ -82,11 +82,24 @@ Capybara.register_driver :selenium do |app|
profile = Selenium::WebDriver::Firefox::Profile.new
profile['intl.accept_languages'] = 'en,en-us'
Capybara::Selenium::Driver.new(app,
browser: :firefox,
profile: profile)
Capybara::Selenium::Driver.new(app, browser: :firefox, profile: profile)
end
require 'capybara/poltergeist'
Capybara.register_driver :poltergeist do |app|
options = {
js_errors: false,
window_size: [1200, 1000],
timeout: 60
}
Capybara::Poltergeist::Driver.new(app, options)
end
# Disable using poltergeist until we upgraded jenkins workers
# Capybara.javascript_driver = :poltergeist
# Use selenium until we upgraded jenkins workers
Capybara.javascript_driver = :selenium
# By default, any exception happening in your Rails application will bubble up
# to Cucumber so that your scenario will fail. This is a different from how
# your application behaves in the production environment, where an error page will
@ -137,6 +150,12 @@ Before do |_scenario|
page.driver.browser.switch_to.alert.accept rescue Selenium::WebDriver::Error::NoAlertOpenError
end
Before do
if Capybara.current_driver == :poltergeist
page.driver.headers = { "Accept-Language" => "en" }
end
end
# Capybara.register_driver :selenium do |app|
# Capybara::Selenium::Driver.new(app, :browser => :chrome)
# end

@ -40,17 +40,13 @@ Feature: Timeline View Tests
| view_timelines |
| edit_timelines |
| edit_work_packages |
And there are the following types:
| Name | Is Milestone | In aggregation |
| Phase | false | true |
And there is a project named "ecookbook"
And the project "ecookbook" has the following types:
| name | position |
| Phase | 1 |
And I am working in project "ecookbook"
And the project uses the following modules:
| timelines |
@ -62,15 +58,13 @@ Feature: Timeline View Tests
| cfList | list | true | true | A,B,C |
| cfUser | user | true | true | |
| cfLocal | bool | false | true | |
And the custom field "cfBool" is activated for type "Phase"
And the custom field "cfList" is activated for type "Phase"
And the custom field "cfUser" is activated for type "Phase"
And the custom field "cfLocal" is activated for type "Phase"
And the custom field "cfLocal" is enabled for the project "ecookbook"
@javascript
@javascript @selenium
Scenario: Select custom field column
Given I am working in the timeline "Testline" of the project called "ecookbook"
When there is a timeline "Testline" for project "ecookbook"
@ -82,7 +76,7 @@ Feature: Timeline View Tests
And I should see the column "Start date" before the column "cfBool" in the timelines table
And I should see the column "cfBool" before the column "End date" in the timelines table
@javascript
@javascript @selenium
Scenario: Select custom field column and deactivate custom field
Given I am working in the timeline "Testline" of the project called "ecookbook"
When there is a timeline "Testline" for project "ecookbook"
@ -93,7 +87,7 @@ Feature: Timeline View Tests
And the custom field "cfLocal" is disabled for the project "ecookbook"
Then I should see the column "Start date" immediately before the column "End date" in the timelines table
@javascript
@javascript @selenium
Scenario: Show Boolean Custom Field Value
Given I am working in the timeline "Testline" of the project called "ecookbook"
And there are the following work packages in project "ecookbook":
@ -116,7 +110,7 @@ Feature: Timeline View Tests
And I should not see "No" in the row of the work package "booleanNone"
@javascript
@javascript @selenium
Scenario: Show Boolean Custom Field Value
Given I am working in the timeline "Testline" of the project called "ecookbook"
And there are the following work packages in project "ecookbook":

@ -172,7 +172,7 @@ Feature: Timeline View Tests with reporters
And I should see the project "ecookbook13"
And I should see the project "ecookbook0"
@javascript
@javascript @wip
Scenario: Second Level Grouping
When there is a timeline "Testline" for project "ecookbook"
And I set the first level grouping criteria to "ecookbook" for the timeline "Testline" of the project called "ecookbook"

@ -54,5 +54,6 @@ Feature: Timeline Work Package Show View
Scenario: planning element click should show the plannin element
When I go to the page of the timeline "Testline" of the project called "ecookbook"
And I wait for timeline to load table
And I click on the Planning Element with name "January"
And I should see "January"
When I click on the Planning Element with name "January"
Then I should see "January" in the new window

@ -27,25 +27,23 @@
#++
Feature: User Authentication
@javascript
Scenario: A user gets a error message if the false credentials are filled in
Given I am logged in as "joe"
Then I should see "Invalid user or password"
@javascript
Scenario: A user is able to login successfully with provided credentials
Given I am on the login page
And I am admin
Then I should see "Admin" within "#top-menu-items"
@javascript
Scenario: A user gets a error message if the false credentials are filled in
Given I am logged in as "joe"
Then I should see "Invalid user or password"
@javascript
Scenario: A user is able to login successfully with provided credentials
Given I am on the login page
And I am admin
Then I should see "Admin" within "#top-menu-items"
@javascript
Scenario: Lost password notification mail will not be sent in case incorrect mail is given
Given I am on the login page
And I open the "Openproject Admin" menu
And I follow "t:label_password_lost" within "#login-form" [i18n]
Then I should be on the lost password page
And I fill in "mail" with "bilbo@shire.com"
And I click on "Submit"
Then I should see "Unknown user"
@javascript
Scenario: Lost password notification mail will not be sent in case incorrect mail is given
Given I am on the login page
And I open the "Openproject Admin" menu
And I follow "t:label_password_lost" within "#login-form" [i18n]
Then I should be on the lost password page
And I fill in "mail" with "bilbo@shire.com"
And I click on "Submit"
Then I should see "Unknown user"

@ -44,7 +44,7 @@ Feature: Attachments on work packages
And there is 1 user with the following:
| login | bob|
And the user "bob" has the following preferences
| warn_on_leaving_unsaved | 0 |
| warn_on_leaving_unsaved | false |
And the user "bob" is a "member" in the project "parent"
And there are the following issue status:
| name | is_closed | is_default |

@ -83,7 +83,7 @@ Feature: Updating work packages
And I follow "pe1"
And I should see "deleted (version1)"
@javascript
@javascript @selenium
Scenario: Bulk updating several work packages without back url should return index
When I go to the work package index page of the project called "ecookbook"
And I open the context menu on the work packages:

@ -47,7 +47,7 @@ Feature: Deleting work packages
And there is a time entry for "wp1" with 10 hours
And I am already logged in as "manager"
@javascript
@javascript @selenium
Scenario: Deleting a work package via the action menu
When I go to the page of the work package "wp1"
And I select "Delete" from the action menu

@ -72,7 +72,7 @@ Feature: Disabled done ratio on the work package index
And I click "Apply"
Then I should see "Author" within ".work-package-table--container table"
@javascript
@javascript @wip
Scenario: Subject column should not be displayed when Subject is moved out of selected columns
When I go to the work packages index page of the project "project1"
And I choose "Columns" from the toolbar "settings" dropdown

@ -86,16 +86,7 @@ Feature: Copying a work package
And I select "project_2" from "Project"
When I click "Copy and follow"
Then I should see "Successful creation."
# FIXME: we currently do not have the project scope
# And I should see "project_2" within ".breadcrumb"
@javascript
Scenario: Move an issue
Given the "cross_project_work_package_relations" setting is set to true
When I go to the move page of the work package "issue1"
And I select "project_2" from "Project"
When I click "Move and follow"
Then I should see "Successful update."
Then I should see "issue1" within "#work-package-subject"
And I should see "project_2" within ".breadcrumb"
@javascript
@ -104,7 +95,7 @@ Feature: Copying a work package
When I go to the move page of the work package "issue1"
And I select "project_2" from "Project"
When I click "Move and follow"
Then I should see "Successful update."
#Then I should see "Successful update."
Then I should see "issue1" within "#work-package-subject"
And I should see "project_2" within ".breadcrumb"
@ -116,7 +107,7 @@ Feature: Copying a work package
Then I should see "Failed to save 1 work package(s) on 1 selected:"
@javascript
@javascript @selenium
Scenario: Going to the Copy Page of 2 Work Packages via bulk edit
When I go to the work package index page of the project called "project_1"
And I open the context menu on the work packages:

@ -45,7 +45,7 @@ Feature: Switching types of work packages
| lastname | Bobbit |
# prevent alerts to occur that would impede subsequent scenarios
And the user "bob" has the following preferences
| warn_on_leaving_unsaved | 0 |
| warn_on_leaving_unsaved | false |
And the user "bob" is a "member" in the project "project1"
And I am already logged in as "bob"

@ -178,7 +178,7 @@ Feature: Viewing a work package
When I select "Move" from the action menu
Then I should see "Move"
@javascript
@javascript @selenium
Scenario: For an issue deletion leads to the work package list
When I go to the page of the work package "issue1"
When I select "Delete" from the action menu

@ -47,6 +47,7 @@ module API
mount ::API::V3::StringObjects::StringObjectsAPI
mount ::API::V3::Types::TypesAPI
mount ::API::V3::Users::UsersAPI
mount ::API::V3::UserPreferences::UserPreferencesAPI
mount ::API::V3::Versions::VersionsAPI
mount ::API::V3::WorkPackages::WorkPackagesAPI

@ -38,6 +38,19 @@ module API
}
end
link :user do
{
href: api_v3_paths.user(current_user.id),
title: current_user.name
} if current_user.logged?
end
link :userPreferences do
{
href: api_v3_paths.my_preferences
} if current_user.logged?
end
link :priorities do
{
href: api_v3_paths.priorities

@ -26,37 +26,40 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require 'features/projects/projects_page'
require_dependency 'api/v3/user_preferences/user_preferences_representer'
describe 'Delete project', type: :feature, js: true do
let(:current_user) { FactoryGirl.create (:admin) }
let(:project) { FactoryGirl.create(:project) }
let(:projects_page) { ProjectsPage.new(project) }
module API
module V3
module UserPreferences
class UserPreferencesAPI < ::API::OpenProjectAPI
resource :my_preferences do
helpers do
def represent_preferences
UserPreferencesRepresenter.new(@preferences, current_user: current_user)
end
end
before do
allow(User).to receive(:current).and_return current_user
projects_page.visit_confirm_destroy
fail ::API::Errors::Unauthenticated unless current_user.logged?
@preferences = current_user.pref
end
describe 'disable delete w/o confirm' do
it { expect(page).to have_css('.danger-zone .button[disabled]') }
get do
represent_preferences
end
describe 'disable delete with wrong input' do
let(:input) { find('.danger-zone input') }
it do
input.set 'Not the project name'
expect(page).to have_css('.danger-zone .button[disabled]')
patch do
representer = represent_preferences
representer.from_hash(request_body)
if @preferences.save
representer
else
raise ::API::Errors::ErrorBase.create_and_merge_errors(@preferences.errors)
end
end
end
end
describe 'enable delete with correct input' do
let(:input) { find('.danger-zone input') }
it do
input.set project.name
expect(page).to have_css('.danger-zone .button:not([disabled])')
end
end
end

@ -1,3 +1,4 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
@ -26,33 +27,50 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
Feature: Creating Projects
Background:
Given there is 1 project with the following:
| name | parent |
| identifier | parent |
And I am already admin
@javascript
Scenario: Creating a Subproject
When I go to the settings page of the project "parent"
And I follow "New subproject"
And I fill in "project_name" with "child"
And I press "Create"
Then I should be on the settings page of the project called "child"
Scenario: Creating a Subproject
When I go to the settings page of the project "parent"
And I follow "New subproject"
Then I should not see "Responsible"
@javascript
Scenario: Creating a Project with an already existing identifier
When I go to the projects admin page
And I follow "New project"
And I fill in "project_name" with "Parent"
And I press "Create"
Then I should be on the projects page
And I should see "Identifier has already been taken"
And I fill in "project_name" with "Parent 2"
And the "Identifier" field should contain "parent-2" within "#content"
require 'roar/decorator'
require 'roar/json/hal'
module API
module V3
module UserPreferences
class UserPreferencesRepresenter < ::API::Decorators::Single
link :self do
{
href: api_v3_paths.my_preferences
}
end
link :user do
{
href: api_v3_paths.user(represented.user.id),
title: represented.user.name
}
end
link :updateImmediately do
{
href: api_v3_paths.my_preferences,
method: :patch
}
end
property :hide_mail
property :time_zone,
getter: -> (*) { canonical_time_zone },
render_nil: true
property :theme
property :warn_on_leaving_unsaved
property :comments_in_reverse_order,
as: :commentSortDescending
property :impaired?,
as: :accessibilityMode
def _type
'UserPreferences'
end
end
end
end
end

@ -84,6 +84,10 @@ module API
"#{work_packages_by_project(project_id)}/form"
end
def self.my_preferences
"#{root}/my_preferences"
end
def self.priorities
"#{root}/priorities"
end

@ -34,9 +34,6 @@ module API
module WorkPackages
module WorkPackagesSharedHelpers
extend Grape::API::Helpers
def request_body
env['api.request.body']
end
def merge_hash_into_work_package!(hash, work_package)
payload = ::API::V3::WorkPackages::WorkPackagePayloadRepresenter.create(work_package)

@ -58,10 +58,24 @@ module OpenProject
def managed_root
scm_config[:manages]
end
##
# Returns the managed remote for this repository vendor,
# if any. Use +manages_remote?+ to determine whether the configuration
# specifies local or remote managed repositories.
def managed_remote
URI.parse(scm_config[:manages])
rescue URI::Error
nil
end
##
#
# Returns whether the managed root is a remote URL to post to
def manages_remote?
managed_remote.present? && managed_remote.absolute?
end
end
def manageable?
self.class.manageable?
end

@ -1,123 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
desc 'Run the Continuous Integration tests for OpenProject'
task :ci do
# RAILS_ENV and ENV[] can diverge so force them both to test
ENV['RAILS_ENV'] = 'test'
RAILS_ENV = 'test'
Rake::Task['ci:setup'].invoke
Rake::Task['ci:build'].invoke
Rake::Task['ci:teardown'].invoke
end
# Tasks can be hooked into by redefining them in a plugin
namespace :ci do
namespace :travis do
desc 'Prepare a TRAVIS run'
task :prepare do
Rails.env = 'test'
ENV['RAILS_ENV'] = 'test'
RAILS_ENV = 'test'
db_adapter = ENV['DB']
raise 'please provide a db adapter with DB={mysql2, postgres}' unless db_adapter
db_info = {
'mysql2' => {
'adapter' => 'mysql2',
'username' => 'root'
},
'postgres' => {
'adapter' => 'postgresql',
'username' => 'postgres'
}
}[db_adapter]
database_yml = {
'database' => 'chiliproject_test'
}.merge(db_info)
File.open('config/database.yml', 'w') do |f|
YAML.dump({ 'test' => database_yml,
'development' => database_yml,
'production' => database_yml }, f)
end
# Create and migrate the database
Rake::Task['db:create'].invoke
# db:create invokes db:load_config. db:load_config collects migration paths, but the
# migration paths for plugins are set on the Engine config when the application
# is initialized, which the environment task does. The environment task is only later
# executed as dependency for db:migrate. db:migrate also depends on load_config, but since
# it has been executed before, rake doesn't execute it a second time.
# Loading the environment bevore explicitly executing db:load_config (not only invoking it)
# makes rake execute it a second time after the environment has been loaded.
# Loading the environment before db:create does not work, since initializing the application
# depends on an existing databse.
Rake::Task['environment'].invoke
Rake::Task['db:load_config'].execute
Rake::Task['db:migrate'].invoke
Rake::Task['db:schema:dump'].invoke
# Create test repositories
Rake::Task['test:scm:setup:all'].invoke
end
end
desc 'Setup OpenProject for a new build.'
task :setup do
Rake::Task['ci:dump_environment'].invoke
Rake::Task['db:drop'].invoke
Rake::Task['db:create'].invoke
Rake::Task['db:migrate'].invoke
Rake::Task['db:schema:dump'].invoke
Rake::Task['test:scm:update'].invoke
end
desc 'Build OpenProject'
task :build do
Rake::Task['test'].invoke
end
# Use this to cleanup after building or run post-build analysis.
desc 'Finish the build'
task :teardown do
end
desc 'Dump the environment information to a BUILD_ENVIRONMENT ENV variable for debugging'
task :dump_environment do
ENV['BUILD_ENVIRONMENT'] = ['ruby -v', 'gem -v', 'gem list'].collect { |command|
result = `#{command}`
"$ #{command}\n#{result}"
}.join("\n")
end
end

@ -0,0 +1,73 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
# script/ci_runner.sh
#!/bin/sh
set -e
# Usage:
# sh script/ci_runner.sh spec 3 1
#
# Use
# sh script/ci_runner.sh spec
# to make use of all available cores on the current machine. Most likely to
# be used on local dev machines.
#
# $1: type
# $2: group size
# $3: group number
run() {
echo $1;
eval $1;
echo $2;
eval $2;
echo $3;
eval $3;
}
if [ $2 != '' ] && [ $3 != '' ]
then
GROUPING=" -n $2 --only-group $3"
else
GROUPING=''
fi
if [ $1 = "npm" ]; then
run "npm test"
elif [ $1 = "legacy" ]; then
run "bundle exec parallel_test --type rspec -o '-I spec_legacy' spec_legacy $GROUPING"
elif [ $1 = "spec" ]; then
run "bundle exec parallel_test --type rspec --runtime-log script/files/parallel_runtime_rspec.log spec $GROUPING || \
bundle exec rspec --only-failures"
elif [ $1 = "cucumber" ]; then
run "bundle exec parallel_test --type cucumber -o '-p rerun -r features' --runtime-log script/files/parallel_runtime_cucumber.log features $GROUPING || \
bundle exec cucumber -p rerun -r features"
fi

@ -0,0 +1,53 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
# script/ci_setup.sh
#!/bin/sh
run() {
echo $1;
eval $1;
}
if [ $1 = "mysql" ]; then
run "mysql -e 'create database travis_ci_test;'"
run "cp script/templates/database.travis.mysql.yml config/database.yml"
elif [ $1 = "postgres" ]; then
run "psql -c 'create database travis_ci_test;' -U postgres"
run "cp script/templates/database.travis.postgres.yml config/database.yml"
fi
# run migrations for mysql or postgres
if [ $1 != '' ]; then
run "bundle exec rake db:migrate"
run "bundle exec rake spec:prepare"
run "bundle exec rake test:scm:setup:all"
fi

@ -0,0 +1,102 @@
features/admin/user.feature:21.254794597625732
features/custom_fields/create_bool.feature:11.824590921401978
features/custom_fields/edit_bool.feature:6.803788900375366
features/general_administration.feature:7.458944082260132
features/planning_elements/filter.feature:67.90526580810547
features/projects/show.feature:4.4210638999938965
features/users/password_complexity_checks.feature:9.30823802947998
features/versions/edit.feature:2.957937717437744
features/work_packages/changesets_on_show.feature:10.702651023864746
features/work_packages/copy_with_watchers.feature:12.6042799949646
features/work_packages/navigate_to_edit.feature:7.26867413520813
features/work_packages/switch_type.feature:4.98246693611145
features/activities/index.feature:3.113095998764038
features/custom_fields/edit_bool_delete_localizations.feature:0.6856679916381836
features/issues/time_entries.feature:29.522482872009277
features/projects/archive.feature:22.968013048171997
features/timelines/timeline_comparison_view.feature:9.385524988174438
features/timelines/timeline_view_with_reporters.feature:70.87646293640137
features/users/add_user.feature:3.314553737640381
features/users/force_password_change.feature:10.895864963531494
features/users/random_password_assignment.feature:11.002774000167847
features/wiki/parent_page.feature:5.9299211502075195
features/wiki/wiki_initial.feature:6.297181844711304
features/work_packages/diff_on_show.feature:0.9747560024261475
features/planning_element_type_colors/colors_administration.feature:21.823258876800537
features/project_types/project_types_administration.feature:25.556543827056885
features/projects/visibility.feature:4.468430042266846
features/reportings/reporting_permissions.feature:29.80443787574768
features/timelines/navigate_to_timeline.feature:8.427798986434937
features/timelines/show.feature:4.93776273727417
features/types/types_adminstration.feature:20.40826916694641
features/users/user_project_access.feature:7.243958950042725
features/wiki/wiki_rename.feature:3.488585948944092
features/work_packages/create.feature:1.4752600193023682
features/work_packages/log_time_on_update.feature:3.8278870582580566
features/work_packages/preview.feature:4.523224115371704
features/work_packages/work_package_textile_link.feature:10.034466743469238
features/custom_fields/create_date.feature:6.6774070262908936
features/custom_fields/create_text.feature:1.6597249507904053
features/issues/issue_edit.feature:24.95867085456848
features/issues/issue_show.feature:73.81519412994385
features/issues/show.feature:4.266723155975342
features/menu_items.feature:10.250272989273071
features/projects/copy_project.feature:37.00779914855957
features/projects/index_feed.feature:2.2133450508117676
features/users/lock_user.feature:1.115311861038208
features/wiki/wiki_add_edit.feature:16.09875988960266
features/wiki/wiki_index.feature:2.774636745452881
features/work_packages/editable_fields.feature:4.424482107162476
features/work_packages/index_groupings.feature:1.3538601398468018
features/custom_fields/create_int.feature:4.047360181808472
features/custom_fields/edit_text.feature:11.943168878555298
features/layout.feature:0.8569579124450684
features/projects/create.feature:15.350144147872925
features/roles/role_crud.feature:10.392412900924683
features/timelines/timeline_filter_custom_fields.feature:53.22785496711731
features/timelines/timeline_work_package_show_view.feature:7.8942320346832275
features/users/brute_force_prevention.feature:11.917443037033081
features/wiki/breadcrumb.feature:3.7399659156799316
features/work_packages/attachments.feature:5.713495969772339
features/work_packages/bulk.feature:58.23527383804321
features/work_packages/index_move_columns.feature:2.68430495262146
features/work_packages/moves/work_package_moves_new_copy.feature:13.66756010055542
features/custom_fields/create_float.feature:10.152086019515991
features/enumerations/administration.feature:2.9889941215515137
features/issues/issue_new.feature:3.173460006713867
features/issues/query.feature:1.7911748886108398
features/menu_items/query_menu_items.feature:28.428421020507812
features/messages/message.feature:21.245274782180786
features/planning_elements/show.feature:4.53304123878479
features/users/lost_password.feature:3.947967052459717
features/users/password_expiry.feature:10.588953256607056
features/work_packages/error_on_update.feature:6.589915037155151
features/work_packages/localized_log_time.feature:5.800209999084473
features/work_packages/update.feature:14.095234870910645
features/work_packages/work_package_show.feature:83.98213696479797
features/custom_fields/create_list.feature:13.42398476600647
features/menu_items/wiki_menu_items.feature:62.65936994552612
features/project_types/project_creation_with_type.feature:4.067986011505127
features/reportings/reporting_administration.feature:33.99033284187317
features/search/pagination.feature:16.74678325653076
features/timelines/timeline_wiki_macro.feature:8.089543104171753
features/users/former_passwords.feature:3.4739813804626465
features/users/status.feature:15.692255020141602
features/users/user_activation.feature:3.3909239768981934
features/users/user_auth.feature:18.107865810394287
features/users/user_registration.feature:3.107316017150879
features/wiki/wiki_create_child.feature:5.420360803604126
features/wiki/wiki_new_child.feature:3.489448308944702
features/issues/relations.feature:1.1860320568084717
features/projects/default_settings.feature:14.26863408088684
features/projects/settings.feature:2.4463469982147217
features/search/search.feature:23.503453969955444
features/session/user_session.feature:34.25952196121216
features/timelines/timeline_view.feature:26.201819896697998
features/timelines/timeline_view_custom_fields.feature:105.09295701980591
features/timelines/timeline_view_with_filters.feature:7.878263711929321
features/types/types.feature:1.200028896331787
features/wiki/wiki_links.feature:1.0075469017028809
features/work_packages/destroy.feature:7.24234414100647
features/work_packages/export.feature:0.5469858646392822
features/work_packages/reports.feature:1.3116469383239746

@ -0,0 +1,443 @@
spec/controllers/work_packages/bulk_controller_spec.rb:99.308038
spec/models/work_package/work_package_scheduling_spec.rb:3.720126
spec/lib/api/v3/formatter/txt_charset_spec.rb:0.021452
spec/permissions/copy_projects_spec.rb:0.92576
spec/models/work_package/work_package_custom_fields_spec.rb:5.968828
spec/lib/api/utilities/property_name_converter_spec.rb:1.646691
spec/requests/api/v3/versions/project_resource_spec.rb:8.200756
spec/models/project_type_spec.rb:0.300143
spec/helpers/work_packages_filter_helper_spec.rb:1.056052
spec/models/work_package/work_package_relations_spec.rb:14.233324
spec/features/categories/delete_spec.rb:3.767279
spec/lib/open_project/themes/theme_finder_spec.rb:0.416195
spec/controllers/api/v2/project_types_controller_spec.rb:0.505695
spec/views/api/v2/reportings/index_api_json_spec.rb:0.53145
spec/helpers/pagination_helper_spec.rb:0.550713
spec/lib/api/v3/work_packages/schema/typed_work_package_schema_spec.rb:0.138517
spec/models/planning_element_type_color_spec.rb:0.11658
spec/lib/api/v3/utilities/custom_field_injector_spec.rb:2.369
spec/models/mail_handler_spec.rb:0.571408
spec/views/api/v2/project_associations/available_projects_api_json_spec.rb:0.475569
spec/controllers/homescreen_controller_spec.rb:3.045273
spec/lib/api/v3/projects/project_collection_representer_spec.rb:0.689504
spec/views/api/v2/authentication/index_api_json_spec.rb:0.060686
spec/models/timeline_spec.rb:0.103997
spec/features/menu_items/admin_menu_item_spec.rb:0.342411
spec/models/changeset_spec.rb:3.163388
spec/lib/api/v3/users/user_representer_spec.rb:0.569778
spec/requests/api/v3/types/types_by_project_resource_spec.rb:6.694846
spec/views/api/v2/projects/show_api_json_spec.rb:3.404146
spec/controllers/custom_fields_controller_spec.rb:0.987382
spec/routing/admin_spec.rb:0.004387
spec/helpers/timelines_helper_spec.rb:0.159103
spec/routing/categories_spec.rb:0.019904
spec/views/account/login.html.erb_spec.rb:0.292788
spec/models/project/storage_spec.rb:8.477173
spec/models/menu_item_spec.rb:0.1732
spec/controllers/api/v2/planning_element_type_colors_controller_spec.rb:0.723309
spec/controllers/api/v2/pagination/statuses_controller_spec.rb:0.040493
spec/models/custom_value/version_strategy_spec.rb:0.314755
spec/controllers/api/v2/project_associations_controller_spec.rb:4.544713
spec/controllers/statuses_controller_spec.rb:3.491003
spec/lib/open_project/plugins/module_handler_spec.rb:0.002694
spec/controllers/project_types_controller_spec.rb:1.1399
spec/controllers/authentication_spec.rb:0.082939
spec/models/reporting_spec.rb:1.123389
spec/controllers/users_controller_spec.rb:6.150856
spec/workers/mail_notification_jobs/deliver_watcher_notification_job_spec.rb:1.139756
spec/views/api/v2/project_types/index_api_json_spec.rb:0.027065
spec/models/work_package/work_package_action_mailer_spec.rb:3.539816
spec/controllers/reportings_controller_spec.rb:6.368339
spec/permissions/work_packages_bulk_spec.rb:0.590634
spec/lib/open_project/footer_spec.rb:0.009343
spec/requests/api/v3/priority_resource_spec.rb:3.395324
spec/lib/acts_as_list/acts_as_list_patch_spec.rb:0.016513
spec/models/repository/subversion_spec.rb:10.255372
spec/lib/open_project/content_type_detector_spec.rb:0.055394
spec/routing/previews_routing_spec.rb:0.21598
spec/lib/open_project/client_preference_extractor_spec.rb:0.0464
spec/lib/open_project/omni_auth/authorization_spec.rb:0.056375
spec/models/journal/aggregated_journal_spec.rb:15.18938
spec/views/projects/settings.html.erb_spec.rb:0.394454
spec/models/workflow_spec.rb:0.7356
spec/features/members/roles_spec.rb:28.947552
spec/controllers/members_controller_spec.rb:3.175401
spec/requests/api/v3/status_resource_spec.rb:5.120934
spec/views/api/experimental/versions/index_api_json_spec.rb:0.392903
spec/controllers/my_controller_spec.rb:2.251532
spec/models/work_package/work_package_planning_spec.rb:12.312333
spec/views/search/index.html.erb_spec.rb:0.6206
spec/requests/short_uri_wp_spec.rb:0.00947
spec/lib/open_project/scm/adapters/git_adapter_spec.rb:1.700146
spec/views/api/v2/authentication/index_api_xml_spec.rb:0.050683
spec/lib/api/v3/users/user_collection_representer_spec.rb:0.231104
spec/controllers/work_packages_controller_spec.rb:24.41532
spec/services/add_attachment_service_spec.rb:2.933369
spec/lib/api/v3/statuses/status_collection_representer_spec.rb:0.180904
spec/controllers/api/v2/workflows_controller_spec.rb:6.83835
spec/models/work_package/work_package_validations_spec.rb:7.127172
spec/controllers/api/v2/users_controller_spec.rb:1.534868
spec/controllers/api/v2/versions_controller_spec.rb:7.633275
spec/requests/api/v3/projects/available_responsibles_api_spec.rb:7.744719
spec/views/work_package/show.html.erb_spec.rb:1.528613
spec/helpers/application_helper_spec.rb:3.370952
spec/lib/api/v3/queries/query_representer_spec.rb:0.707311
spec/controllers/project_associations_controller_spec.rb:5.466559
spec/features/workflows/copy_spec.rb:0.310549
spec/lib/api/v3/work_packages/work_package_payload_representer_spec.rb:8.894541
spec/routing/work_package/calendars_routing_spec.rb:0.006696
spec/requests/api/v3/authentication_spec.rb:3.073634
spec/views/api/experimental/queries/available_columns_api_json_spec.rb:0.152834
spec/app/services/scm/create_managed_repository_service_spec.rb:0.562465
spec/routing/my_spec.rb:0.067017
spec/features/members/error_messages_spec.rb:63.349423
spec/lib/open_project/authentication/strategies/warden/global_basic_auth_spec.rb:0.032612
spec/controllers/api/v2/statuses_controller_spec.rb:0.137511
spec/permissions/manage_boards_spec.rb:0.791393
spec/policies/redirect_policy_spec.rb:0.066476
spec/permissions/export_work_packages_spec.rb:0.619033
spec/routing/wiki_routing_spec.rb:0.020105
spec/views/repositories/stats.html.erb_spec.rb:0.241457
spec/controllers/wiki_menu_items_controller_spec.rb:1.862367
spec/routing/search_spec.rb:0.003618
spec/features/users/edit_users_spec.rb:16.242113
spec/models/repository/git_spec.rb:33.339239
spec/models/enabled_module_spec.rb:0.754045
spec/controllers/copy_projects_controller_spec.rb:4.520659
spec/permissions/edit_messages_spec.rb:0.328164
spec/helpers/projects_helper_spec.rb:1.338794
spec/controllers/journals_controller_spec.rb:2.56735
spec/views/users/edit.html.erb_spec.rb:1.837561
spec/requests/api/v3/activity_resource_spec.rb:12.670909
spec/views/settings/_authentication.html.erb_spec.rb:0.255905
spec/controllers/auth_sources_controller_spec.rb:1.652721
spec/controllers/api/v2/pagination/reported_project_statuses_controller_spec.rb:0.055202
spec/permissions/edit_work_packages_spec.rb:1.997864
spec/lib/api/v3/work_packages/schema/specific_work_package_schema_spec.rb:1.455565
spec/lib/api/v3/activities/activity_representer_spec.rb:7.697857
spec/controllers/api/experimental/concerns/query_loading_spec.rb:0.26306
spec/features/menu_items/top_menu_item_spec.rb:33.061262
spec/lib/open_project/notifications_spec.rb:0.012448
spec/views/layouts/base.html.erb_spec.rb:1.273679
spec/routing/workflows_spec.rb:0.032106
spec/models/work_package/work_package_acts_as_searchable_spec.rb:0.990979
spec/lib/api/v3/types/type_representer_spec.rb:0.584216
spec/features/auth/omniauth_spec.rb:4.438837
spec/lib/api/v3/versions/version_representer_spec.rb:1.883125
spec/lib/api/v3/work_packages/schema/work_package_schema_representer_spec.rb:75.732318
spec/controllers/repositories_controller_spec.rb:3.984208
spec/views/users/show.html.erb_spec.rb:0.458233
spec/models/menu_items/wiki_menu_item_spec.rb:1.204603
spec/models/custom_field_spec.rb:0.798909
spec/views/api/v2/planning_elements/index_api_json_spec.rb:2.246778
spec/requests/api/v3/types/type_resource_spec.rb:4.635973
spec/models/work_package/work_package_status_spec.rb:4.612853
spec/requests/api/v3/work_packages/work_packages_by_project_resource_spec.rb:27.94817
spec/security/active_support_to_json_spec.rb:0.002711
spec/services/set_localization_service_spec.rb:0.085571
spec/permissions/add_messages_spec.rb:0.499467
spec/views/api/v2/reported_project_statuses/show_api_json_spec.rb:0.143796
spec/features/time_entry/csv_export_spec.rb:3.218654
spec/requests/api/v3/watcher_resource_spec.rb:42.718018
spec/helpers/previews_helper_spec.rb:0.007329
spec/routing/homescreen_spec.rb:0.003158
spec/routing/users_routing_spec.rb:0.027318
spec/views/common/validation_error.html.erb_spec.rb:0.024872
spec/controllers/wiki_controller_spec.rb:32.398348
spec/models/news_spec.rb:5.584859
spec/lib/open_project/themes/default_theme_spec.rb:0.060132
spec/controllers/api/v2/reportings_controller_spec.rb:4.071956
spec/lib/custom_field_form_builder_spec.rb:0.758605
spec/views/api/v2/users/index_api_json_spec.rb:0.036961
spec/workers/mail_notification_jobs/deliver_work_package_notification_job_spec.rb:5.112851
spec/models/custom_value/int_strategy_spec.rb:0.020063
spec/models/custom_value/user_strategy_spec.rb:0.047633
spec/routing/api/v2/planning_element_priorities_routing_spec.rb:0.003882
spec/models/principal_spec.rb:2.722756
spec/lib/open_project/principal_allowance_evaluator/default_spec.rb:0.094879
spec/models/work_package/work_package_acts_as_watchable_spec.rb:3.010864
spec/models/query_spec.rb:0.882284
spec/lib/open_project/before_delayed_job_spec.rb:0.004921
spec/helpers/settings_helper_spec.rb:0.449173
spec/views/api/v2/reported_project_statuses/index_api_json_spec.rb:0.036946
spec/views/api/v2/statuses/index_api_json_spec.rb:0.03264
spec/views/layouts/admin.html.erb_spec.rb:0.341824
spec/models/custom_value_spec.rb:0.223067
spec/models/custom_value/format_strategy_spec.rb:0.011586
spec/controllers/api/v2/planning_elements_controller_spec.rb:42.751425
spec/routing/api/v1_spec.rb:0.33479
spec/controllers/api/v2/projects_controller_spec.rb:2.580679
spec/models/work_package/work_package_visibility_spec.rb:2.492948
spec/lib/api/v3/repositories/revision_representer_spec.rb:0.956074
spec/lib/acts_as_watchable/lib/acts_as_watchable/routes_spec.rb:0.022558
spec/models/work_package/work_package_reschedule_after_spec.rb:21.603916
spec/controllers/search_controller_spec.rb:21.447626
spec/controllers/api/v2/pagination/projects_controller_spec.rb:0.091845
spec/requests/api/v3/work_packages/create_form_resource_spec.rb:1.912547
spec/features/menu_items/query_menu_item_spec.rb:19.485524
spec/models/group_spec.rb:1.439552
spec/models/messages_spec.rb:2.010218
spec/requests/api/v3/activities_by_work_package_resource_spec.rb:3.774064
spec/models/work_package/planning_comparison_spec.rb:4.443698
spec/lib/redcloth/redcloth_spec.rb:0.020554
spec/views/api/experimental/work_packages/column_sums_api_json_spec.rb:0.045603
spec/controllers/projects_controller_spec.rb:14.477271
spec/models/work_package_spec.rb:68.809559
spec/views/api/v2/planning_element_type_colors/show_api_json_spec.rb:0.050447
spec/lib/acts_as_journalized/journaled_spec.rb:4.595938
spec/models/user_password_spec.rb:0.111366
spec/models/project/copy_spec.rb:12.506086
spec/lib/api/v3/work_packages/work_package_collection_representer_spec.rb:59.994742
spec/views/api/v2/statuses/show_api_json_spec.rb:0.028202
spec/views/api/v2/planning_element_types/index_api_json_spec.rb:0.028041
spec/controllers/timelog_controller_spec.rb:2.847921
spec/helpers/sort_helper_spec.rb:0.026655
spec/requests/api/v3/attachments/attachments_by_work_package_resource_spec.rb:32.124922
spec/controllers/timelines_controller_spec.rb:10.412259
spec/lib/open_project/scm/manager_spec.rb:0.006536
spec/models/user_preference_spec.rb:0.009078
spec/models/version_spec.rb:5.950683
spec/models/attachment_spec.rb:3.367901
spec/models/project_spec.rb:4.151617
spec/views/api/v2/custom_fields/index_api_rabl_spec.rb:0.116459
spec/models/custom_value/float_strategy_spec.rb:0.020288
spec/models/menu_items/query_menu_item_spec.rb:0.131244
spec/lib/api/v3/priorities/priority_collection_representer_spec.rb:0.132981
spec/models/work_package/ask_before_destruction_spec.rb:13.409882
spec/routing/settings_spec.rb:0.015308
spec/views/api/v2/reportings/show_api_json_spec.rb:0.528269
spec/requests/api/v3/activities_api_spec.rb:3.134775
spec/controllers/api/experimental/groups_controller_spec.rb:1.187537
spec/features/groups/groups_spec.rb:0.227934
spec/lib/open_project/passwords_spec.rb:0.11591800000000001
spec/decorators/api/experimental/work_package_decorator_spec.rb:0.144356
spec/controllers/api/v2/pagination/principals_controller_spec.rb:0.027171
spec/views/api/experimental/queries/grouped_api_json_spec.rb:0.075023
spec/features/users/create_spec.rb:21.664397
spec/controllers/application_controller_spec.rb:0.429966
spec/lib/representable_spec.rb:0.007485
spec/lib/journal_formatter/custom_field_spec.rb:0.20238
spec/controllers/api/experimental/queries_controller_spec.rb:5.389787
spec/controllers/api/experimental/users_controller_spec.rb:0.567282
spec/controllers/api/experimental/work_packages_controller_spec.rb:11.569858
spec/models/role_spec.rb:0.015243
spec/controllers/types_controller_spec.rb:9.081033
spec/lib/open_project/scm/adapters/subversion_adapter_spec.rb:1.102798
spec/models/wiki_spec.rb:2.127099
spec/controllers/news_controller_spec.rb:4.474249
spec/views/api/v2/versions/index_api_json_spec.rb:0.29338
spec/lib/redmine/access_control_spec.rb:0.017074
spec/views/my/page.html.erb_spec.rb:2.146018
spec/lib/api/v3/categories/category_collection_representer_spec.rb:2.39424
spec/requests/api/v3/projects/version_resource_spec.rb:7.604758
spec/lib/open_project/files_spec.rb:0.053701
spec/requests/api/v3/user_resource_spec.rb:3.312744
spec/routing/status_routing_spec.rb:0.035108
spec/features/accessibility/work_packages/work_package_query_spec.rb:164.053308
spec/features/members/pagination_spec.rb:56.184391
spec/requests/api/v3/user/userlock_resource_spec.rb:0.866135
spec/models/queries/work_packages/filter_spec.rb:0.056821
spec/views/api/experimental/projects/index_api_json_spec.rb:0.547933
spec/controllers/settings_controller_spec.rb:4.897906
spec/lib/api/decorators/formattable_spec.rb:0.047913
spec/controllers/api/v2/planning_element_priorities_controller_spec.rb:0.461471
spec/controllers/activities_controller_spec.rb:4.941392
spec/models/wiki_page_spec.rb:1.968979
spec/requests/api/v3/query_resource_spec.rb:11.706804
spec/lib/open_project/plugins/acts_as_op_engine_spec.rb:0.017704
spec/features/attachments/attachments_spec.rb:8.27895
spec/controllers/concerns/omniauth_login_spec.rb:1.312297
spec/routing/work_package_bulk_spec.rb:0.009306
spec/controllers/attachments_controller_spec.rb:3.478276
spec/lib/redmine/unified_diff_spec.rb:0.007393
spec/requests/api/v3/projects/available_assignees_api_spec.rb:6.033197
spec/routing/journals_spec.rb:0.009339
spec/permissions/delete_work_packages_spec.rb:0.306033
spec/permissions/manage_repositories_spec.rb:0.368767
spec/lib/api/v3/attachments/attachment_representer_spec.rb:9.434732
spec/controllers/api/experimental/versions_controller_spec.rb:2.10154
spec/controllers/api/v2/pagination/project_types_controller_spec.rb:0.044854
spec/requests/api/v3/version_resource_spec.rb:2.232249
spec/permissions/add_work_packages_spec.rb:1.325141
spec/lib/api/v3/attachments/attachment_metadata_representer_spec.rb:0.030393
spec/models/permitted_params_spec.rb:0.50866
spec/helpers/work_packages_helper_spec.rb:1.749106
spec/features/work_packages/details/inplace_editor/subject_editor_spec.rb:103.179282
spec/views/api/experimental/projects/show_api_json_spec.rb:0.299414
spec/views/api/v2/project_associations/show_api_json_spec.rb:0.293076
spec/lib/journal_formatter/attachment_spec.rb:1.002066
spec/app/services/scm/repository_factory_service_spec.rb:0.166047
spec/controllers/api/v2/custom_fields_controller_spec.rb:6.572072
spec/lib/api/v3/priorities/priority_representer_spec.rb:0.057706
spec/routing/repositories_routing_spec.rb:0.111144
spec/lib/api/utilities/resource_link_parser_spec.rb:0.052445
spec/models/system_user_spec.rb:0.39638
spec/routing/work_package/auto_completes_routing_spec.rb:0.004203
spec/controllers/api/experimental/projects_controller_spec.rb:0.979335
spec/routing/work_packages_spec.rb:0.087341
spec/models/journal_manager_spec.rb:3.200829
spec/controllers/api/v2/planning_element_types_controller_spec.rb:5.543501
spec/features/auth/login_spec.rb:2.611287
spec/helpers/search_helper_spec.rb:0.796013
spec/routing/api/v2/pagination_spec.rb:0.029344
spec/helpers/users_helper_spec.rb:0.094999
spec/services/move_work_package_service_spec.rb:32.159455
spec/features/repositories/repository_settings_spec.rb:50.164637
spec/models/work_package/work_package_nested_set_spec.rb:33.199842
spec/lib/api/v3/work_packages/update_contract_spec.rb:3.181564
spec/models/custom_value/date_strategy_spec.rb:0.022117
spec/features/accessibility/work_packages/calendar_toggable_fieldsets_spec.rb:18.669838
spec/lib/open_project/text_formatting_spec.rb:27.353966
spec/models/available_project_status_spec.rb:0.287849
spec/mailers/user_mailer_spec.rb:16.599687
spec/permissions/edit_journals_spec.rb:0.882207
spec/app/services/scm/checkout_instructions_service_spec.rb:0.493521
spec/controllers/api/v2/planning_element_journals_controller_spec.rb:1.461652
spec/permissions/manage_news_spec.rb:0.540332
spec/features/boards/message_spec.rb:6.443044
spec/views/api/experimental/work_packages/column_data_api_json_spec.rb:0.044956
spec/routing/project_routing_spec.rb:0.113593
spec/routing/api/v2/workflows_spec.rb:0.004452
spec/models/member_spec.rb:3.393925
spec/lib/api/v3/configuration/configuration_representer_spec.rb:0.074182
spec/features/api/authentication_spec.rb:0.664103
spec/lib/api/decorators/link_object_spec.rb:0.03981
spec/requests/api/v3/work_packages/form/work_package_form_resource_spec.rb:192.160083
spec/routing/roles_spec.rb:0.004646
spec/views/api/v2/planning_element_type_colors/index_api_json_spec.rb:0.025634
spec/views/work_package/auto_complete/index_spec.rb:0.12725
spec/controllers/news/comments_controller_spec.rb:0.817986
spec/models/custom_value/list_strategy_spec.rb:0.03351
spec/lib/copy_model/copy_model_spec.rb:0.121108
spec/lib/api/v3/work_packages/work_packages_shared_helpers_spec.rb:1.850368
spec/requests/api/v3/repositories/revisions_by_work_package_resource_spec.rb:7.117097
spec/controllers/boards_controller_spec.rb:2.179682
spec/controllers/ldap_auth_sources_controller_spec.rb:0.18973
spec/helpers/toolbar_helper_spec.rb:0.013764
spec/lib/api/v3/categories/category_representer_spec.rb:1.591553
spec/views/api/v2/projects/index_api_json_spec.rb:0.129094
spec/views/api/v2/project_associations/index_api_json_spec.rb:0.531214
spec/app/services/scm/delete_managed_repository_service_spec.rb:0.630804
spec/services/create_work_package_service_spec.rb:1.011436
spec/routing/timelog_spec.rb:0.003412
spec/controllers/work_packages/calendars_controller_spec.rb:6.69359
spec/lib/open_project/themes/theme_spec.rb:0.066844
spec/decorators/single_spec.rb:0.227688
spec/models/custom_value/bool_strategy_spec.rb:0.025016
spec/controllers/work_packages/reports_controller_spec.rb:41.046944
spec/policies/query_policy_spec.rb:0.366002
spec/features/projects/delete_projects_spec.rb:8.7933
spec/models/timelines_project_spec.rb:2.028941
spec/views/api/v2/planning_elements/show_api_json_spec.rb:3.894438
spec/views/api/v2/planning_elements/index_api_xml_spec.rb:0.276822
spec/controllers/api/v2/pagination/users_controller_spec.rb:0.034969
spec/requests/api/v3/root_resource_spec.rb:2.349805
spec/requests/api/v3/locale_spec.rb:0.545431
spec/views/api/v2/projects/show_api_xml_spec.rb:0.079676
spec/models/custom_value/string_strategy_spec.rb:0.013353
spec/views/api/experimental/groups/index_api_json_spec.rb:0.056683
spec/permissions/view_work_packages_spec.rb:0.644323
spec/features/accessibility/custom_fields_spec.rb:41.382265
spec/lib/api/contracts/model_contract_spec.rb:0.05214
spec/controllers/api/experimental/concerns/column_data_spec.rb:0.034266
spec/requests/api/v3/projects/work_package_columns_resource_spec.rb:0.787533
spec/controllers/categories_controller_spec.rb:9.718838
spec/lib/open_project/configuration_spec.rb:0.051237
spec/views/api/experimental/work_packages/index_api_json_spec.rb:3.056188
spec/features/work_packages/saving_queries_spec.rb:0.001013
spec/lib/api/v3/projects/project_representer_spec.rb:4.510719
spec/controllers/wiki_menu_authentication_spec.rb:0.527026
spec/permissions/edit_wiki_pages_spec.rb:0.530419
spec/models/deleted_user_spec.rb:0.389781
spec/models/reports_services_spec.rb:0.115777
spec/controllers/api/v2/authentication_spec.rb:0.555701
spec/requests/api/v3/project_resource_spec.rb:4.325029
spec/models/project_association_spec.rb:1.723865
spec/controllers/api/v2/reported_project_statuses_controller_spec.rb:1.585815
spec/models/reported_project_status_spec.rb:0.363101
spec/views/api/v2/planning_element_priorities/index_api_xml_spec.rb:0.255284
spec/views/account/register.html.erb_spec.rb:0.315218
spec/models/user_deletion_spec.rb:73.864086
spec/features/work_packages/table_sorting_spec.rb:8.103008
spec/requests/old_issue_2_wp_spec.rb:0.011256
spec/controllers/api/experimental/roles_controller_spec.rb:0.63792
spec/policies/work_package_policy_spec.rb:0.182723
spec/requests/api/v3/category_resource_spec.rb:10.018845
spec/models/setting_spec.rb:1.432976
spec/lib/open_project/form_tag_helper_spec.rb:0.436246
spec/requests/api/v3/repositories/revisions_resource_spec.rb:2.627811
spec/routing/api/experimental/versions_spec.rb:0.006777
spec/views/wiki/new.html.erb_spec.rb:0.159979
spec/lib/open_project/storage_spec.rb:0.025681
spec/controllers/work_packages/moves_controller_spec.rb:29.931878
spec/routing/api/v2/project_specs_spec.rb:0.009884
spec/lib/api/v3/root_representer_spec.rb:0.26333
spec/routing/watchers_spec.rb:0.067911
spec/features/groups/membership_spec.rb:48.76408
spec/permissions/edit_own_messages_spec.rb:0.431157
spec/controllers/sys_controller_spec.rb:12.457441
spec/lib/redmine/i18n_spec.rb:0.343746
spec/lib/api/v3/work_packages/work_package_representer_spec.rb:76.962801
spec/features/work_packages/select_work_package_row_spec.rb:107.196845
spec/requests/api/v3/render_resource_spec.rb:8.360196
spec/routing/work_package/reports_routing_spec.rb:0.009855
spec/requests/api/v3/attachments/attachment_resource_spec.rb:4.41174
spec/models/query_column_spec.rb:0.017347
spec/models/query/results_spec.rb:5.54435
spec/models/user/authorization_spec.rb:0.939339
spec/controllers/work_packages/auto_completes_controller_spec.rb:15.012008
spec/lib/open_project/themes_spec.rb:0.121362
spec/models/work_package/work_package_acts_as_event_spec.rb:0.206108
spec/lib/api/v3/versions/version_collection_representer_spec.rb:1.905595
spec/lib/api/v3/statuses/status_representer_spec.rb:0.112973
spec/workers/mail_notification_jobs/enqueue_work_package_notification_job_spec.rb:6.465057
spec/controllers/work_packages/creation_spec.rb:1.479588
spec/helpers/avatar_helper_spec.rb:0.393939
spec/lib/api/v3/work_packages/base_contract_spec.rb:11.222081
spec/features/work_packages/select_query_spec.rb:25.175175
spec/features/repositories/create_repository_spec.rb:68.139086
spec/lib/api/v3/string_objects/string_object_representer_spec.rb:0.039102
spec/controllers/account_controller_spec.rb:3.577881
spec/features/work_packages/work_package_index_spec.rb:6.993247
spec/models/journal_notification_mailer_spec.rb:10.190019
spec/controllers/planning_element_type_colors_controller_spec.rb:0.9373
spec/lib/api/v3/utilities/path_helper_spec.rb:0.411127
spec/lib/journal_formatter/diff_spec.rb:0.096114
spec/views/api/v2/project_types/show_api_json_spec.rb:0.047298
spec/features/work_packages/details/inplace_editor/description_editor_spec.rb:128.490802
spec/models/work_package/work_package_notifications_spec.rb:1.457767
spec/models/board_spec.rb:1.466149
spec/controllers/messages_controller_spec.rb:5.821735
spec/lib/tabular_form_builder_spec.rb:1.571655
spec/views/api/v2/workflows/index_api_xml_spec.rb:0.131217
spec/routing/boards_routing_spec.rb:0.008439
spec/workers/scm/create_repository_job_spec.rb:0.517115
spec/lib/api/v3/work_packages/form_representer_spec.rb:2.02016
spec/controllers/api/v2/pagination/types_controller_spec.rb:0.052796
spec/lib/deprecated_alias_spec.rb:0.003059
spec/lib/api/v3/work_packages/create_form_representer_spec.rb:3.903996
spec/lib/api/v3/work_packages/update_form_representer_spec.rb:3.90014
spec/features/work_packages/details/activity_comments_spec.rb:92.738631
spec/helpers/custom_fields_helper_spec.rb:0.017219
spec/routing/types_spec.rb:0.004806
spec/features/groups/group_memberships_spec.rb:17.026232
spec/models/user_spec.rb:2.094293
spec/views/api/v2/planning_elements/destroy_api_json_spec.rb:0.181406
spec/routing/account_spec.rb:0.01156
spec/models/copy_project_job_spec.rb:1.894091
spec/lib/api/v3/utilities/resource_link_generator_spec.rb:0.087959
spec/requests/api/v3/work_package_resource_spec.rb:134.785301
spec/lib/open_project/configuration/helpers_spec.rb:0.010972
spec/requests/api/v3/string_objects_resource_spec.rb:0.147816
spec/requests/auth/api_v2_spec.rb:0.963186
spec/views/api/experimental/roles/index_api_json_spec.rb:0.029343
spec/lib/api/v3/utilities/date_time_formatter_spec.rb:0.035731
spec/models/work_package/work_package_acts_as_journalized_spec.rb:8.644679
spec/views/api/v2/projects/level_list_api_json_spec.rb:0.10303
spec/lib/open_project/themes/view_helpers_spec.rb:0.049764
spec/views/api/v2/planning_element_types/show_api_json_spec.rb:0.018308
spec/models/activity/work_package_activity_provider_spec.rb:4.356187
spec/features/members/membership_spec.rb:121.733605
spec/lib/open_project/file_command_content_type_detector_spec.rb:0.017398
spec/controllers/versions_controller_spec.rb:4.891807
spec/requests/api/v3/work_packages/work_packages_schemas_resource_spec.rb:1.526052

@ -0,0 +1,33 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
test:
adapter: mysql2
database: travis_ci_test
username: travis
encoding: utf8

@ -0,0 +1,32 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
test:
adapter: postgresql
database: travis_ci_test
username: postgres

@ -31,7 +31,7 @@ require 'features/custom_fields/custom_fields_page'
require 'features/projects/project_settings_page'
require 'features/work_packages/work_packages_page'
RSpec.describe 'Custom field accessibility', type: :feature do
RSpec.describe 'Custom field accessibility', type: :feature, selenium: true do
describe 'language tag' do
let(:custom_field) {
FactoryGirl.create(:work_package_custom_field,
@ -176,7 +176,7 @@ RSpec.describe 'Custom field accessibility', type: :feature do
end
context 'de' do
let(:locale) { 'en' }
let(:locale) { 'de' }
include_context 'project settings page'

@ -29,7 +29,7 @@
require 'spec_helper'
require 'features/work_packages/work_packages_page'
describe 'Work package index accessibility', type: :feature do
describe 'Work package index accessibility', type: :feature, selenium: true do
let(:user) { FactoryGirl.create(:admin) }
let(:project) { FactoryGirl.create(:project) }
let(:work_package) { FactoryGirl.create(:work_package, project: project) }
@ -248,9 +248,6 @@ describe 'Work package index accessibility', type: :feature do
describe 'context menus' do
before do
window = Capybara.current_session.driver.browser.manage.window
window.maximize
visit_index_page
end

@ -62,16 +62,12 @@ feature 'Query menu items' do
context 'with dots in their name' do
let(:query) { FactoryGirl.create :public_query, name: 'OP 3.0', project: project }
def check(input_name)
find(:css, "input[name=#{input_name}]").set true
end
it 'can be added', js: true do
it 'can be added', js: true, selenium: true do
visit_index_page(query)
click_on 'Settings'
click_on 'Share ...'
check 'show_in_menu'
check 'Show page in menu'
click_on 'Save'
notification.expect_success('Successful update')

@ -28,7 +28,7 @@
require 'spec_helper'
feature 'Top menu items', js: true do
feature 'Top menu items', js: true, selenium: true do
let(:user) { FactoryGirl.create :user }
let(:modules) { find(:css, "[title=#{I18n.t('label_modules')}]") }

@ -0,0 +1,126 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require 'features/projects/projects_page'
describe 'Projects', type: :feature do
let(:current_user) { FactoryGirl.create(:admin) }
before do
allow(User).to receive(:current).and_return current_user
end
describe 'creation', js: true do
let!(:project) { FactoryGirl.create(:project, name: 'Foo project', identifier: 'foo-project') }
before do
visit admin_path
end
it 'can create a project' do
click_on 'New project'
fill_in 'project[name]', with: 'Foo bar'
click_on 'Advanced settings'
fill_in 'project[identifier]', with: 'foo'
click_on 'Create'
expect(page).to have_content 'Successful creation.'
expect(page).to have_content 'Foo bar'
expect(current_path).to eq '/projects/foo/settings'
end
it 'can create a subproject' do
click_on 'Foo project'
click_on 'New subproject'
fill_in 'project[name]', with: 'Foo child'
click_on 'Create'
expect(page).to have_content 'Successful creation.'
expect(current_path).to eq '/projects/foo-child/settings'
end
it 'does not create a project with an already existing identifier' do
click_on 'New project'
fill_in 'project[name]', with: 'Foo project'
click_on 'Create'
expect(page).to have_content 'Identifier has already been taken'
expect(current_path).to eq '/projects'
end
end
describe 'project types' do
let(:phase_type) { FactoryGirl.create(:type, name: 'Phase', is_default: true) }
let(:milestone_type) { FactoryGirl.create(:type, name: 'Milestone', is_default: false) }
let!(:project) { FactoryGirl.create(:project, name: 'Foo project', types: [phase_type, milestone_type]) }
it "have the correct types checked for the project's types" do
visit admin_path
click_on 'Foo project'
click_on 'Types'
field_checked = find_field('Phase', visible: false)['checked']
expect(field_checked).to be_truthy
field_checked = find_field('Milestone', visible: false)['checked']
expect(field_checked).to be_truthy
end
end
describe 'deletion', js: true do
let(:project) { FactoryGirl.create(:project) }
let(:projects_page) { ProjectsPage.new(project) }
before do
projects_page.visit_confirm_destroy
end
describe 'disable delete w/o confirm' do
it { expect(page).to have_css('.danger-zone .button[disabled]') }
end
describe 'disable delete with wrong input' do
let(:input) { find('.danger-zone input') }
it do
input.set 'Not the project name'
expect(page).to have_css('.danger-zone .button[disabled]')
end
end
describe 'enable delete with correct input' do
let(:input) { find('.danger-zone input') }
it do
input.set project.name
expect(page).to have_css('.danger-zone .button:not([disabled])')
end
end
end
end

@ -29,7 +29,7 @@
require 'spec_helper'
require 'features/repositories/repository_settings_page'
describe 'Create repository', type: :feature, js: true do
describe 'Create repository', type: :feature, js: true, selenium: true do
let(:current_user) { FactoryGirl.create (:admin) }
let(:project) { FactoryGirl.create(:project) }
let(:settings_page) { RepositorySettingsPage.new(project) }
@ -204,5 +204,21 @@ describe 'Create repository', type: :feature, js: true do
'/tmp/git/foo.git'
end
end
describe 'remote managed repositories', webmock: true do
let(:vendor) { 'git' }
let(:url) { 'http://myreposerver.example.com/api/' }
let(:config) {
{
git: { manages: url }
}
}
before do
stub_request(:post, url).to_return(status: 200)
end
it_behaves_like 'it can create the managed repository'
end
end
end

@ -97,6 +97,7 @@ describe 'Repository Settings', type: :feature, js: true do
it_behaves_like 'manages the repository with', 'git', 'local', 'Git - Repository', 'project'
context 'managed repositories' do
context 'local' do
include_context 'with tmpdir'
let(:config) {
{
@ -129,6 +130,32 @@ describe 'Repository Settings', type: :feature, js: true do
end
end
context 'remote', webmock: true do
let(:url) { 'http://myreposerver.example.com/api/' }
let(:config) {
{
git: { manages: url }
}
}
let(:managed_vendor) { :git }
let(:repository) {
repo = Repository.build(
project,
managed_vendor,
# Need to pass AC params here manually to simulate a regular repository build
ActionController::Parameters.new({}),
:managed
)
stub_request(:post, url).to_return(status: 200)
repo.save!
repo
}
it_behaves_like 'manages the repository', 'managed'
end
end
describe 'update repositories' do
let(:repository) {
FactoryGirl.create(:repository_subversion,

@ -28,7 +28,7 @@
require 'spec_helper'
describe 'create users', type: :feature do
describe 'create users', type: :feature, selenium: true do
let(:current_user) { FactoryGirl.create :admin }
let(:auth_source) { FactoryGirl.build :dummy_auth_source }
let(:new_user_page) { Pages::NewUser.new }

@ -37,7 +37,7 @@ describe 'user deletion: ', type: :feature, js: true do
context 'regular user' do
let(:current_user) { FactoryGirl.create :user }
it 'can delete their own account' do
it 'can delete their own account', selenium: true do
Setting.users_deletable_by_self = 1
visit delete_my_account_info_path
@ -64,7 +64,7 @@ describe 'user deletion: ', type: :feature, js: true do
let!(:user) { FactoryGirl.create :user }
let(:current_user) { FactoryGirl.create :admin }
it 'can delete other users if the setting permitts it' do
it 'can delete other users if the setting permitts it', selenium: true do
Setting.users_deletable_by_admins = 1
visit edit_user_path(user)

@ -5,7 +5,7 @@ require 'features/work_packages/details/inplace_editor/shared_examples'
require 'features/work_packages/details/inplace_editor/work_package_field'
require 'features/work_packages/work_packages_page'
describe 'activity comments', js: true do
describe 'activity comments', js: true, selenium: true do
let(:project) { FactoryGirl.create :project_with_types, is_public: true }
let!(:work_package) {
FactoryGirl.create(:work_package,
@ -16,11 +16,9 @@ describe 'activity comments', js: true do
let(:selector) { '.work-packages--activity--add-comment' }
let(:initial_comment) { 'the first comment in this WP' }
include_context 'maximized window'
before do
login_as(user)
allow(user.pref).to receive(:warn_on_leaving_unsaved).and_return('0')
allow(user.pref).to receive(:warn_on_leaving_unsaved?).and_return(false)
end
context 'with permission' do

@ -4,9 +4,7 @@ require 'features/work_packages/shared_contexts'
require 'features/work_packages/details/inplace_editor/work_package_field'
require 'features/work_packages/work_packages_page'
describe 'description inplace editor', js: true do
include_context 'maximized window'
describe 'description inplace editor', js: true, selenium: true do
let(:project) { FactoryGirl.create :project_with_types, is_public: true }
let(:property_name) { :description }
let(:property_title) { 'Description' }

@ -4,9 +4,7 @@ require 'features/work_packages/shared_contexts'
require 'features/work_packages/details/inplace_editor/work_package_field'
require 'features/work_packages/work_packages_page'
describe 'subject inplace editor', js: true do
include_context 'maximized window'
describe 'subject inplace editor', js: true, selenium: true do
let(:project) { FactoryGirl.create :project_with_types, is_public: true }
let(:property_name) { :subject }
let(:property_title) { 'Subject' }

@ -28,7 +28,7 @@
require 'spec_helper'
RSpec.feature 'Work package navigation' do
RSpec.feature 'Work package navigation', selenium: true do
let(:user) { FactoryGirl.create(:admin) }
let(:project) { FactoryGirl.create(:project) }
let(:work_package) { FactoryGirl.build(:work_package, project: project) }

@ -29,7 +29,7 @@
require 'spec_helper'
require 'features/work_packages/work_packages_page'
describe 'Select work package row', type: :feature do
describe 'Select work package row', type: :feature, js:true, selenium: true do
let(:user) { FactoryGirl.create(:admin) }
let(:project) { FactoryGirl.create(:project) }
let(:work_package_1) { FactoryGirl.create(:work_package, project: project) }
@ -64,6 +64,7 @@ describe 'Select work package row', type: :feature do
def select_work_package_row_with_shift(number)
element = find(".work-package-table--container tr:nth-of-type(#{number}).issue td.id")
page.driver.browser.action.key_down(:shift)
.click(element.native)
.key_up(:shift)
@ -72,6 +73,7 @@ describe 'Select work package row', type: :feature do
def select_work_package_row_with_ctrl(number)
element = find(".work-package-table--container tr:nth-of-type(#{number}).issue td.id")
page.driver.browser.action.key_down(:control)
.click(element.native)
.key_up(:control)
@ -266,7 +268,9 @@ describe 'Select work package row', type: :feature do
end
describe 'specific selection' do
before do select_work_package_row_with_ctrl(1) end
before do
select_work_package_row_with_ctrl(1)
end
it_behaves_like 'work package row selected' do
# apparently it should be selected if there one row only
@ -284,7 +288,9 @@ describe 'Select work package row', type: :feature do
end
context 'uninvolved row' do
before do check_row_selection_state(3) end
before do
check_row_selection_state(3)
end
it_behaves_like 'work package row not selected' do
let(:index) { 2 }

@ -26,19 +26,6 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
# maximizes the window for any given page
# is needed for certain situations where the details pane must be visible
shared_context 'maximized window' do
def maximize!
page.driver.browser.manage.window.maximize
end
before do
maximize!
end
end
# Ensure the page is completely loaded before the spec is run.
# The status filter is loaded very late in the page setup.
def ensure_wp_table_loaded

@ -31,7 +31,8 @@ require 'spec_helper'
describe ::API::V3::RootRepresenter do
include ::API::V3::Utilities::PathHelper
let(:representer) { described_class.new({}, current_user: double('current_user')) }
let(:user) { FactoryGirl.build(:user) }
let(:representer) { described_class.new({}, current_user: user) }
let(:app_title) { 'Foo Project' }
let(:version) { 'The version is over 9000!' }
@ -68,6 +69,29 @@ describe ::API::V3::RootRepresenter do
let(:link) { 'workPackages' }
let(:href) { api_v3_paths.work_packages }
end
it_behaves_like 'has a titled link' do
let(:link) { 'user' }
let(:href) { api_v3_paths.user(user.id) }
let(:title) { user.name }
end
it_behaves_like 'has an untitled link' do
let(:link) { 'userPreferences' }
let(:href) { api_v3_paths.my_preferences }
end
context 'anonymous user' do
let(:representer) { described_class.new({}, current_user: User.anonymous) }
it_behaves_like 'has no link' do
let(:link) { 'user' }
end
it_behaves_like 'has no link' do
let(:link) { 'userPreferences' }
end
end
end
it 'shows the name of the instance' do

@ -0,0 +1,103 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe ::API::V3::UserPreferences::UserPreferencesRepresenter do
include ::API::V3::Utilities::PathHelper
let(:preference) { FactoryGirl.build(:user_preference) }
let(:user) { FactoryGirl.build_stubbed(:user, preference: preference) }
let(:representer) { described_class.new(preference, current_user: user) }
before do
allow(preference).to receive(:user).and_return(user)
end
context 'generation' do
subject(:generated) { representer.to_json }
it { is_expected.to include_json('UserPreferences'.to_json).at_path('_type') }
it { is_expected.to have_json_path('hideMail') }
it { is_expected.to have_json_path('timeZone') }
it { is_expected.to have_json_path('theme') }
it { is_expected.to have_json_path('commentSortDescending') }
it { is_expected.to have_json_path('warnOnLeavingUnsaved') }
it { is_expected.to have_json_path('accessibilityMode') }
describe 'timeZone' do
context 'no time zone set' do
let(:preference) { FactoryGirl.build(:user_preference, time_zone: '') }
it 'shows the timeZone as nil' do
is_expected.to be_json_eql(nil.to_json).at_path('timeZone')
end
end
context 'short timezone set' do
let(:preference) { FactoryGirl.build(:user_preference, time_zone: 'Berlin') }
it 'shows the canonical time zone' do
is_expected.to be_json_eql('Europe/Berlin'.to_json).at_path('timeZone')
end
end
context 'canonical timezone set' do
let(:preference) { FactoryGirl.build(:user_preference, time_zone: 'Europe/Paris') }
it 'shows the canonical time zone' do
is_expected.to be_json_eql('Europe/Paris'.to_json).at_path('timeZone')
end
end
end
describe '_links' do
it_behaves_like 'has an untitled link' do
let(:link) { 'self' }
let(:href) { api_v3_paths.my_preferences }
end
it_behaves_like 'has a titled link' do
let(:link) { 'user' }
let(:title) { user.name }
let(:href) { api_v3_paths.user(user.id) }
end
describe 'immediate update' do
it_behaves_like 'has an untitled link' do
let(:link) { 'updateImmediately' }
let(:href) { api_v3_paths.my_preferences }
end
it 'is a patch link' do
is_expected.to be_json_eql('patch'.to_json).at_path('_links/updateImmediately/method')
end
end
end
end
end

@ -120,6 +120,12 @@ describe ::API::V3::Utilities::PathHelper do
it_behaves_like 'api v3 path', '/projects/42/work_packages/form'
end
describe '#user_preferences' do
subject { helper.my_preferences }
it_behaves_like 'api v3 path', '/my_preferences'
end
describe '#render_markup' do
subject { helper.render_markup(format: 'super_fancy', link: 'link-ish') }

@ -49,6 +49,10 @@ describe ::API::V3::WorkPackages::WorkPackagesSharedHelpers do
@env
end
def request_body
@env['api.request.body']
end
def current_user
@user
end

@ -29,7 +29,8 @@
require 'spec_helper'
describe UserPreference do
subject { described_class.new }
let(:user) { FactoryGirl.build_stubbed(:user) }
subject { FactoryGirl.build(:user_preference, user: user) }
describe 'default settings' do
it 'hides the email address' do
@ -44,4 +45,103 @@ describe UserPreference do
expect(subject.others[:no_self_notified]).to be_truthy
end
end
shared_examples 'accepts real and false booleans' do |setter, getter|
it 'accepts true boolean' do
subject.send(setter, true)
expect(subject.send(getter)).to be true
subject.send(setter, false)
expect(subject.send(getter)).to be false
end
it 'accepts false booleans' do
%w(true 1).each do |str|
subject.send(setter, str)
expect(subject.send(getter)).to be true
end
%w(false 0).each do |str|
subject.send(setter, str)
expect(subject.send(getter)).to be false
end
end
end
describe 'sort order' do
it_behaves_like 'accepts real and false booleans',
:comments_in_reverse_order=,
:comments_in_reverse_order?
it 'can be changed by string' do
subject.comments_sorting = 'desc'
expect(subject.comments_in_reverse_order?).to be true
subject.comments_sorting = 'asc'
expect(subject.comments_in_reverse_order?).to be false
end
end
describe 'warn on unsaved changes' do
it_behaves_like 'accepts real and false booleans',
:warn_on_leaving_unsaved=,
:warn_on_leaving_unsaved?
end
describe 'time_zone' do
it 'allows to save short time zones' do
subject.time_zone = 'Berlin'
expect(subject).to be_valid
expect(subject.time_zone).to eq('Berlin')
expect(subject.canonical_time_zone).to eq('Europe/Berlin')
end
it 'allows to set full time zones' do
subject.time_zone = 'Europe/Paris'
expect(subject).to be_valid
expect(subject.time_zone).to eq('Europe/Paris')
expect(subject.canonical_time_zone).to eq('Europe/Paris')
end
it 'disallows invalid time zones' do
subject.time_zone = 'Berlin123'
expect(subject).not_to be_valid
end
it 'allows empty values' do
subject.time_zone = nil
expect(subject).to be_valid
subject.time_zone = ''
expect(subject).to be_valid
end
end
describe 'theme' do
it 'allows to save valid themes' do
subject.theme = 'default'
expect(subject).to be_valid
expect(subject.theme).to eq(:default)
subject.theme = :default
expect(subject).to be_valid
expect(subject.theme).to eq(:default)
end
it 'allows empty values' do
subject.theme = nil
expect(subject).to be_valid
subject.theme = ''
expect(subject).to be_valid
end
it 'rejects invalid themes' do
subject.theme = :mycoolthemethatisnotavailableyet
expect(subject).not_to be_valid
subject.theme = 'mycoolthemethatisnotavailableyet'
expect(subject).not_to be_valid
end
end
end

@ -40,7 +40,8 @@ describe 'API v3 Root resource' do
let(:project) { FactoryGirl.create(:project, is_public: false) }
describe '#get' do
subject(:response) { last_response }
let(:response) { last_response }
subject { response.body }
let(:get_path) { api_v3_paths.root }
context 'anonymous user' do
@ -49,11 +50,11 @@ describe 'API v3 Root resource' do
end
it 'should respond with 200' do
expect(subject.status).to eq(200)
expect(response.status).to eq(200)
end
it 'should respond with a root representer' do
expect(subject.body).to have_json_path('instanceName')
expect(subject).to have_json_path('instanceName')
end
end
@ -65,11 +66,11 @@ describe 'API v3 Root resource' do
end
it 'should respond with 200' do
expect(subject.status).to eq(200)
expect(response.status).to eq(200)
end
it 'should respond with a root representer' do
expect(subject.body).to have_json_path('instanceName')
expect(subject).to have_json_path('instanceName')
end
end
end

@ -0,0 +1,141 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require 'rack/test'
describe 'API v3 UserPreferences resource', type: :request do
include Rack::Test::Methods
include ::API::V3::Utilities::PathHelper
let(:user) { FactoryGirl.create(:user) }
let(:preference) { FactoryGirl.create(:user_preference, user: user) }
let(:representer) { described_class.new(preference, current_user: user) }
let(:preference_path) { api_v3_paths.my_preferences }
subject(:response) { last_response }
before do
allow(User).to receive(:current).and_return user
allow(User).to receive(:preference).and_return preference
end
describe '#GET' do
before do
get preference_path
end
context 'when not logged in' do
let(:user) { User.anonymous }
it 'should respond with 401' do
expect(subject.status).to eq(401)
end
end
context 'when logged in' do
it 'should respond with 200' do
expect(subject.status).to eq(200)
end
it 'should respond with a UserPreferences representer' do
expect(subject.body).to be_json_eql('UserPreferences'.to_json).at_path('_type')
end
end
end
describe '#PATCH' do
before do
patch preference_path, params.to_json, 'CONTENT_TYPE' => 'application/json'
preference.reload
end
context 'when not logged in' do
let(:user) { User.anonymous }
let(:params) do
{ whatever: true }
end
it 'should respond with 401' do
expect(subject.status).to eq(401)
end
end
describe 'theme' do
context 'with invalid identifier' do
let(:params) do
{ theme: 'mycoolthemethatisnotavailableyet' }
end
it_behaves_like 'constraint violation' do
let(:message) { 'Theme is not set to one of the allowed values.' }
end
end
context 'with correct identifier' do
let(:params) do
{ theme: 'default' }
end
it 'should respond with a UserPreferences representer' do
expect(subject.body).to be_json_eql(:default.to_json).at_path('theme')
expect(preference.theme).to eq(:default)
end
end
end
describe 'timezone' do
context 'with invalid timezone' do
let(:params) do
{ timeZone: 'Europe/Awesomeland' }
end
it_behaves_like 'constraint violation' do
let(:message) { 'Time zone is not set to one of the allowed values.' }
end
end
context 'with full time zone' do
let(:params) do
{ timeZone: 'Europe/Paris' }
end
it 'should respond with a UserPreferences representer' do
expect(subject.body).to be_json_eql('Europe/Paris'.to_json).at_path('timeZone')
expect(preference.time_zone).to eq('Europe/Paris')
end
end
context 'with short time zone' do
let(:params) do
{ timeZone: 'Hawaii' }
end
it 'should respond with a UserPreferences representer' do
expect(subject.body).to be_json_eql('Pacific/Honolulu'.to_json).at_path('timeZone')
expect(preference.time_zone).to eq('Hawaii')
expect(preference.canonical_time_zone).to eq('Pacific/Honolulu')
end
end
end
end
end

@ -93,7 +93,7 @@ describe Scm::CheckoutInstructionsService do
it 'returns the default translated instructions' do
expect(service.instructions)
.to eq(I18n.t("repositories.checkout.default_instructions.subversion"))
.to eq(I18n.t('repositories.checkout.default_instructions.subversion'))
end
end
end

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save