Standalone CI docker container (#8943)

pull/8963/head
Cyril Rohr 4 years ago committed by GitHub
parent 7b30026946
commit b4086b2968
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 60
      .github/workflows/test-core.yml
  2. 2
      .rspec_parallel
  3. 5
      config/application.rb
  4. 7
      config/environments/test.rb
  5. 21
      docker-compose.ci.yml
  6. 24
      docker-compose.yml
  7. 47
      docker/ci/Dockerfile
  8. 2
      docker/ci/database.yml
  9. 69
      docker/ci/entrypoint.sh
  10. 8
      frontend/karma.conf.js
  11. 17
      frontend/npm-shrinkwrap.json
  12. 15
      lib/tasks/parallel_testing.rake
  13. 4
      modules/avatars/spec/features/shared_avatar_examples.rb
  14. 12
      modules/backlogs/spec/features/stories_in_backlog_spec.rb
  15. 4
      modules/bim/spec/features/bcf/direct_ifc_upload_spec.rb
  16. 15
      modules/bim/spec/features/bcf/export_spec.rb
  17. 4
      modules/bim/spec/features/viewer/show_viewpoint_spec.rb
  18. 6
      modules/budgets/spec/features/budgets/attachment_upload_spec.rb
  19. 10
      modules/costs/spec/features/add_cost_entry_spec.rb
  20. 1
      modules/costs/spec/features/cost_types/delete_cost_type_spec.rb
  21. 3
      modules/costs/spec/features/destroy_work_package_with_cost_entries_spec.rb
  22. 3
      modules/costs/spec/features/members_hourly_rates_spec.rb
  23. 3
      modules/costs/spec/features/users_hourly_rates_spec.rb
  24. 4
      modules/dashboards/spec/features/custom_text_spec.rb
  25. 2
      modules/dashboards/spec/features/work_package_table_spec.rb
  26. 12
      modules/documents/spec/features/attachment_upload_spec.rb
  27. 1
      modules/grids/spec/support/pages/grid.rb
  28. 9
      modules/ldap_groups/spec/features/administration_spec.rb
  29. 4
      modules/ldap_groups/spec/lib/synchronization_spec.rb
  30. 4
      modules/ldap_groups/spec/lib/synchronize_filter_spec.rb
  31. 8
      modules/meeting/spec/features/meetings_attachments_spec.rb
  32. 5
      modules/meeting/spec/features/meetings_close_spec.rb
  33. 4
      modules/meeting/spec/features/meetings_copy_spec.rb
  34. 4
      modules/meeting/spec/features/meetings_delete_spec.rb
  35. 1
      modules/meeting/spec/features/meetings_locking_spec.rb
  36. 2
      modules/meeting/spec/features/meetings_show_spec.rb
  37. 4
      modules/my_page/spec/features/my/custom_text_spec.rb
  38. 7
      modules/pdf_export/spec/features/export_card_configurations_admin_spec.rb
  39. 11
      modules/reporting/spec/features/support/components/cost_reports_base_table.rb
  40. 9
      modules/reporting/spec/features/update_cost_report_spec.rb
  41. 1
      modules/two_factor_authentication/spec/features/account_activation_spec.rb
  42. 4
      modules/two_factor_authentication/spec/features/admin_edit_two_factor_devices_spec.rb
  43. 12
      modules/two_factor_authentication/spec/features/backup_codes/login_with_backup_code_spec.rb
  44. 3
      modules/two_factor_authentication/spec/features/login/switch_available_devices_spec.rb
  45. 2
      modules/two_factor_authentication/spec/features/password_change_spec.rb
  46. 1
      modules/two_factor_authentication/spec/features/remember_cookie/login_with_remember_cookie_spec.rb
  47. 11
      modules/two_factor_authentication/spec/features/shared_2fa_examples.rb
  48. 6
      modules/webhooks/spec/features/manage_webhooks_spec.rb
  49. 4
      script/ci/cache_prepare.sh
  50. 3
      spec/features/activities/wiki_activity_spec.rb
  51. 12
      spec/features/admin/attribute_help_texts_spec.rb
  52. 4
      spec/features/admin/custom_fields/multi_value_custom_fields_spec.rb
  53. 9
      spec/features/admin/enterprise/enterprise_spec.rb
  54. 2
      spec/features/admin/enterprise/enterprise_trial_spec.rb
  55. 7
      spec/features/admin/oauth/oauth_applications_management_spec.rb
  56. 3
      spec/features/auth/consent_auth_stage_spec.rb
  57. 16
      spec/features/auth/omniauth_spec.rb
  58. 1
      spec/features/custom_fields/create_float_spec.rb
  59. 72
      spec/features/custom_fields/custom_fields_spec.rb
  60. 6
      spec/features/forums/attachment_upload_spec.rb
  61. 2
      spec/features/forums/message_spec.rb
  62. 15
      spec/features/global_roles/global_role_assignment_spec.rb
  63. 4
      spec/features/groups/group_memberships_spec.rb
  64. 1
      spec/features/groups/membership_spec.rb
  65. 14
      spec/features/members/membership_spec.rb
  66. 6
      spec/features/members/pagination_spec.rb
  67. 9
      spec/features/menu_items/wiki_menu_item_spec.rb
  68. 3
      spec/features/my/my_notifications_spec.rb
  69. 2
      spec/features/oauth/authorization_code_flow_spec.rb
  70. 17
      spec/features/onboarding/onboarding_tour_spec.rb
  71. 64
      spec/features/projects/projects_index_spec.rb
  72. 4
      spec/features/projects/projects_spec.rb
  73. 2
      spec/features/repositories/create_repository_spec.rb
  74. 6
      spec/features/repositories/repository_settings_spec.rb
  75. 10
      spec/features/roles/create_spec.rb
  76. 8
      spec/features/search_spec.rb
  77. 1
      spec/features/support/components/attribute_help_text_modal.rb
  78. 3
      spec/features/types/reset_form_configuration_spec.rb
  79. 3
      spec/features/users/delete_spec.rb
  80. 6
      spec/features/users/self_registration_spec.rb
  81. 7
      spec/features/users/user_memberships_spec.rb
  82. 5
      spec/features/versions/create_spec.rb
  83. 4
      spec/features/watching/toggle_watching_spec.rb
  84. 7
      spec/features/wiki/adding_editing_history_spec.rb
  85. 15
      spec/features/wiki/attachment_upload_spec.rb
  86. 1
      spec/features/wiki/child_pages_spec.rb
  87. 2
      spec/features/wiki/rename_spec.rb
  88. 12
      spec/features/work_packages/attachments/attachment_upload_spec.rb
  89. 2
      spec/features/work_packages/display_representations/switch_display_representations_spec.rb
  90. 25
      spec/features/work_packages/export_spec.rb
  91. 6
      spec/features/work_packages/index_sums_spec.rb
  92. 2
      spec/features/work_packages/table/inline_create/create_work_packages_spec.rb
  93. 4
      spec/features/work_packages/table/relations_spec.rb
  94. 2
      spec/features/work_packages/table/switch_types_spec.rb
  95. 3
      spec/features/wysiwyg/macros/child_pages_spec.rb
  96. 4
      spec/features/wysiwyg/macros/code_block_macro_spec.rb
  97. 3
      spec/features/wysiwyg/tables_spec.rb
  98. 4
      spec/models/ldap_auth_source_spec.rb
  99. 4
      spec/rails_helper.rb
  100. 31
      spec/support/browsers/chrome.rb
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,60 @@
name: Core/Test
on:
push:
jobs:
units:
name: Units
if: github.repository == 'opf/openproject'
runs-on: [self-hosted,public]
timeout-minutes: 30
env:
CI_CACHE_PATH: "/tmp/${{ github.sha }}/"
CI_JOBS: 16
CI_RETRY_COUNT: 3
LOCAL_DEV_CHECK: 1
steps:
- uses: actions/checkout@v2
- name: cache
uses: actions/cache@v2
with:
path: /tmp/${{ github.sha }}
key: ${{ runner.os }}-core-tests
- name: test
run: |
cp .env.example .env
docker-compose -f docker-compose.ci.yml build ci
docker-compose -f docker-compose.ci.yml run ci setup-tests run-units
- name: cleanup
if: ${{ always() }}
run: |
docker-compose -f docker-compose.ci.yml down --remove-orphans -t 10
sudo chown -R $(whoami):$(id -ng) $CI_CACHE_PATH
features:
needs: units
name: Features
if: github.repository == 'opf/openproject'
runs-on: [self-hosted,public]
timeout-minutes: 200
env:
CI_JOBS: 16
CI_CACHE_PATH: "/tmp/${{ github.sha }}/"
LOCAL_DEV_CHECK: 1
CI_RETRY_COUNT: 3
steps:
- uses: actions/checkout@v2
- name: cache
uses: actions/cache@v2
with:
path: /tmp/${{ github.sha }}
key: ${{ runner.os }}-core-tests
- name: test
run: |
cp .env.example .env
docker-compose -f docker-compose.ci.yml build ci
docker-compose -f docker-compose.ci.yml run ci setup-tests run-features
- name: cleanup
if: ${{ always() }}
run: |
docker-compose -f docker-compose.ci.yml down --remove-orphans -t 10
sudo chown -R $(whoami):$(id -ng) $CI_CACHE_PATH

@ -0,0 +1,2 @@
--format progress
--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log

@ -34,7 +34,10 @@ require 'active_support'
require 'active_support/dependencies'
require 'core_extensions'
ActiveSupport::Deprecation.silenced = Rails.env.production? && !ENV['OPENPROJECT_SHOW_DEPRECATIONS']
# Silence deprecations early on for testing on CI and production
ActiveSupport::Deprecation.silenced =
(Rails.env.production? && !ENV['OPENPROJECT_SHOW_DEPRECATIONS']) ||
(Rails.env.test? && ENV['CI'])
if defined?(Bundler)
# lib directory has to be added to the load path so that

@ -66,7 +66,12 @@ OpenProject::Application.configure do
config.action_mailer.delivery_method = :test
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
config.active_support.deprecation =
if ENV['CI']
:silence
else
:stderr
end
# Disable asset digests
config.assets.compile = true

@ -0,0 +1,21 @@
version: "3.7"
networks:
testing:
services:
ci:
build:
context: ./docker/ci/
dockerfile: Dockerfile
environment:
RSPEC_RETRY_RETRY_COUNT: "${CI_RETRY_COUNT:-3}"
JOBS: "${CI_JOBS:-8}"
tmpfs:
- "/tmp"
volumes:
- ".:/home/dev/openproject"
- "${CI_CACHE_PATH:-/tmp/}op-bundle:/usr/local/bundle"
- "${CI_CACHE_PATH:-/tmp/}op-node:/home/dev/openproject/frontend/node_modules"
networks:
- testing

@ -5,10 +5,12 @@ networks:
testing:
volumes:
downloads-test:
pgdata:
tmp:
opdata:
bundle:
npm:
pgdata-test:
tmp-test:
fedata-test:
@ -150,10 +152,13 @@ services:
- "bundle:/usr/local/bundle"
- "tmp-test:/home/dev/openproject/tmp"
# https://vitobotta.com/2019/09/04/rails-parallel-system-tests-selenium-docker/
selenium-hub:
image: selenium/hub:latest
container_name: selenium-hub
hostname: selenium-hub
environment:
GRID_MAX_SESSION: "${CI_JOBS:-4}"
depends_on:
- chrome
- firefox
@ -167,41 +172,52 @@ services:
image: selenium/node-chrome-debug:latest
volumes:
- /dev/shm:/dev/shm
- "downloads-test:/home/seluser/Downloads"
networks:
- testing
ports:
- 5900:5900
- "5900-5915:5900"
environment:
HUB_HOST: selenium-hub
HUB_PORT: 4444
SCREEN_WIDTH: 1920
SCREEN_HEIGHT: 1080
# in case we want multiple sessions per container
NODE_MAX_INSTANCES: "${CI_JOBS:-4}"
NODE_MAX_SESSION: "${CI_JOBS:-4}"
firefox:
image: selenium/node-firefox-debug:latest
volumes:
- /dev/shm:/dev/shm
- "downloads-test:/home/seluser/Downloads"
networks:
- testing
ports:
- 5901:5900
- "5916-5931:5900"
environment:
HUB_HOST: selenium-hub
HUB_PORT: 4444
SCREEN_WIDTH: 1920
SCREEN_HEIGHT: 1080
# in case we want multiple sessions per container
NODE_MAX_INSTANCES: "${CI_JOBS:-4}"
NODE_MAX_SESSION: "${CI_JOBS:-4}"
opera:
image: selenium/node-opera-debug:latest
volumes:
- /dev/shm:/dev/shm
- "downloads-test:/home/seluser/Downloads"
networks:
- testing
ports:
- 5902:5900
- "5932-5957:5900"
environment:
HUB_HOST: selenium-hub
HUB_PORT: 4444
SCREEN_WIDTH: 1920
SCREEN_HEIGHT: 1080
# in case we want multiple sessions per container
NODE_MAX_INSTANCES: "${CI_JOBS:-4}"
NODE_MAX_SESSION: "${CI_JOBS:-4}"

@ -0,0 +1,47 @@
FROM ruby:2.7.2-buster
MAINTAINER operations@openproject.com
ENV USER=dev
RUN useradd -d /home/$USER -m $USER -s /bin/bash
WORKDIR /home/$USER
RUN wget --quiet -O- https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
RUN echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list
RUN apt-get update -qq && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
postgresql-9.6 postgresql-client-9.6 time pandoc imagemagick libpq-dev default-jre-headless firefox-esr
ENV CI=true
ENV RAILS_ENV=test
ENV NODE_VERSION="12.18.3"
ENV BUNDLER_VERSION="2.1.4"
ENV BUNDLE_WITHOUT="development:production:docker"
ENV OPENPROJECT_DISABLE_DEV_ASSET_PROXY=1
ENV CAPYBARA_DYNAMIC_HOSTNAME=0
ENV CAPYBARA_DOWNLOADED_FILE_DIR=/tmp
# disable deprecations and other warnings in output
ENV RUBYOPT="-W0"
ENV DATABASE_URL=postgres://app:p4ssw0rd@127.0.0.1/app
ENV CHROME_SOURCE_URL=https://dl.google.com/dl/linux/direct/google-chrome-stable_current_amd64.deb
ENV JOBS=4
RUN wget --no-verbose -O /tmp/$(basename $CHROME_SOURCE_URL) $CHROME_SOURCE_URL && \
apt install -y /tmp/$(basename $CHROME_SOURCE_URL) && rm -f /tmp/$(basename $CHROME_SOURCE_URL)
RUN curl -s https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz | tar xzf - -C /usr/local --strip-components=1
RUN gem install bundler --version "$BUNDLER_VERSION" --no-document
COPY ./entrypoint.sh /usr/sbin/entrypoint.sh
VOLUME [ "/usr/local/bundle", "/home/$USER/openproject", "/home/$USER/openproject/tmp" ]
WORKDIR /home/$USER/openproject
ENTRYPOINT [ "/usr/sbin/entrypoint.sh" ]
CMD ["setup-tests", "bash"]
# ruby servers
EXPOSE 3000-3016
# billy proxy servers
EXPOSE 4000-4016

@ -0,0 +1,2 @@
test:
url: <%= ENV['DATABASE_URL'].sub(/\/app$/, "/app#{ENV['TEST_ENV_NUMBER']}") %>

@ -0,0 +1,69 @@
#!/bin/bash
set -e
export PGBIN="$(pg_config --bindir)"
# for parallel rspec
export PARALLEL_TEST_PROCESSORS=$JOBS
# if from within docker
if [ $(id -u) -eq 0 ]; then
if [ ! -d "/tmp/nulldb" ]; then
su - postgres -c "$PGBIN/initdb -E UTF8 -D /tmp/nulldb"
su - postgres -c "$PGBIN/pg_ctl -D /tmp/nulldb -l /dev/null -w start"
echo "create database app; create user app with superuser encrypted password 'p4ssw0rd'; grant all privileges on database app to app;" | su - postgres -c psql
fi
mkdir -p /usr/local/bundle
mkdir -p /home/$USER/openproject/frontend/node_modules
mkdir -p /home/$USER/openproject/tmp
chown $USER:$USER /usr/local/bundle
chown $USER:$USER /home/$USER/openproject/frontend/node_modules
chown $USER:$USER /home/$USER/openproject/tmp
fi
execute() {
if [ $(id -u) -eq 0 ]; then
su $USER -c "$@"
else
bash -c "$@"
fi
}
if [ "$1" == "setup-tests" ]; then
echo "Preparing environment for running tests..."
shift
execute "cp docker/ci/database.yml config/"
for i in $(seq 0 $JOBS); do
folder="$CAPYBARA_DOWNLOADED_FILE_DIR/$i"
echo "Creating folder $folder..."
rm -rf "$folder"
mkdir -p "$folder"
chmod 1777 "$folder"
done
execute "time bundle install -j$JOBS"
execute "TEST_ENV_NUMBER=0 time bundle exec rake db:create db:migrate webdrivers:chromedriver:update webdrivers:geckodriver:update"
execute "time bundle exec rake parallel:setup"
fi
if [ "$1" == "run-units" ]; then
shift
execute "cd frontend && npm install && npm run test"
execute "time bundle exec rspec -I spec_legacy spec_legacy"
execute "time bundle exec rake parallel:units"
fi
if [ "$1" == "run-features" ]; then
shift
execute "cd frontend; npm install ; cd -"
execute "bundle exec rake assets:precompile assets:clean"
execute "cp -rp config/frontend_assets.manifest.json public/assets/frontend_assets.manifest.json"
execute "time bundle exec rake parallel:features"
fi
if [ ! -z "$1" ] ; then
exec "$@"
fi

@ -35,7 +35,13 @@ module.exports = function (config) {
failOnEmptyTestSuite: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadless'],
browsers: ['ChromeHeadlessNoSandbox'],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox']
}
},
singleRun: false
});
};

@ -3423,6 +3423,15 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ=="
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
@ -6281,6 +6290,12 @@
}
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"optional": true
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -14084,6 +14099,7 @@
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},
@ -14654,6 +14670,7 @@
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
},

@ -50,12 +50,15 @@ namespace :parallel do
class ParallelParser
def self.with_args(args, allow_seed = true)
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: rails #{args[0]} -- [options]"
opts.on("-n ARG", "--group-number ARG", Integer) { |num_cpus| options[:num_cpus] = num_cpus || ENV['GROUP'] }
opts.on("-o ARG", "--only-group ARG", Integer) { |group_number| options[:group] = group_number || ENV['GROUP_SIZE'] }
opts.on("-s ARG", "--seed ARG", Integer) { |seed| options[:seed] = seed || ENV['CI_SEED'] } if allow_seed
end.parse!(args[2..-1])
parseable_args = args[2..-1]
if parseable_args
OptionParser.new do |opts|
opts.banner = "Usage: rails #{args[0]} -- [options]"
opts.on("-n ARG", "--group-number ARG", Integer) { |num_cpus| options[:num_cpus] = num_cpus || ENV['GROUP'] }
opts.on("-o ARG", "--only-group ARG", Integer) { |group_number| options[:group] = group_number || ENV['GROUP_SIZE'] }
opts.on("-s ARG", "--seed ARG", Integer) { |seed| options[:seed] = seed || ENV['CI_SEED'] } if allow_seed
end.parse!(parseable_args)
end
yield options
end

@ -45,7 +45,7 @@ shared_examples 'avatar management' do
expect(page).to have_no_selector('.form--fieldset-legend', text: 'GRAVATAR')
# Attach a new invalid image
find('#avatar_file_input').set File.join(image_base_path, 'invalid.txt')
find('#avatar_file_input').set ::UploadedFile.load_from(File.join(image_base_path, 'invalid.txt')).path
# Expect error
expect(page).to have_selector('.form--label.-error')
@ -54,7 +54,7 @@ shared_examples 'avatar management' do
# Attach new image
visit avatar_management_path
expect(page).to have_selector('.avatars--current-local-avatar', text: 'none')
find('#avatar_file_input').set File.join(image_base_path, 'too_big.jpg')
find('#avatar_file_input').set ::UploadedFile.load_from(File.join(image_base_path, 'too_big.jpg')).path
# Expect not error, since ng-file-upload resizes the image
expect(page).to have_no_selector('.form--label.-error')

@ -180,10 +180,12 @@ describe 'Stories in backlog',
backlogs_page
.expect_velocity(sprint, 30)
SeleniumHubWaiter.wait
# Creating a story
backlogs_page
.click_in_backlog_menu(sprint, 'New Story')
SeleniumHubWaiter.wait
backlogs_page
.edit_new_story(subject: 'New story',
story_points: 10)
@ -206,6 +208,7 @@ describe 'Stories in backlog',
# Editing in a sprint
SeleniumHubWaiter.wait
backlogs_page
.edit_story(sprint_story1,
subject: 'Altered story1',
@ -223,12 +226,11 @@ describe 'Stories in backlog',
backlogs_page
.expect_velocity(sprint, 45)
SeleniumHubWaiter.wait
# Moving a story to top
backlogs_page
.drag_in_sprint(sprint_story1, new_story)
sleep(0.5)
backlogs_page
.expect_stories_in_order(sprint, sprint_story1, new_story, sprint_story2)
@ -249,11 +251,10 @@ describe 'Stories in backlog',
# Moving a story to from the backlog to the sprint (3nd position)
SeleniumHubWaiter.wait
backlogs_page
.drag_in_sprint(backlog_story1, sprint_story2, before: false)
sleep(0.5)
backlogs_page
.expect_stories_in_order(sprint, new_story, sprint_story2, backlog_story1, sprint_story1)
@ -271,6 +272,7 @@ describe 'Stories in backlog',
.expect_status_options(backlog_story1,
[default_status, other_status])
SeleniumHubWaiter.wait
backlogs_page
.alter_attributes_in_edit_story_mode(backlog_story1,
subject: 'Altered backlog story1',
@ -291,6 +293,7 @@ describe 'Stories in backlog',
subject: 'Altered backlog story1',
status: other_status.name)
SeleniumHubWaiter.wait
backlogs_page
.enter_edit_story_mode(backlog_story1)
@ -314,6 +317,7 @@ describe 'Stories in backlog',
type: other_story.name)
# The pdf export is reachable via the menu
SeleniumHubWaiter.wait
backlogs_page
.click_in_backlog_menu(sprint, 'Export')
# Will download something that is currently not speced

@ -31,7 +31,7 @@ require 'spec_helper'
describe 'direct IFC upload', type: :feature, js: true, with_direct_uploads: :redirect, with_config: { edition: 'bim' } do
let(:user) { FactoryBot.create :admin }
let(:project) { FactoryBot.create :project, enabled_module_names: %i[bim] }
let(:ifc_fixture) { Rails.root.join('modules/bim/spec/fixtures/files/minimal.ifc') }
let(:ifc_fixture) { ::UploadedFile.load_from('modules/bim/spec/fixtures/files/minimal.ifc') }
before do
login_as user
@ -42,7 +42,7 @@ describe 'direct IFC upload', type: :feature, js: true, with_direct_uploads: :re
it 'should work' do
visit new_bcf_project_ifc_model_path(project_id: project.identifier)
page.attach_file("file", ifc_fixture, visible: :all)
page.attach_file("file", ifc_fixture.path, visible: :all)
click_on "Create"

@ -69,14 +69,15 @@ describe 'bcf export',
before do
login_as current_user
@download_list = DownloadList.new
end
after do
DownloadedFile::clear_downloads
DownloadList.clear
end
def export_into_bcf_extractor
::DownloadedFile::clear_downloads
DownloadList.clear
page.find('.export-bcf-button').click
# Expect to get a response regarding queuing
@ -84,17 +85,16 @@ describe 'bcf export',
wait: 10
perform_enqueued_jobs
# Wait for the file to download
::DownloadedFile.wait_for_download
::DownloadedFile.wait_for_download_content
expect(page).to have_text("completed successfully")
# Close the modal
page.find('.op-modal--modal-close-button').click
@download_list.refresh_from(page)
# Check the downloaded file
OpenProject::Bim::BcfXml::Importer.new(
::DownloadedFile.download,
@download_list.latest_download,
project,
current_user: current_user
).extractor_list
@ -111,6 +111,7 @@ describe 'bcf export',
expect(extractor_list.length).to eq 1
expect(extractor_list.first[:title]).to eq 'Open WP'
model_page.visit!
# Change the query to show all statuses
filters.open
filters.remove_filter 'status'

@ -63,9 +63,9 @@ describe 'Show viewpoint in model viewer',
model_tree.expect_checked 'minimal'
model_tree.all_checkboxes.each do |label, checkbox|
if label.text == 'minimal' || label.text == 'LUB_Segment_new:S_WHG_Ess:7243035'
expect(checkbox.checked?).to eq(true)
expect(checkbox).to be_checked
else
expect(checkbox.checked?).to eq(false)
expect(checkbox).to_not be_checked
end
end
end

@ -40,7 +40,7 @@ describe 'Upload attachment to budget', js: true do
end
let(:project) { FactoryBot.create(:project) }
let(:attachments) { ::Components::Attachments.new }
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { ::UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { ::Components::WysiwygEditor.new }
before do
@ -57,7 +57,7 @@ describe 'Upload attachment to budget', js: true do
fill_in "Subject", with: 'New budget'
# adding an image
editor.drag_attachment image_fixture, 'Image uploaded on creation'
editor.drag_attachment image_fixture.path, 'Image uploaded on creation'
expect(page).to have_selector('attachment-list-item', text: 'image.png')
@ -71,7 +71,7 @@ describe 'Upload attachment to budget', js: true do
click_on "Update"
end
editor.drag_attachment image_fixture, 'Image uploaded the second time'
editor.drag_attachment image_fixture.path, 'Image uploaded the second time'
expect(page).to have_selector('attachment-list-item', text: 'image.png', count: 2)

@ -66,18 +66,18 @@ describe 'Work Package cost fields', type: :feature, js: true do
it 'does not show read-only fields' do
full_view.visit!
# Go to add cost entry page
SeleniumHubWaiter.wait
find('#action-show-more-dropdown-menu .button').click
find('.menu-item', text: 'Log unit costs').click
SeleniumHubWaiter.wait
# Set single value, should update suffix
select 'A', from: 'cost_entry_cost_type_id'
fill_in 'cost_entry_units', with: '1'
sleep 1
expect(page).to have_selector('#cost_entry_unit_name', text: 'A single')
expect(page).to have_selector('#cost_entry_costs', text: '1.00 EUR')
fill_in 'cost_entry_units', with: '2'
sleep 1
expect(page).to have_selector('#cost_entry_unit_name', text: 'A plural')
expect(page).to have_selector('#cost_entry_costs', text: '2.00 EUR')
@ -88,6 +88,7 @@ describe 'Work Package cost fields', type: :feature, js: true do
# Override costs
find('#cost_entry_costs').click
SeleniumHubWaiter.wait
fill_in 'cost_entry_costs_edit', with: '15.52'
click_on 'Save'
@ -112,9 +113,11 @@ describe 'Work Package cost fields', type: :feature, js: true do
full_view.visit!
# Go to add cost entry page
SeleniumHubWaiter.wait
find('#action-show-more-dropdown-menu .button').click
find('.menu-item', text: I18n.t(:button_log_costs)).click
SeleniumHubWaiter.wait
fill_in 'cost_entry_units', with: '1,42'
select 'B', from: 'cost_entry_cost_type_id'
expect(page).to have_selector('#cost_entry_unit_name', text: 'B plural')
@ -122,6 +125,7 @@ describe 'Work Package cost fields', type: :feature, js: true do
# Override costs
find('#cost_entry_costs').click
SeleniumHubWaiter.wait
fill_in 'cost_entry_costs_edit', with: '1.350,25'
click_on I18n.t(:button_save)
@ -139,9 +143,11 @@ describe 'Work Package cost fields', type: :feature, js: true do
expect(page).to have_selector('#cost_entry_costs', text: '1.350,25 EUR')
# Toggle the cost button
SeleniumHubWaiter.wait
find('#cost_entry_costs').click
# Update the costs in german locale
SeleniumHubWaiter.wait
fill_in 'cost_entry_costs_edit', with: '55.000,55'
click_on I18n.t(:button_save)

@ -51,6 +51,7 @@ describe 'deleting a cost type', type: :feature, js: true do
expect_angular_frontend_initialized
expect(page).to have_selector '.generic-table--no-results-container', wait: 10
SeleniumHubWaiter.wait
# Show locked
find('#include_deleted').set true
click_on 'Apply'

@ -74,6 +74,7 @@ describe 'Deleting time entries', type: :feature, js: true do
wp_page = Pages::FullWorkPackage.new(work_package)
wp_page.visit!
SeleniumHubWaiter.wait
find('#action-show-more-dropdown-menu').click
click_link(I18n.t('js.button_delete'))
@ -81,8 +82,8 @@ describe 'Deleting time entries', type: :feature, js: true do
destroy_modal.expect_listed(work_package)
destroy_modal.confirm_deletion
SeleniumHubWaiter.wait
choose 'to_do_action_reassign'
sleep 1
fill_in 'to_do_reassign_to_id', with: other_work_package.id
click_button(I18n.t('button_delete'))

@ -79,6 +79,7 @@ describe 'hourly rates on a member', type: :feature, js: true do
expect_current_rate_in_members_table('0.00 EUR')
click_link('0.00 EUR')
SeleniumHubWaiter.wait
add_rate(date: Date.today, rate: 10)
@ -86,6 +87,7 @@ describe 'hourly rates on a member', type: :feature, js: true do
expect_current_rate_in_members_table('10.00 EUR')
SeleniumHubWaiter.wait
click_link('10.00 EUR')
add_rate(date: 3.days.ago, rate: 20)
@ -94,6 +96,7 @@ describe 'hourly rates on a member', type: :feature, js: true do
expect_current_rate_in_members_table('10.00 EUR')
SeleniumHubWaiter.wait
click_link('10.00 EUR')
change_rate_date(from: Date.today, to: 5.days.ago)

@ -63,6 +63,7 @@ describe 'hourly rates on user edit', type: :feature, js: true do
describe 'deleting all rates' do
before do
click_link 'Update' # go to update view for rates
SeleniumHubWaiter.wait
find('.icon-delete').click # delete last existing rate
click_on 'Save' # save change
end
@ -72,7 +73,7 @@ describe 'hourly rates on user edit', type: :feature, js: true do
expect(page).to have_text /rate history/i
expect(page).to have_text I18n.t('no_results_title_text')
expect(page).not_to have_text 'Current rate'
expect(page).to have_no_text 'Current rate'
end
end
end

@ -50,7 +50,7 @@ describe 'Project description widget on dashboard', type: :feature, js: true do
let(:dashboard_page) do
Pages::Dashboard.new(project)
end
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { ::UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { ::Components::WysiwygEditor.new 'body' }
let(:field) { TextEditorField.new(page, 'description', selector: '.inline-edit--active-field') }
@ -104,7 +104,7 @@ describe 'Project description widget on dashboard', type: :feature, js: true do
# The drag_attachment is written in a way that it requires to be executed with page on body
# so we cannot have it wrapped in the within block.
editor.drag_attachment image_fixture, 'Image uploaded'
editor.drag_attachment image_fixture.path, 'Image uploaded'
within custom_text_widget.area do
expect(page).to have_selector('attachment-list-item', text: 'image.png')

@ -96,8 +96,6 @@ describe 'Arbitrary WorkPackage query table widget dashboard', type: :feature, j
it 'can add the widget and see the work packages of the filtered for types' do
dashboard_page.add_widget(1, 1, :row, "Work packages table")
sleep(0.2)
filter_area = Components::Grids::GridArea.new('.grid--area.-widgeted:nth-of-type(2)')
filter_area.expect_to_span(1, 1, 2, 3)

@ -41,7 +41,7 @@ describe 'Upload attachment to documents', js: true do
end
let(:project) { FactoryBot.create(:project) }
let(:attachments) { ::Components::Attachments.new }
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { ::UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { ::Components::WysiwygEditor.new }
before do
@ -53,11 +53,12 @@ describe 'Upload attachment to documents', js: true do
visit new_project_document_path(project)
expect(page).to have_selector('#new_document', wait: 10)
SeleniumHubWaiter.wait
select(category.name, from: 'Category')
fill_in "Title", with: 'New documentation'
# adding an image
editor.drag_attachment image_fixture, 'Image uploaded on creation'
editor.drag_attachment image_fixture.path, 'Image uploaded on creation'
expect(page).to have_selector('attachment-list-item', text: 'image.png')
click_on 'Create'
@ -71,6 +72,7 @@ describe 'Upload attachment to documents', js: true do
expect(document.title).to eq 'New documentation'
# Expect it to be present on the show page
SeleniumHubWaiter.wait
find('.document-category-elements--header a', text: 'New documentation').click
expect(page).to have_current_path "/documents/#{document.id}", wait: 10
expect(page).to have_selector('#content img', count: 1)
@ -78,10 +80,12 @@ describe 'Upload attachment to documents', js: true do
# Adding a second image
# We should be using the 'Edit' button at the top but that leads to flickering specs
# FIXME: yes indeed
visit edit_document_path(document)
#editor.click_and_type_slowly 'abc'
editor.drag_attachment image_fixture, 'Image uploaded the second time'
SeleniumHubWaiter.wait
editor.drag_attachment image_fixture.path, 'Image uploaded the second time'
expect(page).to have_selector('attachment-list-item', text: 'image.png', count: 2)
click_on 'Save'
@ -96,7 +100,7 @@ describe 'Upload attachment to documents', js: true do
context 'with direct uploads (Regression #34285)', with_direct_uploads: true do
before do
allow_any_instance_of(Attachment).to receive(:diskfile).and_return Struct.new(:path).new(image_fixture.to_s)
allow_any_instance_of(Attachment).to receive(:diskfile).and_return image_fixture
end
it_behaves_like 'can upload an image'

@ -35,6 +35,7 @@ module Pages
expect(page)
.to have_content(I18n.t('js.grid.add_widget'))
SeleniumHubWaiter.wait
page.find('.grid--addable-widget', text: Regexp.new("^#{name}$")).click
end
end

@ -23,6 +23,7 @@ describe 'LDAP group sync administration spec', type: :feature, js: true do
# Create group
find('.button', text: I18n.t('ldap_groups.synchronized_groups.singular')).click
SeleniumHubWaiter.wait
select 'ldap', from: 'synchronized_group_auth_source_id'
select 'foo', from: 'synchronized_group_group_id'
@ -36,6 +37,7 @@ describe 'LDAP group sync administration spec', type: :feature, js: true do
expect(page).to have_selector('td.users', text: '0')
# Show entry
SeleniumHubWaiter.wait
find('td.dn a').click
expect(page).to have_selector '.generic-table--empty-row'
@ -54,11 +56,12 @@ describe 'LDAP group sync administration spec', type: :feature, js: true do
visit ldap_groups_synchronized_groups_path
expect_angular_frontend_initialized
find('.buttons a', text: 'Delete').click
SeleniumHubWaiter.wait
find('.danger-zone--verification input').set 'cn=foo,ou=groups,dc=example,dc=com'
sleep 2
SeleniumHubWaiter.wait
click_on 'Delete'
expect(page).to have_selector('.flash.notice', text: I18n.t(:notice_successful_delete))
@ -67,4 +70,4 @@ describe 'LDAP group sync administration spec', type: :feature, js: true do
expect(::LdapGroups::Membership.where(id: memberships)).to be_empty
end
end
end
end

@ -8,7 +8,7 @@ describe OpenProject::LdapGroups::Synchronization, with_ee: %i[ldap_groups] do
before(:all) do
ldif = Rails.root.join('spec/fixtures/ldap/users.ldif')
@ldap_server = Ladle::Server.new(quiet: false, port: '12389', domain: 'dc=example,dc=com', ldif: ldif).start
@ldap_server = Ladle::Server.new(quiet: false, port: ParallelHelper.port_for_ldap.to_s, domain: 'dc=example,dc=com', ldif: ldif).start
end
after(:all) do
@ -20,7 +20,7 @@ describe OpenProject::LdapGroups::Synchronization, with_ee: %i[ldap_groups] do
# two groups foo (aa729), bar(aa729, bb459, cc414)
let(:auth_source) do
FactoryBot.create :ldap_auth_source,
port: '12389',
port: ParallelHelper.port_for_ldap.to_s,
account: 'uid=admin,ou=system',
account_password: 'secret',
base_dn: 'ou=people,dc=example,dc=com',

@ -4,7 +4,7 @@ require 'ladle'
describe OpenProject::LdapGroups::SynchronizeFilter, with_ee: %i[ldap_groups] do
before(:all) do
ldif = Rails.root.join('spec/fixtures/ldap/users.ldif')
@ldap_server = Ladle::Server.new(quiet: false, port: '12389', domain: 'dc=example,dc=com', ldif: ldif).start
@ldap_server = Ladle::Server.new(quiet: false, port: ParallelHelper.port_for_ldap.to_s, domain: 'dc=example,dc=com', ldif: ldif).start
end
after(:all) do
@ -16,7 +16,7 @@ describe OpenProject::LdapGroups::SynchronizeFilter, with_ee: %i[ldap_groups] do
# two groups foo (aa729), bar(aa729, bb459, cc414)
let(:auth_source) do
FactoryBot.create :ldap_auth_source,
port: '12389',
port: ParallelHelper.port_for_ldap.to_s,
account: 'uid=admin,ou=system',
account_password: 'secret',
base_dn: 'dc=example,dc=com',

@ -22,7 +22,7 @@ describe 'Add an attachment to a meeting (agenda)', js: true do
end
let(:attachments) { ::Components::Attachments.new }
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { Components::WysiwygEditor.new }
before do
@ -42,7 +42,7 @@ describe 'Add an attachment to a meeting (agenda)', js: true do
editor.expect_button 'Insert image'
editor.drag_attachment image_fixture, 'Some image caption'
editor.drag_attachment image_fixture.path, 'Some image caption'
click_on "Save"
@ -64,13 +64,13 @@ describe 'Add an attachment to a meeting (agenda)', js: true do
##
# Attach file manually
expect(page).to have_no_selector('.work-package--attachments--filename')
attachments.attach_file_on_input(image_fixture)
attachments.attach_file_on_input(image_fixture.path)
expect(page).not_to have_selector('notification-upload-progress')
expect(page).to have_selector('.work-package--attachments--filename', text: 'image.png', wait: 5)
##
# and via drag & drop
attachments.drag_and_drop_file(container, Rails.root.join('spec/fixtures/files/image.png'))
attachments.drag_and_drop_file(container, image_fixture.path)
expect(page).not_to have_selector('notification-upload-progress')
expect(page).to have_selector('.work-package--attachments--filename', text: 'image.png', count: 2, wait: 5)
end

@ -57,12 +57,15 @@ describe 'Meetings close', type: :feature do
click_link meeting.title
# Go to minutes, expect uneditable
SeleniumHubWaiter.wait
find('.tabrow a', text: 'MINUTES').click
expect(page).to have_selector('.button', text: 'Close the agenda to begin the Minutes')
# Close the meeting
SeleniumHubWaiter.wait
find('.tabrow a', text: 'AGENDA').click
SeleniumHubWaiter.wait
find('.button', text: 'Close').click
page.accept_confirm
@ -73,7 +76,9 @@ describe 'Meetings close', type: :feature do
expect(page).to have_selector('#meeting_minutes-text', text: 'asdf')
# Go back to agenda, expect we can open it again
SeleniumHubWaiter.wait
find('.tabrow a', text: 'AGENDA').click
SeleniumHubWaiter.wait
find('.button', text: 'Open').click
page.accept_confirm
expect(page).to have_selector('.button', text: 'Close')

@ -73,6 +73,7 @@ describe 'Meetings copy', type: :feature, js: true do
expect(page)
.to have_link 'Copy'
SeleniumHubWaiter.wait
click_link 'Copy'
end
@ -87,6 +88,7 @@ describe 'Meetings copy', type: :feature, js: true do
expect(page)
.to have_field 'Time', with: "18:55"
SeleniumHubWaiter.wait
click_button "Create"
# Be on the new meeting's page with copied over attributes
@ -115,12 +117,14 @@ describe 'Meetings copy', type: :feature, js: true do
.to have_content "Attendees:"
# Copies the agenda
SeleniumHubWaiter.wait
click_link "Agenda"
expect(page)
.to have_content agenda_text
# Adds an entry to the history
SeleniumHubWaiter.wait
click_link "History"
expect(page)

@ -54,7 +54,9 @@ describe 'Meetings deletion', type: :feature do
it 'can delete own and other`s meetings' do
visit meetings_path(project)
SeleniumHubWaiter.wait
click_link meeting.title
SeleniumHubWaiter.wait
click_link "Delete"
page.accept_confirm
@ -62,7 +64,9 @@ describe 'Meetings deletion', type: :feature do
expect(page)
.to have_current_path meetings_path(project)
SeleniumHubWaiter.wait
click_link other_meeting.title
SeleniumHubWaiter.wait
click_link "Delete"
page.accept_confirm

@ -46,6 +46,7 @@ describe 'Meetings locking', type: :feature, js: true do
within '#tab-content-agenda' do
find('.button--edit-agenda').click
SeleniumHubWaiter.wait
agenda.text = 'blabla'
agenda.save!

@ -72,7 +72,9 @@ describe 'Meetings', type: :feature, js: true do
agenda_update
visit meeting_path(meeting)
click_on 'History'
SeleniumHubWaiter.wait
find('#version-1').click
expect(page).to have_selector('.meeting_agenda', text: 'foo')

@ -49,7 +49,7 @@ describe 'Custom text widget on my page', type: :feature, js: true do
let(:my_page) do
Pages::My::Page.new
end
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { ::UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { ::Components::WysiwygEditor.new 'body' }
let(:field) { TextEditorField.new(page, 'description', selector: '.inline-edit--active-field') }
@ -94,7 +94,7 @@ describe 'Custom text widget on my page', type: :feature, js: true do
# The drag_attachment is written in a way that it requires to be executed with page on body
# so we cannot have it wrapped in the within block.
editor.drag_attachment image_fixture, 'Image uploaded'
editor.drag_attachment image_fixture.path, 'Image uploaded'
within custom_text_widget.area do
expect(page).to have_selector('attachment-list-item', text: 'image.png')

@ -47,6 +47,7 @@ describe "export card configurations Admin", type: :feature, js: true do
# CREATE
click_on 'New Export Card Config'
SeleniumHubWaiter.wait
fill_in 'export_card_configuration_name', with: 'New config'
fill_in 'export_card_configuration_per_page', with: '5'
select 'landscape', from: 'export_card_configuration_orientation'
@ -56,7 +57,9 @@ describe "export card configurations Admin", type: :feature, js: true do
expect(page).to have_text 'Successful creation.'
# EDIT
SeleniumHubWaiter.wait
page.first('a', text: 'Config 1').click
SeleniumHubWaiter.wait
fill_in 'export_card_configuration_name', with: 'New name'
fill_in 'export_card_configuration_per_page', with: '5'
select 'portrait', from: 'export_card_configuration_orientation'
@ -68,11 +71,13 @@ describe "export card configurations Admin", type: :feature, js: true do
expect(config1.reload).to be_portrait
# DEACTIVATE
SeleniumHubWaiter.wait
page.first('a', text: 'De-activate').click
expect(page).to have_text 'Config succesfully de-activated'
# ACTIVATE
SeleniumHubWaiter.wait
page.first('a', text: 'Activate').click
expect(page).to have_text 'Config succesfully activated'
end
end
end

@ -55,6 +55,7 @@ module Components
end
def edit_time_entry(new_value, row)
SeleniumHubWaiter.wait
page.find("#{row_selector(row)} .icon-edit").click
time_logging_modal.is_visible true
@ -62,31 +63,29 @@ module Components
time_logging_modal.work_package_is_missing false
time_logging_modal.perform_action 'Save'
sleep(3)
SeleniumHubWaiter.wait
expect_action_icon 'edit', row
expect_value new_value, row
end
def edit_cost_entry(new_value, row, cost_entry_id)
SeleniumHubWaiter.wait
page.find("#{row_selector(row)} .icon-edit").click
expect(page).to have_current_path('/cost_entries/' + cost_entry_id + '/edit')
SeleniumHubWaiter.wait
fill_in('cost_entry_units', with: new_value)
click_button 'Save'
expect(page).to have_selector('.flash.notice')
sleep(3)
end
def delete_entry(row)
SeleniumHubWaiter.wait
page.find("#{row_selector(row)} .icon-delete").click
page.driver.browser.switch_to.alert.accept
sleep(3)
end
private

@ -54,16 +54,11 @@ describe "updating a cost report's cost type", type: :feature, js: true do
it 'works' do
report_page.visit!
report_page.save(as: 'My Query', public: true)
SeleniumHubWaiter.wait
report_page.switch_to_type cost_type.name
click_on "Save"
click_on "My Query"
option = all("[name=unit]").last
expect(option).to be_checked
expect(option.value).to eq cost_type.id.to_s
expect(page).to have_field(cost_type.name, checked: true)
end
end

@ -51,6 +51,7 @@ describe 'activating an invited account',
expect(page).to have_selector('.flash.notice', text: 'Developer strategy generated the following one-time password:')
SeleniumHubWaiter.wait
fill_in I18n.t(:field_otp), with: sms_token
click_button I18n.t(:button_login)

@ -39,12 +39,15 @@ describe 'Admin 2FA management', with_2fa_ee: true, type: :feature,
# Visit inline create
find('.button', text: I18n.t('two_factor_authentication.admin.button_register_mobile_phone_for_user')).click
SeleniumHubWaiter.wait
# Try to save with invalid phone number
fill_in 'device_phone_number', with: 'invalid!'
click_button I18n.t(:button_continue)
# Enter valid phone number
expect(page).to have_selector('#errorExplanation', text: 'Phone number must be of format +XX XXXXXXXXX')
SeleniumHubWaiter.wait
fill_in 'device_phone_number', with: '+49 123456789'
click_button I18n.t(:button_continue)
@ -52,6 +55,7 @@ describe 'Admin 2FA management', with_2fa_ee: true, type: :feature,
expect(page).to have_selector('.mobile-otp--two-factor-device-row td .icon-yes', count: 2)
expect(page).to have_selector('.on-off-status.-enabled')
SeleniumHubWaiter.wait
# Delete the one
find('.two-factor--delete-button').click
dialog.confirm_flow_with user_password, should_fail: false

@ -40,12 +40,13 @@ describe 'Login with 2FA backup code', with_2fa_ee: true, type: :feature,
# Open other options
# This may fail on the first request when the assets aren't ready yet
retry_block do
find('#toggle_resend_form').click
find('a', text: I18n.t('two_factor_authentication.login.enter_backup_code_title'), wait: 10).click
end
SeleniumHubWaiter.wait
find('#toggle_resend_form').click
SeleniumHubWaiter.wait
find('a', text: I18n.t('two_factor_authentication.login.enter_backup_code_title'), wait: 2).click
expect(page).to have_selector('h2', text: I18n.t('two_factor_authentication.login.enter_backup_code_title'))
SeleniumHubWaiter.wait
fill_in 'backup_code', with: 'whatever'
click_on 'Submit'
@ -55,10 +56,13 @@ describe 'Login with 2FA backup code', with_2fa_ee: true, type: :feature,
# Try again!
first_login_step
SeleniumHubWaiter.wait
find('#toggle_resend_form').click
SeleniumHubWaiter.wait
find('a', text: I18n.t('two_factor_authentication.login.enter_backup_code_title')).click
expect(page).to have_selector('h2', text: I18n.t('two_factor_authentication.login.enter_backup_code_title'))
SeleniumHubWaiter.wait
fill_in 'backup_code', with: valid_backup_codes.first
click_on 'Submit'

@ -22,8 +22,11 @@ describe 'Login by switching 2FA device', with_2fa_ee: true, type: :feature,
expect(page).to have_selector('input#otp')
SeleniumHubWaiter.wait
# Toggle device to TOTP
find('#toggle_resend_form').click
SeleniumHubWaiter.wait
find(".button--link[value='#{device2.redacted_identifier}']").click
expect(page).to have_selector('input#otp')

@ -31,6 +31,7 @@ describe 'Password change with OTP', with_2fa_ee: true, type: :feature,
expect(page).to have_selector('h2', text: I18n.t(:button_change_password))
within('#content') do
SeleniumHubWaiter.wait
fill_in('password', with: user_password)
fill_in('new_password', with: new_user_password)
fill_in('new_password_confirmation', with: new_user_password)
@ -39,6 +40,7 @@ describe 'Password change with OTP', with_2fa_ee: true, type: :feature,
if requires_otp
expect(page).to have_selector('input#otp')
SeleniumHubWaiter.wait
fill_in 'otp', with: sms_token
click_button I18n.t(:button_login)
end

@ -27,6 +27,7 @@ describe 'Login with 2FA remember cookie',
first_login_step
expect(page).to have_selector('input#remember_me')
SeleniumHubWaiter.wait
check 'remember_me'
two_factor_step sms_token

@ -1,5 +1,6 @@
def first_login_step
visit signin_path
SeleniumHubWaiter.wait
within('#login-form') do
fill_in('username', with: user.login)
fill_in('password', with: user_password)
@ -9,12 +10,14 @@ end
def two_factor_step(token)
expect(page).to have_selector('input#otp')
SeleniumHubWaiter.wait
fill_in 'otp', with: token
click_button I18n.t(:button_login)
end
def expect_logged_in
visit my_account_path
SeleniumHubWaiter.wait
expect(page).to have_selector('.form--field-container', text: user.login)
end
@ -34,14 +37,18 @@ shared_examples 'create enforced sms device' do
it do
expect(page).to have_selector('.flash.info', text: I18n.t('two_factor_authentication.forced_registration.required_to_add_device'))
SeleniumHubWaiter.wait
# Create SMS device
find('.mobile-otp-new-device-sms .button--tiny').click
SeleniumHubWaiter.wait
fill_in 'device_phone_number', with: 'invalid'
click_on 'Continue'
# Expect error on invalid phone
expect(page).to have_selector('#errorExplanation', text: 'Phone number must be of format +XX XXXXXXXXX')
SeleniumHubWaiter.wait
fill_in 'device_phone_number', with: '+49 123456789'
click_on 'Continue'
@ -49,6 +56,7 @@ shared_examples 'create enforced sms device' do
expect(page).to have_selector('h2', text: I18n.t('two_factor_authentication.devices.confirm_device'))
expect(page).to have_selector('input#otp')
SeleniumHubWaiter.wait
# Fill in wrong token
fill_in 'otp', with: 'whatever'
@ -65,6 +73,7 @@ shared_examples 'create enforced sms device' do
expect(page).to have_selector('input#otp')
expect(page).to have_selector('.flash.error', text: I18n.t('two_factor_authentication.devices.registration_failed_token_invalid'))
SeleniumHubWaiter.wait
# Fill in wrong token
fill_in 'otp', with: sms_token
click_button I18n.t(:button_continue)
@ -72,4 +81,4 @@ shared_examples 'create enforced sms device' do
# Expected logged in after correct registration
expect_logged_in
end
end
end

@ -24,6 +24,7 @@ describe 'Manage webhooks through UI', type: :feature, js: true do
# Visit inline create
find('.wp-inline-create--add-link').click
SeleniumHubWaiter.wait
# Fill in elements
fill_in 'webhook_name', with: 'My webhook'
@ -50,9 +51,11 @@ describe 'Manage webhooks through UI', type: :feature, js: true do
expect(page).to have_selector('.webhooks--outgoing-webhook-row .events', text: 'Work packages')
expect(page).to have_selector('.webhooks--outgoing-webhook-row .description', text: webhook.description)
SeleniumHubWaiter.wait
# Edit this webhook
find(".webhooks--outgoing-webhook-row-#{webhook.id} .icon-edit").click
SeleniumHubWaiter.wait
# Check the other event
find('.form--check-box[value="work_package:created"]').set false
find('.form--check-box[value="work_package:updated"]').set true
@ -69,6 +72,7 @@ describe 'Manage webhooks through UI', type: :feature, js: true do
expect(webhook.projects.all).to eq [project]
expect(webhook.all_projects).to be_falsey
SeleniumHubWaiter.wait
# Delete webhook
find(".webhooks--outgoing-webhook-row-#{webhook.id} .icon-delete").click
page.driver.browser.switch_to.alert.accept
@ -83,6 +87,7 @@ describe 'Manage webhooks through UI', type: :feature, js: true do
it 'shows the delivery' do
visit admin_outgoing_webhooks_path
SeleniumHubWaiter.wait
find('.webhooks--outgoing-webhook-row .name a', text: 'testing').click
expect(page).to have_selector('.on-off-status.-enabled')
@ -90,6 +95,7 @@ describe 'Manage webhooks through UI', type: :feature, js: true do
expect(page).to have_selector('td.response_code', text: '200')
# Open modal
SeleniumHubWaiter.wait
find('td.response_body a', text: 'Show').click
page.within('.webhooks--response-body-modal') do

@ -38,9 +38,9 @@ run() {
eval $2;
}
run "bundle exec rake db:migrate webdrivers:chromedriver:update webdrivers:geckodriver:update"
run "bundle exec rake db:create db:migrate webdrivers:chromedriver:update webdrivers:geckodriver:update"
run "for i in {1..3}; do (cd frontend; npm install && break || sleep 15;) done"
run "cd frontend; npm install ; cd -"
run "bundle exec rake assets:precompile assets:clean"

@ -53,13 +53,16 @@ feature 'Wiki activities' do
editor.set_markdown('First content')
click_button 'Save'
expect(page).to have_text("Successful creation")
# alter the page
SeleniumHubWaiter.wait
click_link 'Edit'
editor.set_markdown('Second content')
click_button 'Save'
expect(page).to have_text("Successful update")
# After creating and altering the page, there
# will be two activities to see

@ -34,8 +34,7 @@ describe 'Attribute help texts', js: true do
let(:instance) { AttributeHelpText.last }
let(:modal) { Components::AttributeHelpTextModal.new(instance) }
let(:editor) { Components::WysiwygEditor.new }
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:relation_columns_allowed) { true }
describe 'Work package help texts' do
@ -48,7 +47,7 @@ describe 'Attribute help texts', js: true do
context 'with direct uploads (Regression #34285)', with_direct_uploads: true do
before do
allow_any_instance_of(Attachment).to receive(:diskfile).and_return Struct.new(:path).new(image_fixture.to_s)
allow_any_instance_of(Attachment).to receive(:diskfile).and_return image_fixture
end
it 'can upload an image' do
@ -56,7 +55,7 @@ describe 'Attribute help texts', js: true do
select 'Status', from: 'attribute_help_text_attribute_name'
editor.set_markdown('My attribute help text')
editor.drag_attachment image_fixture, 'Image uploaded on creation'
editor.drag_attachment image_fixture.path, 'Image uploaded on creation'
expect(page).to have_selector('attachment-list-item', text: 'image.png')
click_button 'Save'
@ -81,7 +80,7 @@ describe 'Attribute help texts', js: true do
# Add an image
# adding an image
editor.drag_attachment image_fixture, 'Image uploaded on creation'
editor.drag_attachment image_fixture.path, 'Image uploaded on creation'
expect(page).to have_selector('attachment-list-item', text: 'image.png')
click_button 'Save'
@ -105,13 +104,16 @@ describe 'Attribute help texts', js: true do
modal.close!
# -> edit
SeleniumHubWaiter.wait
page.find('.attribute-help-text--entry td a', text: 'Status').click
SeleniumHubWaiter.wait
expect(page).to have_selector('#attribute_help_text_attribute_name[disabled]')
editor.set_markdown(' ')
click_button 'Save'
# Handle errors
expect(page).to have_selector('#errorExplanation', text: "Help text can't be blank.")
SeleniumHubWaiter.wait
editor.set_markdown('New**help**text')
click_button 'Save'

@ -60,6 +60,7 @@ describe 'Multi-value custom fields creation', type: :feature, js: true do
# Create CF
click_on 'Create a new custom field'
SeleniumHubWaiter.wait
fill_in 'custom_field_name', with: 'My List CF'
select 'List', from: 'custom_field_field_format'
@ -68,17 +69,20 @@ describe 'Multi-value custom fields creation', type: :feature, js: true do
# Add new row
find('#add-custom-option').click
SeleniumHubWaiter.wait
expect(page).to have_selector('input#custom_field_custom_options_attributes_1_value')
fill_in 'custom_field_custom_options_attributes_1_value', with: 'B'
# Add new row
find('#add-custom-option').click
SeleniumHubWaiter.wait
expect(page).to have_selector('input#custom_field_custom_options_attributes_2_value')
fill_in 'custom_field_custom_options_attributes_2_value', with: 'C'
click_on 'Save'
# Edit again
SeleniumHubWaiter.wait
page.find('a', text: 'My List CF').click
expect(page).to have_selector('input#custom_field_custom_options_attributes_0_value[value=A]')
expect(page).to have_selector('input#custom_field_custom_options_attributes_1_value[value=B]')

@ -92,9 +92,9 @@ describe 'Enterprise token', type: :feature, js: true do
RequestStore.clear!
expect(EnterpriseToken.current.encoded_token).to eq('foobar')
# Replace token
find('.collapsible-section--toggle-link').click
textarea.set 'blabla'
expect(page).to have_text("Successful update")
click_on "Replace your current support token"
fill_in 'enterprise_token_encoded_token', with: "blabla"
submit_button.click
expect(page).to have_selector('.flash.notice', text: I18n.t(:notice_successful_update))
@ -103,7 +103,8 @@ describe 'Enterprise token', type: :feature, js: true do
expect(EnterpriseToken.current.encoded_token).to eq('blabla')
# Remove token
find('.button.icon-delete', text: I18n.t(:button_delete)).click
SeleniumHubWaiter.wait
click_on "Delete"
# Expect modal
find('.confirm-form-submit--continue').click

@ -30,7 +30,7 @@ require 'spec_helper'
describe 'Enterprise trial management',
type: :feature,
driver: :firefox_billy do
driver: :chrome_billy do
let(:admin) { FactoryBot.create(:admin) }

@ -42,6 +42,8 @@ describe 'OAuth applications management', type: :feature, js: true do
# Create application
find('.button', text: 'Add').click
SeleniumHubWaiter.wait
fill_in 'application_name', with: 'My API application'
# Fill invalid redirect_uri
fill_in 'application_redirect_uri', with: "not a url!"
@ -50,6 +52,7 @@ describe 'OAuth applications management', type: :feature, js: true do
expect(page).to have_selector('.errorExplanation', text: 'Redirect URI must be an absolute URI.')
# Can create localhost without https (https://community.openproject.com/wp/34025)
SeleniumHubWaiter.wait
fill_in 'application_redirect_uri', with: "urn:ietf:wg:oauth:2.0:oob\nhttp://localhost/my/callback"
click_on 'Create'
@ -63,12 +66,15 @@ describe 'OAuth applications management', type: :feature, js: true do
expect(page.first('.attributes-key-value--value code').text).to match /\w+/
# Edit again
SeleniumHubWaiter.wait
click_on 'Edit'
SeleniumHubWaiter.wait
fill_in 'application_redirect_uri', with: "urn:ietf:wg:oauth:2.0:oob"
click_on 'Save'
# Show application
SeleniumHubWaiter.wait
find('td a', text: 'My API application').click
expect(page).to have_no_selector('.attributes-key-value--key', text: 'Client secret')
@ -76,6 +82,7 @@ describe 'OAuth applications management', type: :feature, js: true do
expect(page).to have_selector('.attributes-key-value--key', text: 'Client ID')
expect(page).to have_selector('.attributes-key-value--value', text: "urn:ietf:wg:oauth:2.0:oob")
SeleniumHubWaiter.wait
click_on 'Delete'
page.driver.browser.switch_to.alert.accept

@ -135,6 +135,7 @@ describe 'Authentication Stages', type: :feature do
expect(page).to have_selector('.account-consent')
expect(page).to have_selector('h1', text: 'Consent header')
SeleniumHubWaiter.wait
# Confirm consent
check 'consent_check'
click_on I18n.t(:button_continue)
@ -164,6 +165,7 @@ describe 'Authentication Stages', type: :feature do
visit signout_path
login_with user.login, user_password
SeleniumHubWaiter.wait
# Confirm consent
check 'consent_check'
click_on I18n.t(:button_continue)
@ -215,6 +217,7 @@ describe 'Authentication Stages', type: :feature do
expect(page).to have_selector('h2', text: 'Consent')
# Confirm consent
SeleniumHubWaiter.wait
check 'consent_check'
click_on I18n.t(:button_continue)

@ -71,6 +71,8 @@ describe 'Omniauth authentication', type: :feature do
it 'should redirect to back url' do
visit account_lost_password_path
click_link("Omniauth Developer", match: :first, visible: :all)
SeleniumHubWaiter.wait
fill_in('first_name', with: user.firstname)
fill_in('last_name', with: user.lastname)
fill_in('email', with: user.mail)
@ -81,6 +83,8 @@ describe 'Omniauth authentication', type: :feature do
it 'should sign in user' do
visit '/auth/developer'
SeleniumHubWaiter.wait
fill_in('first_name', with: user.firstname)
fill_in('last_name', with: user.lastname)
fill_in('email', with: user.mail)
@ -97,6 +101,7 @@ describe 'Omniauth authentication', type: :feature do
visit my_account_path
# requires login, redirects to developer login which is why we see the login form now
SeleniumHubWaiter.wait
fill_in('first_name', with: user.firstname)
fill_in('last_name', with: user.lastname)
fill_in('email', with: user.mail)
@ -125,6 +130,7 @@ describe 'Omniauth authentication', type: :feature do
click_on 'here'
SeleniumHubWaiter.wait
fill_in('first_name', with: user.firstname)
fill_in('last_name', with: user.lastname)
fill_in('email', with: user.mail)
@ -139,6 +145,7 @@ describe 'Omniauth authentication', type: :feature do
visit '/'
click_link("Omniauth Developer", :match => :first)
SeleniumHubWaiter.wait
# login form developer strategy
fill_in('first_name', with: user.firstname)
# intentionally do not supply last_name
@ -148,6 +155,7 @@ describe 'Omniauth authentication', type: :feature do
expect(page).to have_content "Last name can't be blank"
# on register form, we are prompted for a last name
within('#content') do
SeleniumHubWaiter.wait
fill_in('user_lastname', with: user.lastname)
click_link_or_button 'Create'
end
@ -179,6 +187,7 @@ describe 'Omniauth authentication', type: :feature do
visit account_lost_password_path
click_link("Omniauth Developer", :match => :first)
SeleniumHubWaiter.wait
# login form developer strategy
fill_in('first_name', with: user.firstname)
# intentionally do not supply last_name
@ -187,6 +196,7 @@ describe 'Omniauth authentication', type: :feature do
# on register form, we are prompted for a last name
within('#content') do
SeleniumHubWaiter.wait
fill_in('user_lastname', with: user.lastname)
click_link_or_button 'Create'
end
@ -211,6 +221,7 @@ describe 'Omniauth authentication', type: :feature do
it 'shows a note explaining that the account has to be activated' do
visit login_path
SeleniumHubWaiter.wait
# login form developer strategy
fill_in 'first_name', with: 'Ifor'
fill_in 'last_name', with: 'McAlistar'
@ -254,6 +265,11 @@ describe 'Omniauth authentication', type: :feature do
# to a symbol will force omniauth to fail /auth/failure
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:developer] = :invalid_credentials
# seems like this default behaviour is removed when running the full
# test suite, so let's set it back when running this test
OmniAuth.config.on_failure = Proc.new { |env|
OmniAuth::FailureEndpoint.new(env).redirect_to_failure
}
visit login_path
expect(page).to have_content(I18n.t(:error_external_authentication_failed))

@ -14,6 +14,7 @@ describe 'custom fields', js: true do
cf_page.visit!
click_on "Create a new custom field"
SeleniumHubWaiter.wait
end
it "creates a new float custom field" do

@ -48,20 +48,17 @@ describe 'custom fields', js: true do
expect(page).to have_text("Successful creation")
SeleniumHubWaiter.wait
click_on "Operating System"
expect(page).to have_selector('.custom-option-row', count: 3)
values = all(".custom-option-value input")
expect(page).to have_field("custom_field_custom_options_attributes_0_value", with: "Solaris")
expect(page).to have_field("custom_field_custom_options_attributes_1_value", with: "Windows")
expect(page).to have_field("custom_field_custom_options_attributes_2_value", with: "Linux")
expect(values[0].value).to eql("Solaris")
expect(values[1].value).to eql("Windows")
expect(values[2].value).to eql("Linux")
defaults = all(".custom-option-default-value input")
expect(defaults[0]).not_to be_checked
expect(defaults[1]).to be_checked
expect(defaults[2]).not_to be_checked
expect(page).to have_field("custom_field_custom_options_attributes_0_default_value", checked: false)
expect(page).to have_field("custom_field_custom_options_attributes_1_default_value", checked: true)
expect(page).to have_field("custom_field_custom_options_attributes_2_default_value", checked: false)
end
end
@ -79,13 +76,16 @@ describe 'custom fields', js: true do
cf_page.visit!
expect_angular_frontend_initialized
SeleniumHubWaiter.wait
click_on custom_field.name
expect_angular_frontend_initialized
SeleniumHubWaiter.wait
end
it "adds new options" do
click_on "add-custom-option"
SeleniumHubWaiter.wait
expect(page).to have_selector('.custom-option-row', count: 5)
within all(".custom-option-row").last do
@ -93,6 +93,7 @@ describe 'custom fields', js: true do
end
click_on "add-custom-option"
SeleniumHubWaiter.wait
expect(page).to have_selector('.custom-option-row', count: 6)
within all(".custom-option-row").last do
@ -105,55 +106,40 @@ describe 'custom fields', js: true do
expect(page).to have_text("Platform")
expect(page).to have_selector('.custom-option-row', count: 6)
values = all(".custom-option-value input").map(&:value)
expect(values).to eq ["Playstation", "Xbox", "Nintendo", "PC", "Sega", "Atari"]
["Playstation", "Xbox", "Nintendo", "PC", "Sega", "Atari"].each_with_index do |value, i|
expect(page).to have_field("custom_field_custom_options_attributes_#{i}_value", with: value)
end
end
it "updates the values and orders of the custom options" do
expect(page).to have_text("Platform")
expect(page).to have_selector('.custom-option-row', count: 4)
rows = all(".custom-option-value input")
expect(rows[0].value).to eql("Playstation")
expect(rows[1].value).to eql("Xbox")
expect(rows[2].value).to eql("Nintendo")
expect(rows[3].value).to eql("PC")
rows[1].set "Sega"
find("#custom_field_multi_value").set true
defaults = all(".custom-option-default-value input")
defaults[0].set true
defaults[2].set true
["Playstation", "Xbox", "Nintendo", "PC"].each_with_index do |value, i|
expect(page).to have_field("custom_field_custom_options_attributes_#{i}_value", with: value)
end
fill_in("custom_field_custom_options_attributes_1_value", with: "Sega")
check("custom_field_multi_value")
check("custom_field_custom_options_attributes_0_default_value")
check("custom_field_custom_options_attributes_2_default_value")
within all(".custom-option-row").first do
click_on "Move to bottom"
end
click_on "Save"
expect(page).to have_text("Successful update")
expect(page).to have_text("Platform")
expect(page).to have_field("custom_field_multi_value", checked: true)
expect(find("#custom_field_multi_value")).to be_checked
new_rows = all(".custom-option-value input")
expect(new_rows[0].value).to eql("Sega")
expect(new_rows[1].value).to eql("Nintendo")
expect(new_rows[2].value).to eql("PC")
expect(new_rows[3].value).to eql("Playstation")
new_defaults = all(".custom-option-default-value input")
["Sega", "Nintendo", "PC", "Playstation"].each_with_index do |value, i|
expect(page).to have_field("custom_field_custom_options_attributes_#{i}_value", with: value)
end
expect(new_defaults[0]).not_to be_checked
expect(new_defaults[1]).to be_checked
expect(new_defaults[2]).not_to be_checked
expect(new_defaults[3]).to be_checked
expect(page).to have_field("custom_field_custom_options_attributes_0_default_value", checked: false)
expect(page).to have_field("custom_field_custom_options_attributes_1_default_value", checked: true)
expect(page).to have_field("custom_field_custom_options_attributes_2_default_value", checked: false)
expect(page).to have_field("custom_field_custom_options_attributes_3_default_value", checked: true)
end
context "with work packages using the options" do

@ -42,7 +42,7 @@ describe 'Upload attachment to forum message', js: true do
end
let(:project) { forum.project }
let(:attachments) { ::Components::Attachments.new }
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { ::Components::WysiwygEditor.new }
let(:index_page) { Pages::Messages::Index.new(forum.project) }
@ -58,7 +58,7 @@ describe 'Upload attachment to forum message', js: true do
create_page.set_subject 'A new message'
# adding an image
editor.drag_attachment image_fixture, 'Image uploaded on creation'
editor.drag_attachment image_fixture.path, 'Image uploaded on creation'
expect(page).to have_selector('attachment-list-item', text: 'image.png')
expect(page).not_to have_selector('notification-upload-progress')
@ -78,7 +78,7 @@ describe 'Upload attachment to forum message', js: true do
editor.type_slowly("A spacer text")
editor.drag_attachment image_fixture, 'Image uploaded the second time'
editor.drag_attachment image_fixture.path, 'Image uploaded the second time'
expect(page).to have_selector('attachment-list-item', text: 'image.png', count: 2)
expect(page).not_to have_selector('notification-upload-progress')

@ -55,10 +55,12 @@ describe 'messages', type: :feature, js: true do
create_page = index_page.click_create_message
SeleniumHubWaiter.wait
create_page.set_subject 'The message is'
create_page.click_save
create_page.expect_notification(type: :error, message: 'Content can\'t be blank')
SeleniumHubWaiter.wait
create_page.add_text 'There is no message here'
show_page = create_page.click_save

@ -64,16 +64,12 @@ describe 'Global role: Global role assignment', type: :feature, js: true do
expect(page).to have_text 'global_role2'
end
SeleniumHubWaiter.wait
# And I select the available global role "global_role"
check 'global_role2'
# And I press "Add"
click_on 'Add'
# Then I should see "global_role" within "#table_principal_roles"
page.within('#available_principal_roles') do
expect(page).to have_no_text 'global_role1'
expect(page).to have_no_text 'global_role2'
end
# And I should not see "global_role" within "#available_principal_roles"
# And I should see "There is currently nothing to display"
page.within('#table_principal_roles') do
@ -81,15 +77,22 @@ describe 'Global role: Global role assignment', type: :feature, js: true do
expect(page).to have_text 'global_role2'
end
# Then I should see "global_role" within "#table_principal_roles"
page.within('#available_principal_roles') do
expect(page).to have_no_text 'global_role1'
expect(page).to have_no_text 'global_role2'
end
# And I delete the assigned role "global_role"
page.within("#assigned_global_role_#{global_role1.id}") do
SeleniumHubWaiter.wait
page.find('.buttons a.icon-delete').click
end
# Then I should see "global_role" within "#table_principal_roles"
page.within('#available_principal_roles') do
expect(page).to have_text 'global_role1'
expect(page).to have_no_text 'global_role2'
expect(page).to have_text 'global_role1'
end
# And I should not see "global_role" within "#available_principal_roles"
# And I should see "There is currently nothing to display"

@ -53,9 +53,12 @@ feature 'group memberships through groups page', type: :feature, js: true do
expect(members_page).not_to have_user 'Hannibal Smith'
group_page.visit!
SeleniumHubWaiter.wait
group_page.add_to_project! 'Project 1', as: 'Manager'
expect(page).to have_text 'Successful update'
SeleniumHubWaiter.wait
group_page.add_user! 'Hannibal'
members_page.visit!
@ -75,6 +78,7 @@ feature 'group memberships through groups page', type: :feature, js: true do
expect(members_page).to have_user 'Hannibal Smith'
group_page.visit!
SeleniumHubWaiter.wait
group_page.remove_user! 'Hannibal Smith'
members_page.visit!

@ -116,6 +116,7 @@ feature 'group memberships through project members page', type: :feature do
expect(members_page).to have_user('group1') # the group is already there though
groups_page.visit!
SeleniumHubWaiter.wait
groups_page.add_user_to_group! 'Alice Wonderland', 'group1'
members_page.visit!

@ -54,10 +54,12 @@ feature 'group memberships through groups page', type: :feature, js: true do
shared_examples 'adding and removing principals' do
scenario 'Adding and Removing a Group as Member' do
members_page.visit!
SeleniumHubWaiter.wait
members_page.add_user! 'A-Team', as: 'Manager'
expect(members_page).to have_added_group('A-Team')
SeleniumHubWaiter.wait
members_page.remove_group! 'A-Team'
expect(page).to have_text 'Removed A-Team from project'
expect(page).to have_text 'There are currently no members part of this project.'
@ -65,10 +67,12 @@ feature 'group memberships through groups page', type: :feature, js: true do
scenario 'Adding and removing a User as Member' do
members_page.visit!
SeleniumHubWaiter.wait
members_page.add_user! 'Hannibal Smith', as: 'Manager'
expect(members_page).to have_added_user 'Hannibal Smith'
SeleniumHubWaiter.wait
members_page.remove_user! 'Hannibal Smith'
expect(page).to have_text 'Removed Hannibal Smith from project'
expect(page).to have_text 'There are currently no members part of this project.'
@ -76,23 +80,29 @@ feature 'group memberships through groups page', type: :feature, js: true do
scenario 'Entering a Username as Member in firstname, lastname order' do
members_page.visit!
SeleniumHubWaiter.wait
members_page.open_new_member!
SeleniumHubWaiter.wait
members_page.search_principal! 'Hannibal S'
expect(members_page).to have_search_result 'Hannibal Smith'
end
scenario 'Entering a Username as Member in lastname, firstname order' do
members_page.visit!
SeleniumHubWaiter.wait
members_page.open_new_member!
SeleniumHubWaiter.wait
members_page.search_principal! 'Smith, H'
expect(members_page).to have_search_result 'Hannibal Smith'
end
scenario 'Escaping should work properly when entering a name' do
members_page.visit!
SeleniumHubWaiter.wait
members_page.open_new_member!
SeleniumHubWaiter.wait
members_page.search_principal! 'script'
expect(members_page).not_to have_alert_dialog
@ -108,19 +118,23 @@ feature 'group memberships through groups page', type: :feature, js: true do
scenario 'sorting the page' do
members_page.visit!
SeleniumHubWaiter.wait
members_page.sort_by 'last name'
members_page.expect_sorted_by 'last name'
expect(members_page.contents('lastname')).to eq ['', peter.lastname, hannibal.lastname]
SeleniumHubWaiter.wait
members_page.sort_by 'last name'
members_page.expect_sorted_by 'last name', desc: true
expect(members_page.contents('lastname')).to eq [hannibal.lastname, peter.lastname, '']
SeleniumHubWaiter.wait
members_page.sort_by 'first name'
members_page.expect_sorted_by 'first name'
expect(members_page.contents('firstname')).to eq ['', hannibal.firstname, peter.firstname]
SeleniumHubWaiter.wait
members_page.sort_by 'email'
members_page.expect_sorted_by 'email'
expect(members_page.contents('email')).to eq ['', hannibal.mail, peter.mail]

@ -52,8 +52,10 @@ feature 'members pagination', type: :feature, js: true do
members_page.set_items_per_page! 2
members_page.visit!
SeleniumHubWaiter.wait
members_page.add_user! 'Peter Pan', as: 'Manager'
SeleniumHubWaiter.wait
members_page.go_to_page! 2
expect(members_page).to have_user 'Alice Alison' # members are sorted by last name desc
end
@ -63,9 +65,11 @@ feature 'members pagination', type: :feature, js: true do
members_page.set_items_per_page! 1
members_page.visit!
SeleniumHubWaiter.wait
members_page.remove_user! 'Peter Pan'
expect(members_page).to have_user 'Bob Bobbit'
SeleniumHubWaiter.wait
members_page.go_to_page! 2
expect(members_page).to have_user 'Alice Alison'
end
@ -74,10 +78,12 @@ feature 'members pagination', type: :feature, js: true do
members_page.set_items_per_page! 1
members_page.visit!
SeleniumHubWaiter.wait
members_page.edit_user! 'Bob Bobbit', add_roles: ['Developer']
expect(page).to have_text 'Successful update'
expect(members_page).to have_user 'Bob Bobbit', roles: ['Developer', 'Manager']
SeleniumHubWaiter.wait
members_page.go_to_page! 2
expect(members_page).to have_user 'Alice Alison'
end

@ -92,9 +92,11 @@ feature 'Wiki menu items' do
visit project_wiki_path(project, wiki_page)
# creating the menu item with the pages name for the menu item
SeleniumHubWaiter.wait
click_link 'More'
click_link 'Configure menu item'
SeleniumHubWaiter.wait
choose "Show as menu item in project navigation"
click_button "Save"
@ -102,12 +104,14 @@ feature 'Wiki menu items' do
expect(page)
.to have_selector('.main-menu--children-menu-header', text: wiki_page.title)
SeleniumHubWaiter.wait
find('.main-menu--arrow-left-to-project').click
expect(page)
.to have_selector('.main-item-wrapper', text: wiki_page.title)
# clicking the menu item leads to the page
SeleniumHubWaiter.wait
click_link wiki_page.title
expect(page)
@ -115,9 +119,11 @@ feature 'Wiki menu items' do
# modifying the menu item to a different name and to be a subpage
SeleniumHubWaiter.wait
click_link 'More'
click_link 'Configure menu item'
SeleniumHubWaiter.wait
fill_in 'Name of menu item', with: 'Custom page name'
choose "Show as submenu item of"
@ -133,12 +139,14 @@ feature 'Wiki menu items' do
expect(page)
.to have_selector('.wiki-menu--sub-item', text: 'Custom page name')
SeleniumHubWaiter.wait
click_link 'Custom page name'
expect(page)
.to have_current_path(project_wiki_path(project, wiki_page))
# the submenu item is not visible on top level
SeleniumHubWaiter.wait
find('.main-menu--arrow-left-to-project').click
expect(page)
@ -169,6 +177,7 @@ feature 'Wiki menu items' do
click_button 'Save'
# Because it is the last wiki menu item, the user is prompted to select another menu item
SeleniumHubWaiter.wait
select another_wiki_page.title, from: 'main-menu-item-select'
click_button 'Save'

@ -43,6 +43,7 @@ describe 'My notifications spec', type: :feature, js: true do
visit my_account_path
click_on 'Email notifications'
SeleniumHubWaiter.wait
end
it 'allows to select a project to receive notifications for (Regression #28519)' do
@ -53,6 +54,8 @@ describe 'My notifications spec', type: :feature, js: true do
find("#notified_project_ids_#{project.id}", wait: 5).set true
click_on 'Save'
SeleniumHubWaiter.wait
expect(page).to have_selector('.flash.notice')
user.reload

@ -68,6 +68,7 @@ describe 'OAuth authorization code flow',
forms
end
SeleniumHubWaiter.wait
# Authorize
find('input.button[value="Authorize"]').click
@ -105,6 +106,7 @@ describe 'OAuth authorization code flow',
# Revoke the application
within("#oauth-application-grant-#{app.id}") do
SeleniumHubWaiter.wait
click_on 'Revoke'
end

@ -47,6 +47,7 @@ describe 'onboarding tour for new users', js: true do
visit home_path first_time_user: true
expect(page).to have_text 'Please select your language'
# SeleniumHubWaiter.wait
select 'Deutsch', :from => 'user_language'
click_button 'Save'
@ -58,13 +59,14 @@ describe 'onboarding tour for new users', js: true do
allow(Setting).to receive(:welcome_text).and_return("<a> #{project.name} </a>")
visit home_path first_time_user: true
# SeleniumHubWaiter.wait
select 'English', :from => 'user_language'
click_button 'Save'
end
it 'when the welcome block does not include the demo projects' do
expect(page).not_to have_text 'Take a three minutes introduction tour to learn the most important features.'
expect(page).not_to have_selector '.enjoyhint_next_btn'
expect(page).to have_no_text 'Take a three minutes introduction tour to learn the most important features.'
expect(page).to have_no_selector '.enjoyhint_next_btn'
end
end
@ -73,8 +75,10 @@ describe 'onboarding tour for new users', js: true do
allow(Setting).to receive(:welcome_text).and_return("<a href=/projects/#{project.identifier}> #{project.name} </a><a href=/projects/#{scrum_project.identifier}> #{scrum_project.name} </a>")
visit home_path first_time_user: true
# SeleniumHubWaiter.wait
select 'English', :from => 'user_language'
click_button 'Save'
SeleniumHubWaiter.wait
end
after do
@ -92,20 +96,21 @@ describe 'onboarding tour for new users', js: true do
find('.enjoyhint_skip_btn').click
# The tutorial disappears
expect(page).not_to have_text 'Take a three minutes introduction tour to learn the most important features.'
expect(page).not_to have_selector '.enjoyhint_next_btn'
expect(page).to have_no_text 'Take a three minutes introduction tour to learn the most important features.'
expect(page).to have_no_selector '.enjoyhint_next_btn'
page.driver.browser.navigate.refresh
# The tutorial did not start again
expect(page).not_to have_text 'Take a three minutes introduction tour to learn the most important features.'
expect(page).not_to have_selector '.enjoyhint_next_btn'
expect(page).to have_no_text 'Take a three minutes introduction tour to learn the most important features.'
expect(page).to have_no_selector '.enjoyhint_next_btn'
end
it 'and I continue the tutorial' do
next_button.click
expect(page).to have_text 'Please click on one of the projects with useful demo data to get started'
# SeleniumHubWaiter.wait
find('.welcome').click_link 'Demo project'
expect(page).to have_current_path "/projects/#{project.identifier}/work_packages?start_onboarding_tour=true"

@ -251,6 +251,7 @@ describe 'Projects index page',
scenario 'it keeps applying filters and order' do
load_and_open_filters admin
SeleniumHubWaiter.wait
projects_page.set_filter('name_and_identifier',
'Name or identifier',
'doesn\'t contain',
@ -259,43 +260,48 @@ describe 'Projects index page',
click_on 'Apply'
# Sorts ASC by name
SeleniumHubWaiter.wait
click_on 'Sort by "Name"'
# Results should be filtered and ordered ASC by name
expect(page).to have_text(development_project.name)
expect(page).to_not have_text(project.name) # as it filtered away
expect(page).to have_no_text(project.name) # as it filtered away
expect(page).to have_text('Next') # as the result set is larger than 1
expect(page).to_not have_text(public_project.name) # as it is on the second page
expect(page).to have_no_text(public_project.name) # as it is on the second page
# Changing the page size to 5 and back to 1 should not change the filters (which we test later on the second page)
SeleniumHubWaiter.wait
find('.pagination--options .pagination--item', text: '5').click # click page size '5'
SeleniumHubWaiter.wait
find('.pagination--options .pagination--item', text: '1').click # return back to page size '1'
SeleniumHubWaiter.wait
click_on '2' # Go to pagination page 2
# On page 2 you should see the second page of the filtered set ordered ASC by name
expect(page).to have_text(public_project.name)
expect(page).to_not have_text(project.name) # Filtered away
expect(page).to_not have_text('Next') # Filters kept active, so there is no third page.
expect(page).to_not have_text(development_project.name) # That one should be on page 1
expect(page).to have_no_text(project.name) # Filtered away
expect(page).to have_no_text('Next') # Filters kept active, so there is no third page.
expect(page).to have_no_text(development_project.name) # That one should be on page 1
# Sorts DESC by name
SeleniumHubWaiter.wait
click_on 'Ascending sorted by "Name"'
# On page 2 the same filters should still be intact but the order should be DESC on name
expect(page).to have_text(development_project.name)
expect(page).to_not have_text(project.name) # Filtered away
expect(page).to_not have_text('Next') # Filters kept active, so there is no third page.
expect(page).to_not have_text(public_project.name) # That one should be on page 1
expect(page).to have_no_text(project.name) # Filtered away
expect(page).to have_no_text('Next') # Filters kept active, so there is no third page.
expect(page).to have_no_text(public_project.name) # That one should be on page 1
# Sending the filter form again what implies to compose the request freshly
SeleniumHubWaiter.wait
click_on 'Apply'
# We should see page 1, resetting pagination, as it is a new filter, but keeping the DESC order on the project
# name
expect(page).to have_text(public_project.name)
expect(page).to_not have_text(development_project.name) # as it is on the second page
expect(page).to_not have_text(project.name) # as it filtered away
expect(page).to have_no_text(development_project.name) # as it is on the second page
expect(page).to have_no_text(project.name) # as it filtered away
expect(page).to have_text('Next') # as the result set is larger than 1
end
end
@ -305,6 +311,7 @@ describe 'Projects index page',
load_and_open_filters admin
# Filter on model attribute 'name'
SeleniumHubWaiter.wait
projects_page.set_filter('name_and_identifier',
'Name or identifier',
'doesn\'t contain',
@ -314,9 +321,10 @@ describe 'Projects index page',
expect(page).to have_text(development_project.name)
expect(page).to have_text(public_project.name)
expect(page).to_not have_text(project.name)
expect(page).to have_no_text(project.name)
# Filter on model attribute 'identifier'
SeleniumHubWaiter.wait
remove_filter('name_and_identifier')
projects_page.set_filter('name_and_identifier',
@ -327,8 +335,8 @@ describe 'Projects index page',
click_on 'Apply'
expect(page).to have_text(project.name)
expect(page).to_not have_text(development_project.name)
expect(page).to_not have_text(public_project.name)
expect(page).to have_no_text(development_project.name)
expect(page).to have_no_text(public_project.name)
end
feature 'Active or archived' do
@ -356,6 +364,7 @@ describe 'Projects index page',
expect(page).to have_text('Development project')
expect(page).to have_text('Public project')
SeleniumHubWaiter.wait
projects_page.click_menu_item_of('Archive', parent_project)
projects_page.accept_alert_dialog!
@ -375,6 +384,7 @@ describe 'Projects index page',
load_and_open_filters admin
SeleniumHubWaiter.wait
projects_page.filter_by_active('no')
expect(page).to have_text("ARCHIVED #{parent_project.name}")
@ -389,6 +399,7 @@ describe 'Projects index page',
expect(menu).to_not have_text('Settings')
expect(menu).to_not have_text('New subproject')
SeleniumHubWaiter.wait
click_link('Unarchive')
end
@ -401,6 +412,7 @@ describe 'Projects index page',
load_and_open_filters admin
SeleniumHubWaiter.wait
projects_page.filter_by_active('yes')
expect(page).to have_text(parent_project.name)
@ -435,16 +447,19 @@ describe 'Projects index page',
login_as(admin)
projects_page.visit!
SeleniumHubWaiter.wait
click_link('Sort by "Status"')
expect_project_at_place(green_project, 1)
expect(page).to have_text('(1 - 8/8)')
SeleniumHubWaiter.wait
click_link('Ascending sorted by "Status"')
expect_project_at_place(green_project, 8)
expect(page).to have_text('(1 - 8/8)')
SeleniumHubWaiter.wait
projects_page.open_filters
projects_page.set_filter('project_status_code',
@ -458,6 +473,7 @@ describe 'Projects index page',
expect(page).to_not have_text(gray_project.name)
expect(page).to_not have_text(no_status_project.name)
SeleniumHubWaiter.wait
projects_page.set_filter('project_status_code',
'Project status',
'all',
@ -469,6 +485,7 @@ describe 'Projects index page',
expect(page).to_not have_text(gray_project.name)
expect(page).to_not have_text(no_status_project.name)
SeleniumHubWaiter.wait
projects_page.set_filter('project_status_code',
'Project status',
'none',
@ -540,6 +557,7 @@ describe 'Projects index page',
allow_enterprise_edition
project_created_on_today
load_and_open_filters admin
SeleniumHubWaiter.wait
end
scenario 'selecting operator' do
@ -555,6 +573,7 @@ describe 'Projects index page',
expect(page).to_not have_text(project_created_on_fixed_date.name)
# created on 'this week' shows projects that were created within the last seven days
SeleniumHubWaiter.wait
remove_filter('created_at')
projects_page.set_filter('created_at',
@ -568,6 +587,7 @@ describe 'Projects index page',
expect(page).to_not have_text(project_created_on_fixed_date.name)
# created on 'on' shows projects that were created within the last seven days
SeleniumHubWaiter.wait
remove_filter('created_at')
projects_page.set_filter('created_at',
@ -582,6 +602,7 @@ describe 'Projects index page',
expect(page).to_not have_text(project_created_on_this_week.name)
# created on 'less than days ago'
SeleniumHubWaiter.wait
remove_filter('created_at')
projects_page.set_filter('created_at',
@ -595,6 +616,7 @@ describe 'Projects index page',
expect(page).to_not have_text(project_created_on_fixed_date.name)
# created on 'more than days ago'
SeleniumHubWaiter.wait
remove_filter('created_at')
projects_page.set_filter('created_at',
@ -608,6 +630,7 @@ describe 'Projects index page',
expect(page).to_not have_text(project_created_on_today.name)
# created on 'between'
SeleniumHubWaiter.wait
remove_filter('created_at')
projects_page.set_filter('created_at',
@ -621,6 +644,7 @@ describe 'Projects index page',
expect(page).to_not have_text(project_created_on_today.name)
# Latest activity at 'today'. This spot check would fail if the data does not get collected from multiple tables
SeleniumHubWaiter.wait
remove_filter('created_at')
projects_page.set_filter('latest_activity_at',
@ -633,6 +657,7 @@ describe 'Projects index page',
expect(page).to_not have_text(project_created_on_fixed_date.name)
# CF List
SeleniumHubWaiter.wait
remove_filter('latest_activity_at')
projects_page.set_filter("cf_#{list_custom_field.id}",
@ -650,11 +675,13 @@ describe 'Projects index page',
within(cf_filter) do
# Initial filter is a 'single select'
expect(cf_filter.find(:select, 'value')).not_to be_multiple
SeleniumHubWaiter.wait
click_on 'Toggle multiselect'
# switching to multiselect keeps the current selection
expect(cf_filter.find(:select, 'value')).to be_multiple
expect(cf_filter).to have_select('value', selected: list_custom_field.possible_values[2].value)
SeleniumHubWaiter.wait
select list_custom_field.possible_values[3].value, from: 'value'
end
@ -670,6 +697,7 @@ describe 'Projects index page',
list_custom_field.possible_values[3].value])
# switching to single select keeps the first selection
SeleniumHubWaiter.wait
select list_custom_field.possible_values[1].value, from: 'value'
unselect list_custom_field.possible_values[2].value, from: 'value'
@ -688,6 +716,7 @@ describe 'Projects index page',
end
# CF date filter work (at least for one operator)
SeleniumHubWaiter.wait
remove_filter("cf_#{list_custom_field.id}")
projects_page.set_filter("cf_#{date_custom_field.id}",
@ -695,10 +724,11 @@ describe 'Projects index page',
'on',
['2011-11-11'])
SeleniumHubWaiter.wait
click_on 'Apply'
expect(page).to have_text(project_created_on_today.name)
expect(page).to_not have_text(project_created_on_fixed_date.name)
expect(page).to have_no_text(project_created_on_fixed_date.name)
end
pending "NOT WORKING YET: Date vs. DateTime issue: Selecting same date for from and to value shows projects of that date"
@ -862,6 +892,7 @@ describe 'Projects index page',
child_project_z,
public_project)
SeleniumHubWaiter.wait
click_link('Name')
# Projects ordered by name asc
@ -872,6 +903,7 @@ describe 'Projects index page',
public_project,
child_project_z)
SeleniumHubWaiter.wait
click_link('Name')
# Projects ordered by name desc
@ -882,6 +914,7 @@ describe 'Projects index page',
development_project,
child_project_a)
SeleniumHubWaiter.wait
click_link(integer_custom_field.name)
# Projects ordered by cf asc first then project name desc
@ -892,6 +925,7 @@ describe 'Projects index page',
child_project_m,
child_project_a)
SeleniumHubWaiter.wait
click_link('Sort by "Project hierarchy"')
# again ordered by name asc on each hierarchical level

@ -76,7 +76,9 @@ describe 'Projects', type: :feature do
it 'can create a subproject' do
click_on 'Foo project'
SeleniumHubWaiter.wait
click_on 'Project settings'
SeleniumHubWaiter.wait
click_on 'New subproject'
fill_in 'project[name]', with: 'Foo child'
@ -152,7 +154,9 @@ describe 'Projects', type: :feature do
it 'updates the project identifier' do
visit projects_path
click_on project.name
SeleniumHubWaiter.wait
click_on 'Project settings'
SeleniumHubWaiter.wait
click_on 'Edit'
expect(page).to have_content "CHANGE THE PROJECT'S IDENTIFIER"

@ -79,7 +79,9 @@ describe 'Create repository', type: :feature, js: true, selenium: true do
describe 'with submitted vendor form' do
before do
settings_page.visit_repository_settings
SeleniumHubWaiter.wait
find("option[value='#{vendor}']").select_option
SeleniumHubWaiter.wait
end
shared_examples 'has only the type which is selected' do |type, vendor|

@ -63,6 +63,7 @@ describe 'Repository Settings', type: :feature, js: true do
expect(Repository.exists?(repository.id)).to be true
if type == 'managed'
SeleniumHubWaiter.wait
find('a.icon-delete', text: I18n.t(:button_delete)).click
dangerzone = DangerZone.new(page)
@ -70,16 +71,21 @@ describe 'Repository Settings', type: :feature, js: true do
expect(page).to have_selector(dangerzone.container_selector)
expect(dangerzone.disabled?).to be true
SeleniumHubWaiter.wait
dangerzone.confirm_with('definitely not the correct value')
expect(dangerzone.disabled?).to be true
SeleniumHubWaiter.wait
dangerzone.confirm_with(project.identifier)
expect(dangerzone.disabled?).to be false
SeleniumHubWaiter.wait
dangerzone.danger_button.click
else
SeleniumHubWaiter.wait
find('a.icon-remove', text: I18n.t(:button_remove)).click
expect(page).to have_selector('.notification-box.-warning')
SeleniumHubWaiter.wait
find('a', text: I18n.t(:button_remove)).click
end

@ -48,6 +48,7 @@ describe 'Role creation', type: :feature, js: true do
click_link 'Role'
end
SeleniumHubWaiter.wait
fill_in 'Name', with: existing_role.name
select existing_role.name, from: 'Copy workflow from'
check 'Edit work packages'
@ -58,6 +59,7 @@ describe 'Role creation', type: :feature, js: true do
expect(page)
.to have_selector('.errorExplanation', text: 'Name has already been taken')
SeleniumHubWaiter.wait
fill_in 'Name', with: 'New role name'
# This will lead to an error as manage versions requires view versions
@ -69,6 +71,7 @@ describe 'Role creation', type: :feature, js: true do
.to have_selector('.errorExplanation',
text: "Permissions need to also include 'View members' as 'Manage members' is selected.")
SeleniumHubWaiter.wait
check 'View members'
select existing_role.name, from: 'Copy workflow from'
@ -83,6 +86,7 @@ describe 'Role creation', type: :feature, js: true do
expect(page)
.to have_selector('table td', text: 'New role name')
SeleniumHubWaiter.wait
click_link 'New role name'
expect(page)
@ -109,6 +113,7 @@ describe 'Role creation', type: :feature, js: true do
# Workflow routes are not resource-oriented.
visit(url_for(controller: :workflows, action: :edit, only_path: true))
SeleniumHubWaiter.wait
select 'New role name', from: 'Role'
select type.name, from: 'Type'
click_button 'Edit'
@ -116,9 +121,6 @@ describe 'Role creation', type: :feature, js: true do
from_id = existing_workflow.old_status_id
to_id = existing_workflow.new_status_id
checkbox = page.find("input.old-status-#{from_id}.new-status-#{to_id}[value=always]")
expect(checkbox)
.to be_checked
expect(page).to have_field("status_#{from_id}_#{to_id}_", checked: true)
end
end

@ -304,8 +304,8 @@ describe 'Search', type: :feature, js: true, with_settings: { per_page_options:
table.expect_work_package_subject(work_packages[0].subject)
# ... for type: string
global_search.search custom_field_string_value, submit: true
table.ensure_work_package_not_listed! work_packages[0]
table.expect_work_package_subject(work_packages[1].subject)
table.ensure_work_package_not_listed! work_packages[0]
# Change to project scope to include subprojects
global_search.search other_work_package.subject
@ -420,23 +420,23 @@ describe 'Search', type: :feature, js: true, with_settings: { per_page_options:
global_search.expect_global_scope_marked
global_search.submit_in_global_scope
table.ensure_work_package_not_listed! wp_2
table.expect_work_package_listed(wp_1, wp_3)
table.ensure_work_package_not_listed! wp_2
global_search.search "# Bar"
global_search.find_option "Foo # Bar"
global_search.find_option "Foo &# Bar"
global_search.submit_in_global_scope
table.ensure_work_package_not_listed! wp_1
table.expect_work_package_listed(wp_2)
table.ensure_work_package_not_listed! wp_1
global_search.search "&"
# Bug in ng-select causes highlights to break up entities
global_search.find_option "Foo && Bar"
global_search.find_option "Foo &# Bar"
global_search.submit_in_global_scope
table.ensure_work_package_not_listed! wp_2
table.expect_work_package_listed(wp_1, wp_3)
table.ensure_work_package_not_listed! wp_2
end
end
end

@ -51,6 +51,7 @@ module Components
end
def open!
SeleniumHubWaiter.wait
container.find(".help-text--for-#{help_text.attribute_name}").click
expect(page).to have_selector('.attribute-help-text--modal h3', text: help_text.attribute_caption)
end

@ -63,12 +63,13 @@ describe 'Reset form configuration', type: :feature, js: true do
form.save_changes
expect(page).to have_selector('.flash.notice', text: 'Successful update.', wait: 10)
SeleniumHubWaiter.wait
form.reset_button.click
dialog.expect_open
dialog.confirm
# Wait for page reload
sleep 1
SeleniumHubWaiter.wait
expect(page).to have_no_selector('.group-head', text: 'NEW GROUP')
expect(page).to have_no_selector('.group-head', text: 'OTHER')

@ -107,11 +107,14 @@ describe 'user deletion: ', type: :feature, js: true do
expect(page).to have_content "#{user.firstname} #{user.lastname}"
click_on 'Delete'
SeleniumHubWaiter.wait
fill_in 'login_verification', with: user.login
click_on 'Delete'
dialog.confirm_flow_with 'wrong', should_fail: true
SeleniumHubWaiter.wait
fill_in 'login_verification', with: user.login
click_on 'Delete'

@ -62,14 +62,13 @@ describe 'user self registration', type: :feature, js: true do
within '.top-menu-items-right .menu_root' do
click_link 'Sign in'
# Wait until click handler has been initialized
sleep(0.1)
SeleniumHubWaiter.wait
click_link 'Create a new account'
end
# deliberately inserting a wrong password confirmation
within '.registration-modal' do
SeleniumHubWaiter.wait
fill_in 'Username', with: 'heidi'
fill_in 'First name', with: 'Heidi'
fill_in 'Last name', with: 'Switzerland'
@ -87,6 +86,7 @@ describe 'user self registration', type: :feature, js: true do
within '.registration-modal' do
# Cannot use 'Password' here as the error message on 'Confirmation' is part of the label
# and contains the string 'Password' as well
SeleniumHubWaiter.wait
fill_in 'user_password', with: 'test123=321test'
fill_in 'Confirmation', with: 'test123=321test'

@ -46,9 +46,11 @@ feature 'user memberships through user page', type: :feature, js: true do
end
scenario 'handles role modification flow' do
SeleniumHubWaiter.wait
user_page.add_to_project! project.name, as: 'Manager'
member = admin.memberships.where(project_id: project.id).first
SeleniumHubWaiter.wait
user_page.edit_roles!(member, %w(Manager Developer))
# Modify roles
@ -59,6 +61,7 @@ feature 'user memberships through user page', type: :feature, js: true do
# Remove all roles
user_page.expect_project(project.name)
SeleniumHubWaiter.wait
user_page.edit_roles!(member, %w())
expect(page).to have_selector('.flash.error', text: 'Roles need to be assigned.')
@ -68,6 +71,7 @@ feature 'user memberships through user page', type: :feature, js: true do
user_page.expect_no_membership(project.name)
# Re-add the user
SeleniumHubWaiter.wait
user_page.add_to_project! project.name, as: %w(Manager Developer)
user_page.expect_project(project.name)
@ -86,6 +90,7 @@ feature 'user memberships through user page', type: :feature, js: true do
user_page.expect_no_membership(project.name)
group_page.visit!
SeleniumHubWaiter.wait
group_page.add_to_project! project.name, as: 'Manager'
expect(page).to have_text 'Successful update'
@ -98,6 +103,7 @@ feature 'user memberships through user page', type: :feature, js: true do
# Remove all roles
member = admin.memberships.where(project_id: project.id).first
SeleniumHubWaiter.wait
user_page.edit_roles!(member, %w())
# Keeps inherited role
@ -105,6 +111,7 @@ feature 'user memberships through user page', type: :feature, js: true do
user_page.expect_roles(project.name, %w(Manager))
# Extend roles
SeleniumHubWaiter.wait
user_page.edit_roles!(member, %w(Developer))
user_page.expect_project(project.name)
user_page.expect_roles(project.name, %w(Manager Developer))

@ -28,7 +28,7 @@
require 'spec_helper'
describe 'version create', type: :feature, js: true do
describe 'version create', type: :feature, js: false do
let(:user) do
FactoryBot.create(:user,
member_in_project: project,
@ -55,13 +55,12 @@ describe 'version create', type: :feature, js: true do
it 'and redirect back to where you started' do
visit project_roadmap_path(project)
click_on 'New version'
expect(page).to have_current_path(new_project_version_path(project))
fill_in 'Name', with: new_version_name
click_on 'Create'
expect(page).to have_text("Successful creation")
expect(page).to have_current_path(project_roadmap_path(project))
expect(page).to have_content new_version_name
end

@ -49,13 +49,11 @@ describe 'Toggle watching', type: :feature, js: true do
topic_path(message),
project_wiki_path(project, wiki_page)].each do |path|
visit path
click_link(I18n.t('button_watch'))
expect(page).to have_link(I18n.t('button_unwatch'))
SeleniumHubWaiter.wait
click_link(I18n.t('button_unwatch'))
expect(page).to have_link(I18n.t('button_watch'))
end
end

@ -79,19 +79,23 @@ describe 'wiki pages', type: :feature, js: true do
expect(page).to have_selector('.wiki-content', text: content_first_version)
within '.toolbar-items' do
SeleniumHubWaiter.wait
click_on "Edit"
end
find('.ck-content').set(content_second_version)
SeleniumHubWaiter.wait
click_button 'Save'
expect(page).to have_selector('.wiki-content', text: content_second_version)
within '.toolbar-items' do
SeleniumHubWaiter.wait
click_on 'More'
click_on 'History'
end
SeleniumHubWaiter.wait
click_on 'View differences'
within '.text-diff' do
@ -99,16 +103,19 @@ describe 'wiki pages', type: :feature, js: true do
expect(page).to have_selector('del.diffmod', text: 'first')
end
SeleniumHubWaiter.wait
# Go back to history
find('.button', text: 'History').click
# Click on first version
# to determine text (Regression test #31531)
SeleniumHubWaiter.wait
find('td.id a', text: 1).click
expect(page).to have_selector('.wiki-version--details', text: 'Version 1/2')
expect(page).to have_selector('.wiki-content', text: content_first_version)
SeleniumHubWaiter.wait
find('.button', text: 'Next').click
expect(page).to have_selector('.wiki-version--details', text: 'Version 2/2')

@ -39,7 +39,7 @@ describe 'Upload attachment to wiki page', js: true do
end
let(:project) { FactoryBot.create(:project) }
let(:attachments) { ::Components::Attachments.new }
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { ::Components::WysiwygEditor.new }
let(:wiki_page_content) { project.wiki.pages.first.content.text }
@ -51,26 +51,30 @@ describe 'Upload attachment to wiki page', js: true do
visit project_wiki_path(project, 'test')
# adding an image
editor.drag_attachment image_fixture, 'Image uploaded the first time'
editor.drag_attachment image_fixture.path, 'Image uploaded the first time'
expect(page).to have_selector('attachment-list-item', text: 'image.png')
expect(page).not_to have_selector('notification-upload-progress')
click_on 'Save'
expect(page).to have_text("Successful creation")
expect(page).to have_selector('#content img', count: 1)
expect(page).to have_content('Image uploaded the first time')
expect(page).to have_selector('attachment-list-item', text: 'image.png')
# required sleep otherwise clicking on the Edit button doesn't do anything
SeleniumHubWaiter.wait
within '.toolbar-items' do
click_on "Edit"
end
# Replace the image with a named attachment URL (Regression #28381)
expect(page).to have_selector('.ck-editor__editable')
expect(page).to have_selector('.ck-editor__editable', wait: 5)
editor.set_markdown "\n\nSome text\n![my-first-image](image.png)\n\nText that prevents the two images colliding"
editor.drag_attachment image_fixture, 'Image uploaded the second time'
editor.drag_attachment image_fixture.path, 'Image uploaded the second time'
expect(page).not_to have_selector('notification-upload-progress')
expect(page).to have_selector('attachment-list-item', text: 'image.png', count: 2)
@ -89,6 +93,7 @@ describe 'Upload attachment to wiki page', js: true do
click_on 'Save'
expect(page).to have_text("Successful update")
expect(page).to have_selector('#content img', count: 2)
# First figcaption is lost by having replaced the markdown
expect(page).to have_content('Image uploaded the second time')
@ -109,7 +114,7 @@ describe 'Upload attachment to wiki page', js: true do
# Upload image to dropzone
expect(page).to have_no_selector('.work-package--attachments--filename')
attachments.attach_file_on_input(image_fixture)
attachments.attach_file_on_input(image_fixture.path)
expect(page).not_to have_selector('notification-upload-progress')
expect(page).to have_selector('.work-package--attachments--filename', text: 'image.png')

@ -56,6 +56,7 @@ describe 'wiki child pages', type: :feature, js: true do
click_on 'Wiki page'
SeleniumHubWaiter.wait
fill_in 'content_page_title', with: child_page_name
find('.ck-content').set('The child page\'s content')

@ -49,9 +49,11 @@ describe 'Wiki page', type: :feature, js: true do
it 'allows renaming' do
visit project_wiki_path(project, wiki_page)
SeleniumHubWaiter.wait
click_link 'More'
click_link 'Rename'
SeleniumHubWaiter.wait
fill_in 'Title', with: rename_name
click_button 'Rename'

@ -18,7 +18,7 @@ describe 'Upload attachment to work package', js: true do
let(:wp_page) { ::Pages::FullWorkPackage.new(work_package, project) }
let(:attachments) { ::Components::Attachments.new }
let(:field) { TextEditorField.new wp_page, 'description' }
let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') }
let(:image_fixture) { UploadedFile.load_from('spec/fixtures/files/image.png') }
let(:editor) { Components::WysiwygEditor.new }
before do
@ -40,7 +40,7 @@ describe 'Upload attachment to work package', js: true do
editor.expect_button 'Insert image'
editor.drag_attachment image_fixture, 'Some image caption'
editor.drag_attachment image_fixture.path, 'Some image caption'
field.submit_by_click
@ -97,7 +97,7 @@ describe 'Upload attachment to work package', js: true do
subject.set_value 'My subject'
target = find('.ck-content')
attachments.drag_and_drop_file(target, image_fixture)
attachments.drag_and_drop_file(target, image_fixture.path)
sleep 2
expect(page).not_to have_selector('notification-upload-progress')
@ -144,7 +144,7 @@ describe 'Upload attachment to work package', js: true do
# everywhere so if this works it should work everywhere else too.
context 'with direct uploads', with_direct_uploads: true do
before do
allow_any_instance_of(Attachment).to receive(:diskfile).and_return Struct.new(:path).new(image_fixture.to_s)
allow_any_instance_of(Attachment).to receive(:diskfile).and_return Struct.new(:path).new(image_fixture.path.to_s)
end
it_behaves_like 'it supports image uploads via drag & drop' do
@ -170,13 +170,13 @@ describe 'Upload attachment to work package', js: true do
##
# Attach file manually
expect(page).to have_no_selector('.work-package--attachments--filename')
attachments.attach_file_on_input(image_fixture)
attachments.attach_file_on_input(image_fixture.path)
expect(page).not_to have_selector('notification-upload-progress')
expect(page).to have_selector('.work-package--attachments--filename', text: 'image.png', wait: 5)
##
# and via drag & drop
attachments.drag_and_drop_file(container, Rails.root.join('spec/fixtures/files/image.png'))
attachments.drag_and_drop_file(container, image_fixture.path)
expect(page).not_to have_selector('notification-upload-progress')
expect(page).to have_selector('.work-package--attachments--filename', text: 'image.png', count: 2, wait: 5)
end

@ -144,7 +144,7 @@ describe 'Switching work package view',
expect(url).not_to match(/query_props=.+/)
# Since the query is unchanged, the WPs will be displayed as list on larger screens again
page.driver.browser.manage.window.resize_to(680, 1080)
page.driver.browser.manage.window.resize_to(700, 1080)
page.driver.browser.navigate.refresh
wp_table.expect_work_package_listed wp_1, wp_2
wp_table.expect_work_package_order wp_1, wp_2

@ -50,6 +50,7 @@ describe 'work package export', type: :feature do
let(:settings_menu) { ::Components::WorkPackages::SettingsMenu.new }
before do
@download_list = DownloadList.new
wp_1
wp_2
wp_3
@ -59,11 +60,9 @@ describe 'work package export', type: :feature do
end
let(:export_type) { 'CSV' }
subject { DownloadedFile.download_content }
def export!(wait_for_downloads = true)
DownloadedFile::clear_downloads
subject { @download_list.refresh_from(page).latest_downloaded_content }
def export!(expect_success = true)
work_packages_page.ensure_loaded
settings_menu.open_and_choose 'Export ...'
@ -82,15 +81,13 @@ describe 'work package export', type: :feature do
# nothing
end
if wait_for_downloads
# Wait for the file to download
::DownloadedFile.wait_for_download
::DownloadedFile.wait_for_download_content
if expect_success
expect(page).to have_text("The export has completed successfully")
end
end
after do
DownloadedFile::clear_downloads
DownloadList.clear
end
context 'CSV export' do
@ -101,7 +98,7 @@ describe 'work package export', type: :feature do
filters.open
end
it 'shows all work packages with the default filters', js: true, retry: 2 do
it 'shows all work packages with the default filters', js: true do
export!
expect(subject).to have_text(wp_1.description)
@ -113,7 +110,7 @@ describe 'work package export', type: :feature do
expect(subject.scan(/Type (A|B)/).flatten).to eq %w(A A B A)
end
it 'shows all work packages grouped by ', js: true, retry: 2 do
it 'shows all work packages grouped by ', js: true do
group_by.enable_via_menu 'Type'
wp_table.expect_work_package_listed(wp_1)
@ -133,7 +130,7 @@ describe 'work package export', type: :feature do
end
it 'shows only the work package with the right progress if filtered this way',
js: true, retry: 2 do
js: true do
filters.add_filter_by 'Progress (%)', 'is', ['25'], 'percentageDone'
sleep 1
@ -149,7 +146,7 @@ describe 'work package export', type: :feature do
expect(subject).not_to have_text(wp_3.description)
end
it 'shows only work packages of the filtered type', js: true, retry: 2 do
it 'shows only work packages of the filtered type', js: true do
filters.add_filter_by 'Type', 'is', wp_3.type.name
expect(page).to have_no_content(wp_2.description) # safeguard
@ -163,7 +160,7 @@ describe 'work package export', type: :feature do
expect(subject).to have_text(wp_3.description)
end
it 'exports selected columns', js: true, retry: 2 do
it 'exports selected columns', js: true do
columns.add 'Progress (%)'
export!

@ -118,11 +118,11 @@ RSpec.feature 'Work package index sums', js: true do
# Add float cf column
columns.add float_cf.name
# Add overall costs column
columns.add 'Overall costs'
columns.add 'Overall costs', finicky: true
# Add unit costs column
columns.add 'Unit costs'
columns.add 'Unit costs', finicky: true
# Add labor costs column
columns.add 'Labor costs'
columns.add 'Labor costs', finicky: true
# Trigger action from action menu dropdown
modal.set_display_sums enable: true

@ -108,7 +108,7 @@ describe 'inline create work package', js: true do
sleep(0.3)
columns.open_modal
columns.add(cf_list.name, save_changes: true)
columns.add(cf_list.name, save_changes: true, finicky: true)
wp_table.click_inline_create

@ -57,8 +57,8 @@ describe 'Work Package table relations', js: true do
wp_table.visit_query(query)
wp_table.expect_work_package_listed(wp_from, wp_to, wp_to2)
columns.add("Relations to #{type.name}")
columns.add("follows relations")
columns.add(type.name, finicky: true)
columns.add("follows", finicky: true)
wp_from_row = wp_table.row(wp_from)
wp_from_to = wp_table.row(wp_to)

@ -188,6 +188,7 @@ describe 'Switching types in work package table', js: true do
req_text_field.expect_active!
# Cancel edition now
SeleniumHubWaiter.wait
req_text_field.cancel_by_escape
req_text_field.expect_state_text '-'
@ -311,6 +312,7 @@ describe 'Switching types in work package table', js: true do
visit new_project_work_packages_path(project.identifier, type: type.id)
expect_angular_frontend_initialized
SeleniumHubWaiter.wait
end
it 'can switch to the type with CF list' do

@ -129,6 +129,7 @@ describe 'Wysiwyg child pages spec',
expect(page).not_to have_selector('.pages-hierarchy', text: 'Parent page')
expect(page).to have_selector('h1', text: 'My page')
SeleniumHubWaiter.wait
find('.toolbar .icon-edit').click
end
@ -161,9 +162,9 @@ describe 'Wysiwyg child pages spec',
expect(page).to have_selector('.pages-hierarchy', text: 'Parent page')
expect(page).to have_selector('h1', text: 'My page')
SeleniumHubWaiter.wait
find('.toolbar .icon-edit').click
end
end
end
end

@ -83,8 +83,10 @@ describe 'Wysiwyg code block macro',
expect(page).to have_selector('pre.highlight-ruby', count: 2)
end
SeleniumHubWaiter.wait
# Edit page again, expect widget
click_on 'Edit'
# SeleniumHubWaiter.wait
editor.in_editor do |container,|
expect(container).to have_selector('.op-uc-code-block', text: snippet, count: 2)
@ -111,6 +113,7 @@ describe 'Wysiwyg code block macro',
wp = WikiPage.last
expect(wp.content.text.gsub("\r\n", "\n")).to eq("```text\nasdf\n```")
SeleniumHubWaiter.wait
click_on 'Edit'
editor.in_editor do |container,|
@ -164,6 +167,7 @@ describe 'Wysiwyg code block macro',
end
# Edit page again, expect widget
SeleniumHubWaiter.wait
click_on 'Edit'
editor.in_editor do |container,|

@ -126,6 +126,7 @@ describe 'Wysiwyg tables',
expect(page).to have_selector('td', text: 'a')
end
SeleniumHubWaiter.wait
# Edit again
click_on 'Edit'
@ -189,6 +190,7 @@ describe 'Wysiwyg tables',
expect(page).to have_selector('td[style*="vertical-align:top"]')
end
SeleniumHubWaiter.wait
# Edit again
click_on 'Edit'
@ -305,6 +307,7 @@ describe 'Wysiwyg tables',
expect(page).to have_selector('td[style*="width:250px"]')
end
SeleniumHubWaiter.wait
# Edit again
click_on 'Edit'

@ -57,7 +57,7 @@ describe LdapAuthSource, type: :model do
describe 'with live LDAP' do
before(:all) do
ldif = Rails.root.join('spec/fixtures/ldap/users.ldif')
@ldap_server = Ladle::Server.new(quiet: false, port: '12389', domain: 'dc=example,dc=com', ldif: ldif).start
@ldap_server = Ladle::Server.new(quiet: false, port: ParallelHelper.port_for_ldap.to_s, domain: 'dc=example,dc=com', ldif: ldif).start
end
after(:all) do
@ -67,7 +67,7 @@ describe LdapAuthSource, type: :model do
# Ldap has three users aa729, bb459, cc414
let(:ldap) do
FactoryBot.create :ldap_auth_source,
port: '12389',
port: ParallelHelper.port_for_ldap.to_s,
account: 'uid=admin,ou=system',
account_password: 'secret',
base_dn: 'ou=people,dc=example,dc=com',

@ -27,7 +27,6 @@
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'spec_helper'
require 'factory_bot_rails'
require 'rspec/rails'
require 'shoulda/matchers'
@ -53,6 +52,9 @@ require 'test_prof/recipes/rspec/before_all'
# may lead to broken specs on the CI, if we don't sort here
# (example: with_config.rb has to precede with_direct_uploads.rb).
#
require_relative "./support/parallel_helper"
require_relative "./support/download_list"
require_relative "./support/capybara"
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
Dir[Rails.root.join('spec/features/support/**/*.rb')].each { |f| require f }
Dir[Rails.root.join('spec/lib/api/v3/support/**/*.rb')].each { |f| require f }

@ -14,23 +14,28 @@ def register_chrome(language, name: :"chrome_#{language}")
if ActiveRecord::Type::Boolean.new.cast(ENV['OPENPROJECT_TESTING_NO_HEADLESS'])
# Maximize the window however large the available space is
options.add_argument('start-maximized')
# options.add_argument('window-size=1920,1080')
# Open dev tools for quick access
options.add_argument('auto-open-devtools-for-tabs')
if ActiveRecord::Type::Boolean.new.cast(ENV['OPENPROJECT_TESTING_AUTO_DEVTOOLS'])
options.add_argument('auto-open-devtools-for-tabs')
end
else
options.add_argument('window-size=1920,1080')
options.add_argument('headless')
options.add_argument('disable-gpu')
end
options.add_argument('no-sandbox')
options.add_argument('disable-gpu')
options.add_argument('disable-popup-blocking')
options.add_argument("lang=#{language}")
# This is REQUIRED for running in a docker container
# https://github.com/grosser/parallel_tests/issues/658
options.add_argument('disable-dev-shm-usage')
options.add_preference(:download,
directory_upgrade: true,
prompt_for_download: false,
default_directory: DownloadedFile::PATH.to_s)
default_directory: DownloadList::SHARED_PATH.to_s)
options.add_preference(:browser, set_download_behavior: { behavior: 'allow' })
@ -44,13 +49,17 @@ def register_chrome(language, name: :"chrome_#{language}")
client.read_timeout = 180
client.open_timeout = 180
service = ::Selenium::WebDriver::Service.chrome(args: { verbose: true, log_path: '/tmp/chromedriver.log' })
driver = Capybara::Selenium::Driver.new(
app,
browser: ENV['SELENIUM_GRID_URL'] ? :remote : :chrome,
# browser: ENV['SELENIUM_GRID_URL'] ? :remote : :chrome,
browser: :chrome,
url: ENV['SELENIUM_GRID_URL'],
desired_capabilities: capabilities,
http_client: client,
options: options
options: options,
service: service
)
if !ENV['SELENIUM_GRID_URL']
@ -61,7 +70,7 @@ def register_chrome(language, name: :"chrome_#{language}")
bridge.http.call :post,
"/session/#{bridge.session_id}/chromium/send_command",
cmd: 'Page.setDownloadBehavior',
params: { behavior: 'allow', downloadPath: DownloadedFile::PATH.to_s }
params: { behavior: 'allow', downloadPath: DownloadList::SHARED_PATH.to_s }
end
driver
@ -76,10 +85,17 @@ register_chrome 'en'
# Register german locale for custom field decimal test
register_chrome 'de'
Billy.configure do |c|
c.proxy_host = Capybara.server_host
if Capybara.server_port
c.proxy_port = Capybara.server_port + 1000
end
end
# Register mocking proxy driver
register_chrome 'en', name: :chrome_billy do |options, capabilities|
options.add_argument("proxy-server=#{Billy.proxy.host}:#{Billy.proxy.port}")
options.add_argument('proxy-bypass-list=127.0.0.1;localhost')
options.add_argument("proxy-bypass-list=127.0.0.1;localhost;#{Capybara.server_host}")
capabilities[:acceptInsecureCerts] = true
end
@ -88,4 +104,3 @@ end
register_chrome 'en', name: :chrome_revit_add_in do |options, capabilities|
options.add_argument("user-agent='foo bar Revit'")
end

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

Loading…
Cancel
Save