diff options
103 files changed, 718 insertions, 2601 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9c1eb2736b1..b1445feee58 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,7 +31,6 @@ variables: GIT_SUBMODULE_STRATEGY: "none" GET_SOURCES_ATTEMPTS: "3" KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json - KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json FLAKY_RSPEC_SUITE_REPORT_PATH: rspec_flaky/report-suite.json before_script: @@ -179,46 +178,6 @@ stages: <<: *rspec-metadata-mysql <<: *rails5 -.spinach-metadata: &spinach-metadata - <<: *dedicated-runner - <<: *except-docs-and-qa - <<: *pull-cache - <<: *rails5-variables - stage: test - script: - - JOB_NAME=( $CI_JOB_NAME ) - - export CI_NODE_INDEX=${JOB_NAME[-2]} - - export CI_NODE_TOTAL=${JOB_NAME[-1]} - - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - - export KNAPSACK_GENERATE_REPORT=true - - export CACHE_CLASSES=true - - cp ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH} - - scripts/gitaly-test-spawn - - knapsack spinach "-r rerun" -b || retry '[[ -e tmp/spinach-rerun.txt ]] && bundle exec spinach -b -r rerun $(cat tmp/spinach-rerun.txt)' - artifacts: - expire_in: 31d - when: always - paths: - - coverage/ - - knapsack/ - - tmp/capybara/ - -.spinach-metadata-pg: &spinach-metadata-pg - <<: *spinach-metadata - <<: *use-pg - -.spinach-metadata-pg-rails5: &spinach-metadata-pg-rails5 - <<: *spinach-metadata-pg - <<: *rails5 - -.spinach-metadata-mysql: &spinach-metadata-mysql - <<: *spinach-metadata - <<: *use-mysql - -.spinach-metadata-mysql-rails5: &spinach-metadata-mysql-rails5 - <<: *spinach-metadata-mysql - <<: *rails5 - .only-canonical-masters: &only-canonical-masters only: - master@gitlab-org/gitlab-ce @@ -350,9 +309,7 @@ retrieve-tests-metadata: script: - mkdir -p knapsack/${CI_PROJECT_NAME}/ - wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH - - wget -O $KNAPSACK_SPINACH_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_SPINACH_SUITE_REPORT_PATH || rm $KNAPSACK_SPINACH_SUITE_REPORT_PATH - '[[ -f $KNAPSACK_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${KNAPSACK_RSPEC_SUITE_REPORT_PATH}' - - '[[ -f $KNAPSACK_SPINACH_SUITE_REPORT_PATH ]] || echo "{}" > ${KNAPSACK_SPINACH_SUITE_REPORT_PATH}' - mkdir -p rspec_flaky/ - wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH - '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}' @@ -370,10 +327,9 @@ update-tests-metadata: script: - retry gem install fog-aws mime-types activesupport - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - - scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach-pg_node_*.json - scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json - FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH} - - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH' + - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH' - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $FLAKY_RSPEC_SUITE_REPORT_PATH' - rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json - rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json @@ -441,133 +397,129 @@ setup-test-env: - config/secrets.yml - vendor/gitaly-ruby -rspec-pg 0 28: *rspec-metadata-pg -rspec-pg 1 28: *rspec-metadata-pg -rspec-pg 2 28: *rspec-metadata-pg -rspec-pg 3 28: *rspec-metadata-pg -rspec-pg 4 28: *rspec-metadata-pg -rspec-pg 5 28: *rspec-metadata-pg -rspec-pg 6 28: *rspec-metadata-pg -rspec-pg 7 28: *rspec-metadata-pg -rspec-pg 8 28: *rspec-metadata-pg -rspec-pg 9 28: *rspec-metadata-pg -rspec-pg 10 28: *rspec-metadata-pg -rspec-pg 11 28: *rspec-metadata-pg -rspec-pg 12 28: *rspec-metadata-pg -rspec-pg 13 28: *rspec-metadata-pg -rspec-pg 14 28: *rspec-metadata-pg -rspec-pg 15 28: *rspec-metadata-pg -rspec-pg 16 28: *rspec-metadata-pg -rspec-pg 17 28: *rspec-metadata-pg -rspec-pg 18 28: *rspec-metadata-pg -rspec-pg 19 28: *rspec-metadata-pg -rspec-pg 20 28: *rspec-metadata-pg -rspec-pg 21 28: *rspec-metadata-pg -rspec-pg 22 28: *rspec-metadata-pg -rspec-pg 23 28: *rspec-metadata-pg -rspec-pg 24 28: *rspec-metadata-pg -rspec-pg 25 28: *rspec-metadata-pg -rspec-pg 26 28: *rspec-metadata-pg -rspec-pg 27 28: *rspec-metadata-pg - -rspec-mysql 0 28: *rspec-metadata-mysql -rspec-mysql 1 28: *rspec-metadata-mysql -rspec-mysql 2 28: *rspec-metadata-mysql -rspec-mysql 3 28: *rspec-metadata-mysql -rspec-mysql 4 28: *rspec-metadata-mysql -rspec-mysql 5 28: *rspec-metadata-mysql -rspec-mysql 6 28: *rspec-metadata-mysql -rspec-mysql 7 28: *rspec-metadata-mysql -rspec-mysql 8 28: *rspec-metadata-mysql -rspec-mysql 9 28: *rspec-metadata-mysql -rspec-mysql 10 28: *rspec-metadata-mysql -rspec-mysql 11 28: *rspec-metadata-mysql -rspec-mysql 12 28: *rspec-metadata-mysql -rspec-mysql 13 28: *rspec-metadata-mysql -rspec-mysql 14 28: *rspec-metadata-mysql -rspec-mysql 15 28: *rspec-metadata-mysql -rspec-mysql 16 28: *rspec-metadata-mysql -rspec-mysql 17 28: *rspec-metadata-mysql -rspec-mysql 18 28: *rspec-metadata-mysql -rspec-mysql 19 28: *rspec-metadata-mysql -rspec-mysql 20 28: *rspec-metadata-mysql -rspec-mysql 21 28: *rspec-metadata-mysql -rspec-mysql 22 28: *rspec-metadata-mysql -rspec-mysql 23 28: *rspec-metadata-mysql -rspec-mysql 24 28: *rspec-metadata-mysql -rspec-mysql 25 28: *rspec-metadata-mysql -rspec-mysql 26 28: *rspec-metadata-mysql -rspec-mysql 27 28: *rspec-metadata-mysql - -spinach-pg 0 2: *spinach-metadata-pg -spinach-pg 1 2: *spinach-metadata-pg - -spinach-mysql 0 2: *spinach-metadata-mysql -spinach-mysql 1 2: *spinach-metadata-mysql - -rspec-pg-rails5 0 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 1 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 2 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 3 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 4 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 5 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 6 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 7 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 8 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 9 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 10 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 11 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 12 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 13 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 14 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 15 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 16 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 17 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 18 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 19 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 20 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 21 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 22 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 23 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 24 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 25 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 26 28: *rspec-metadata-pg-rails5 -rspec-pg-rails5 27 28: *rspec-metadata-pg-rails5 - -rspec-mysql-rails5 0 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 1 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 2 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 3 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 4 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 5 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 6 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 7 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 8 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 9 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 10 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 11 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 12 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 13 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 14 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 15 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 16 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 17 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 18 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 19 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 20 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 21 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 22 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 23 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 24 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 25 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 26 28: *rspec-metadata-mysql-rails5 -rspec-mysql-rails5 27 28: *rspec-metadata-mysql-rails5 - -spinach-pg-rails5 0 2: *spinach-metadata-pg-rails5 -spinach-pg-rails5 1 2: *spinach-metadata-pg-rails5 - -spinach-mysql-rails5 0 2: *spinach-metadata-mysql-rails5 -spinach-mysql-rails5 1 2: *spinach-metadata-mysql-rails5 +rspec-pg 0 30: *rspec-metadata-pg +rspec-pg 1 30: *rspec-metadata-pg +rspec-pg 2 30: *rspec-metadata-pg +rspec-pg 3 30: *rspec-metadata-pg +rspec-pg 4 30: *rspec-metadata-pg +rspec-pg 5 30: *rspec-metadata-pg +rspec-pg 6 30: *rspec-metadata-pg +rspec-pg 7 30: *rspec-metadata-pg +rspec-pg 8 30: *rspec-metadata-pg +rspec-pg 9 30: *rspec-metadata-pg +rspec-pg 10 30: *rspec-metadata-pg +rspec-pg 11 30: *rspec-metadata-pg +rspec-pg 12 30: *rspec-metadata-pg +rspec-pg 13 30: *rspec-metadata-pg +rspec-pg 14 30: *rspec-metadata-pg +rspec-pg 15 30: *rspec-metadata-pg +rspec-pg 16 30: *rspec-metadata-pg +rspec-pg 17 30: *rspec-metadata-pg +rspec-pg 18 30: *rspec-metadata-pg +rspec-pg 19 30: *rspec-metadata-pg +rspec-pg 20 30: *rspec-metadata-pg +rspec-pg 21 30: *rspec-metadata-pg +rspec-pg 22 30: *rspec-metadata-pg +rspec-pg 23 30: *rspec-metadata-pg +rspec-pg 24 30: *rspec-metadata-pg +rspec-pg 25 30: *rspec-metadata-pg +rspec-pg 26 30: *rspec-metadata-pg +rspec-pg 27 30: *rspec-metadata-pg +rspec-pg 28 30: *rspec-metadata-pg +rspec-pg 29 30: *rspec-metadata-pg + +rspec-mysql 0 30: *rspec-metadata-mysql +rspec-mysql 1 30: *rspec-metadata-mysql +rspec-mysql 2 30: *rspec-metadata-mysql +rspec-mysql 3 30: *rspec-metadata-mysql +rspec-mysql 4 30: *rspec-metadata-mysql +rspec-mysql 5 30: *rspec-metadata-mysql +rspec-mysql 6 30: *rspec-metadata-mysql +rspec-mysql 7 30: *rspec-metadata-mysql +rspec-mysql 8 30: *rspec-metadata-mysql +rspec-mysql 9 30: *rspec-metadata-mysql +rspec-mysql 10 30: *rspec-metadata-mysql +rspec-mysql 11 30: *rspec-metadata-mysql +rspec-mysql 12 30: *rspec-metadata-mysql +rspec-mysql 13 30: *rspec-metadata-mysql +rspec-mysql 14 30: *rspec-metadata-mysql +rspec-mysql 15 30: *rspec-metadata-mysql +rspec-mysql 16 30: *rspec-metadata-mysql +rspec-mysql 17 30: *rspec-metadata-mysql +rspec-mysql 18 30: *rspec-metadata-mysql +rspec-mysql 19 30: *rspec-metadata-mysql +rspec-mysql 20 30: *rspec-metadata-mysql +rspec-mysql 21 30: *rspec-metadata-mysql +rspec-mysql 22 30: *rspec-metadata-mysql +rspec-mysql 23 30: *rspec-metadata-mysql +rspec-mysql 24 30: *rspec-metadata-mysql +rspec-mysql 25 30: *rspec-metadata-mysql +rspec-mysql 26 30: *rspec-metadata-mysql +rspec-mysql 27 30: *rspec-metadata-mysql +rspec-mysql 28 30: *rspec-metadata-mysql +rspec-mysql 29 30: *rspec-metadata-mysql + +rspec-pg-rails5 0 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 1 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 2 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 3 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 4 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 5 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 6 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 7 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 8 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 9 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 10 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 11 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 12 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 13 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 14 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 15 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 16 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 17 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 18 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 19 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 20 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 21 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 22 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 23 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 24 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 25 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 26 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 27 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 28 30: *rspec-metadata-pg-rails5 +rspec-pg-rails5 29 30: *rspec-metadata-pg-rails5 + +rspec-mysql-rails5 0 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 1 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 2 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 3 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 4 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 5 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 6 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 7 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 8 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 9 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 10 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 11 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 12 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 13 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 14 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 15 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 16 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 17 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 18 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 19 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 20 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 21 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 22 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 23 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 24 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 25 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 26 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 27 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 28 30: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 29 30: *rspec-metadata-mysql-rails5 static-analysis: <<: *dedicated-no-docs-no-db-pull-cache-job diff --git a/.gitlab/merge_request_templates/Database Changes.md b/.gitlab/merge_request_templates/Database Changes.md index 2bb1f374e98..1c4f30d9320 100644 --- a/.gitlab/merge_request_templates/Database Changes.md +++ b/.gitlab/merge_request_templates/Database Changes.md @@ -37,12 +37,14 @@ When removing columns, tables, indexes or other structures: - [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html) - [ ] API support added - [ ] Tests added for this feature/bug -- Review - - [ ] Has been reviewed by Backend - - [ ] Has been reviewed by Database +- Conform by the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html) + - [ ] Has been reviewed by a Backend maintainer + - [ ] Has been reviewed by a Database specialist - [ ] Conform by the [merge request performance guides](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html) - [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides) -- [ ] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) +- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) - [ ] Internationalization required/considered - [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan - [ ] End-to-end tests pass (`package-and-qa` manual pipeline job) + +/label ~database diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f5d19ce2ce..d82f21fe795 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -728,6 +728,3 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [polling-etag]: https://docs.gitlab.com/ce/development/polling.html [testing]: doc/development/testing_guide/index.md [us-english]: https://en.wikipedia.org/wiki/American_English - -[^1]: Please note that specs other than JavaScript specs are considered backend - code. @@ -160,7 +160,7 @@ gem 'state_machines-activerecord', '~> 0.5.1' gem 'acts-as-taggable-on', '~> 5.0' # Background jobs -gem 'sidekiq', '~> 5.0' +gem 'sidekiq', '~> 5.1' gem 'sidekiq-cron', '~> 0.6.0' gem 'redis-namespace', '~> 1.5.2' gem 'sidekiq-limit_fetch', '~> 3.4', require: false @@ -325,8 +325,6 @@ group :development, :test do gem 'factory_bot_rails', '~> 4.8.2' gem 'rspec-rails', '~> 3.6.0' gem 'rspec-retry', '~> 0.4.5' - gem 'spinach-rails', '~> 0.2.1' - gem 'spinach-rerun-reporter', '~> 0.0.2' gem 'rspec_profiling', '~> 0.0.5' gem 'rspec-set', '~> 0.1.3' gem 'rspec-parameterized', require: false @@ -343,7 +341,6 @@ group :development, :test do gem 'spring', '~> 2.0.0' gem 'spring-commands-rspec', '~> 1.0.4' - gem 'spring-commands-spinach', '~> 1.1.0' gem 'gitlab-styles', '~> 2.3', require: false # Pin these dependencies, otherwise a new rule could break the CI pipelines diff --git a/Gemfile.lock b/Gemfile.lock index 6028ce32d2f..18c25cc34b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -131,7 +131,6 @@ GEM coderay (1.1.1) coercible (1.0.0) descendants_tracker (~> 0.0.1) - colorize (0.7.7) commonmarker (0.17.8) ruby-enum (~> 0.5) concord (0.1.5) @@ -288,7 +287,6 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gherkin-ruby (0.3.2) gitaly-proto (0.99.0) google-protobuf (~> 3.1) grpc (~> 1.10) @@ -846,11 +844,11 @@ GEM rack shoulda-matchers (3.1.2) activesupport (>= 4.0.0) - sidekiq (5.0.5) + sidekiq (5.1.3) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) - redis (>= 3.3.4, < 5) + redis (>= 3.3.5, < 5) sidekiq-cron (0.6.0) rufus-scheduler (>= 3.3.0) sidekiq (>= 4.2.1) @@ -869,22 +867,10 @@ GEM simplecov-html (0.10.0) slack-notifier (1.5.1) slop (3.6.0) - spinach (0.8.10) - colorize - gherkin-ruby (>= 0.3.2) - json - spinach-rails (0.2.1) - capybara (>= 2.0.0) - railties (>= 3) - spinach (>= 0.4) - spinach-rerun-reporter (0.0.2) - spinach (~> 0.8) spring (2.0.1) activesupport (>= 4.2) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - spring-commands-spinach (1.1.0) - spring (>= 0.9.1) sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -1177,17 +1163,14 @@ DEPENDENCIES settingslogic (~> 2.0.9) sham_rack (~> 1.3.6) shoulda-matchers (~> 3.1.2) - sidekiq (~> 5.0) + sidekiq (~> 5.1) sidekiq-cron (~> 0.6.0) sidekiq-limit_fetch (~> 3.4) simple_po_parser (~> 1.1.2) simplecov (~> 0.14.0) slack-notifier (~> 1.5.1) - spinach-rails (~> 0.2.1) - spinach-rerun-reporter (~> 0.0.2) spring (~> 2.0.0) spring-commands-rspec (~> 1.0.4) - spring-commands-spinach (~> 1.1.0) sprockets (~> 3.7.0) sshkey (~> 1.9.0) stackprof (~> 0.2.10) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index fc6dfd040c2..af7305619eb 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -132,7 +132,6 @@ GEM coderay (1.1.2) coercible (1.0.0) descendants_tracker (~> 0.0.1) - colorize (0.8.1) commonmarker (0.17.9) ruby-enum (~> 0.5) concord (0.1.5) @@ -289,7 +288,6 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gherkin-ruby (0.3.2) gitaly-proto (0.99.0) google-protobuf (~> 3.1) grpc (~> 1.10) @@ -871,22 +869,10 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.2) slack-notifier (1.5.1) - spinach (0.8.10) - colorize - gherkin-ruby (>= 0.3.2) - json - spinach-rails (0.2.1) - capybara (>= 2.0.0) - railties (>= 3) - spinach (>= 0.4) - spinach-rerun-reporter (0.0.2) - spinach (~> 0.8) spring (2.0.2) activesupport (>= 4.2) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - spring-commands-spinach (1.1.0) - spring (>= 0.9.1) sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -1187,11 +1173,8 @@ DEPENDENCIES simple_po_parser (~> 1.1.2) simplecov (~> 0.14.0) slack-notifier (~> 1.5.1) - spinach-rails (~> 0.2.1) - spinach-rerun-reporter (~> 0.0.2) spring (~> 2.0.0) spring-commands-rspec (~> 1.0.4) - spring-commands-spinach (~> 1.1.0) sprockets (~> 3.7.0) sshkey (~> 1.9.0) stackprof (~> 0.2.10) diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue new file mode 100644 index 00000000000..df21e2f8771 --- /dev/null +++ b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue @@ -0,0 +1,77 @@ +<script> +import _ from 'underscore'; +import GlModal from '~/vue_shared/components/gl_modal.vue'; +import { s__, sprintf } from '~/locale'; + +export default { + components: { + GlModal, + }, + props: { + deleteWikiUrl: { + type: String, + required: true, + default: '', + }, + pageTitle: { + type: String, + required: true, + default: '', + }, + csrfToken: { + type: String, + required: true, + default: '', + }, + }, + computed: { + message() { + return s__('WikiPageConfirmDelete|Are you sure you want to delete this page?'); + }, + title() { + return sprintf( + s__('WikiPageConfirmDelete|Delete page %{pageTitle}?'), + { + pageTitle: _.escape(this.pageTitle), + }, + false, + ); + }, + }, + methods: { + onSubmit() { + this.$refs.form.submit(); + }, + }, +}; +</script> + +<template> + <gl-modal + id="delete-wiki-modal" + :header-title-text="title" + footer-primary-button-variant="danger" + :footer-primary-button-text="s__('WikiPageConfirmDelete|Delete page')" + @submit="onSubmit" + > + {{ message }} + <form + ref="form" + :action="deleteWikiUrl" + method="post" + class="form-horizontal js-requires-input" + > + <input + ref="method" + type="hidden" + name="_method" + value="delete" + /> + <input + type="hidden" + name="authenticity_token" + :value="csrfToken" + /> + </form> + </gl-modal> +</template> diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js index ec01c66ffda..0295653cb29 100644 --- a/app/assets/javascripts/pages/projects/wikis/index.js +++ b/app/assets/javascripts/pages/projects/wikis/index.js @@ -1,12 +1,40 @@ import $ from 'jquery'; +import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; +import csrf from '~/lib/utils/csrf'; import Wikis from './wikis'; import ShortcutsWiki from '../../../shortcuts_wiki'; import ZenMode from '../../../zen_mode'; import GLForm from '../../../gl_form'; +import deleteWikiModal from './components/delete_wiki_modal.vue'; document.addEventListener('DOMContentLoaded', () => { new Wikis(); // eslint-disable-line no-new new ShortcutsWiki(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new new GLForm($('.wiki-form'), true); // eslint-disable-line no-new + + const deleteWikiButton = document.getElementById('delete-wiki-button'); + + if (deleteWikiButton) { + Vue.use(Translate); + + const { deleteWikiUrl, pageTitle } = deleteWikiButton.dataset; + const deleteWikiModalEl = document.getElementById('delete-wiki-modal'); + const deleteModal = new Vue({ // eslint-disable-line + el: deleteWikiModalEl, + data: { + deleteWikiUrl: '', + }, + render(createElement) { + return createElement(deleteWikiModal, { + props: { + pageTitle, + deleteWikiUrl, + csrfToken: csrf.token, + }, + }); + }, + }); + } }); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_maintainer_edit.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_maintainer_edit.vue deleted file mode 100644 index f0298f732ea..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_maintainer_edit.vue +++ /dev/null @@ -1,20 +0,0 @@ -<script> - export default { - name: 'MRWidgetMaintainerEdit', - props: { - maintainerEditAllowed: { - type: Boolean, - default: false, - required: false, - }, - }, - }; -</script> - -<template> - <section class="mr-info-list mr-links"> - <p v-if="maintainerEditAllowed"> - {{ s__("mrWidget|Allows edits from maintainers") }} - </p> - </section> -</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue index bf8628d18a6..926a3172412 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue @@ -10,6 +10,6 @@ In EE, the configuration extends this object to add a functioning squash-before- button. */ -export default { - template: '', -}; +<script> + export default {}; +</script> diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js index 7f5f28091da..15097fa2a3f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js +++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js @@ -15,7 +15,6 @@ export { default as WidgetHeader } from './components/mr_widget_header.vue'; export { default as WidgetMergeHelp } from './components/mr_widget_merge_help.vue'; export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue'; export { default as Deployment } from './components/deployment.vue'; -export { default as WidgetMaintainerEdit } from './components/mr_widget_maintainer_edit.vue'; export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue'; export { default as MergedState } from './components/states/mr_widget_merged.vue'; export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue'; @@ -41,8 +40,8 @@ export { default as MRWidgetService } from './services/mr_widget_service'; export { default as eventHub } from './event_hub'; export { default as getStateKey } from './stores/get_state_key'; export { default as stateMaps } from './stores/state_maps'; -export { default as SquashBeforeMerge } from './components/states/mr_widget_squash_before_merge'; +export { default as SquashBeforeMerge } from './components/states/mr_widget_squash_before_merge.vue'; export { default as notify } from '../lib/utils/notify'; export { default as SourceBranchRemovalStatus } from './components/source_branch_removal_status.vue'; -export { default as mrWidgetOptions } from './mr_widget_options'; +export { default as mrWidgetOptions } from './mr_widget_options.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 345f9ac1b4b..f69fe03fcb3 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -1,12 +1,13 @@ +<script> + import Project from '~/pages/projects/project'; import SmartInterval from '~/smart_interval'; -import Flash from '../flash'; +import createFlash from '../flash'; import { WidgetHeader, WidgetMergeHelp, WidgetPipeline, Deployment, - WidgetMaintainerEdit, WidgetRelatedLinks, MergedState, ClosedState, @@ -40,10 +41,39 @@ import { setFavicon } from '../lib/utils/common_utils'; export default { el: '#js-vue-mr-widget', name: 'MRWidget', + components: { + 'mr-widget-header': WidgetHeader, + 'mr-widget-merge-help': WidgetMergeHelp, + 'mr-widget-pipeline': WidgetPipeline, + Deployment, + 'mr-widget-related-links': WidgetRelatedLinks, + 'mr-widget-merged': MergedState, + 'mr-widget-closed': ClosedState, + 'mr-widget-merging': MergingState, + 'mr-widget-failed-to-merge': FailedToMerge, + 'mr-widget-wip': WorkInProgressState, + 'mr-widget-archived': ArchivedState, + 'mr-widget-conflicts': ConflictsState, + 'mr-widget-nothing-to-merge': NothingToMergeState, + 'mr-widget-not-allowed': NotAllowedState, + 'mr-widget-missing-branch': MissingBranchState, + 'mr-widget-ready-to-merge': ReadyToMergeState, + 'sha-mismatch': ShaMismatchState, + 'mr-widget-squash-before-merge': SquashBeforeMerge, + 'mr-widget-checking': CheckingState, + 'mr-widget-unresolved-discussions': UnresolvedDiscussionsState, + 'mr-widget-pipeline-blocked': PipelineBlockedState, + 'mr-widget-pipeline-failed': PipelineFailedState, + 'mr-widget-merge-when-pipeline-succeeds': MergeWhenPipelineSucceedsState, + 'mr-widget-auto-merge-failed': AutoMergeFailed, + 'mr-widget-rebase': RebaseState, + SourceBranchRemovalStatus, + }, props: { mrData: { type: Object, required: false, + default: null, }, }, data() { @@ -72,6 +102,13 @@ export default { (!this.mr.isNothingToMergeState && !this.mr.isMergedState); }, }, + created() { + this.initPolling(); + this.bindEventHubListeners(); + }, + mounted() { + this.handleMounted(); + }, methods: { createService(store) { const endpoints = { @@ -99,7 +136,7 @@ export default { cb.call(null, data); } }) - .catch(() => new Flash('Something went wrong. Please try again.')); + .catch(() => createFlash('Something went wrong. Please try again.')); }, initPolling() { this.pollingInterval = new SmartInterval({ @@ -134,7 +171,7 @@ export default { } }) .catch(() => { - new Flash('Something went wrong while fetching the environments for this merge request. Please try again.'); // eslint-disable-line + createFlash('Something went wrong while fetching the environments for this merge request. Please try again.'); // eslint-disable-line }); }, fetchActionsContent() { @@ -147,7 +184,7 @@ export default { Project.initRefSwitcher(); } }) - .catch(() => new Flash('Something went wrong. Please try again.')); + .catch(() => createFlash('Something went wrong. Please try again.')); }, handleNotification(data) { if (data.ci_status === this.mr.ciStatus) return; @@ -202,76 +239,53 @@ export default { this.initDeploymentsPolling(); }, }, - created() { - this.initPolling(); - this.bindEventHubListeners(); - }, - mounted() { - this.handleMounted(); - }, - components: { - 'mr-widget-header': WidgetHeader, - 'mr-widget-merge-help': WidgetMergeHelp, - 'mr-widget-pipeline': WidgetPipeline, - Deployment, - 'mr-widget-maintainer-edit': WidgetMaintainerEdit, - 'mr-widget-related-links': WidgetRelatedLinks, - 'mr-widget-merged': MergedState, - 'mr-widget-closed': ClosedState, - 'mr-widget-merging': MergingState, - 'mr-widget-failed-to-merge': FailedToMerge, - 'mr-widget-wip': WorkInProgressState, - 'mr-widget-archived': ArchivedState, - 'mr-widget-conflicts': ConflictsState, - 'mr-widget-nothing-to-merge': NothingToMergeState, - 'mr-widget-not-allowed': NotAllowedState, - 'mr-widget-missing-branch': MissingBranchState, - 'mr-widget-ready-to-merge': ReadyToMergeState, - 'mr-widget-sha-mismatch': ShaMismatchState, - 'mr-widget-squash-before-merge': SquashBeforeMerge, - 'mr-widget-checking': CheckingState, - 'mr-widget-unresolved-discussions': UnresolvedDiscussionsState, - 'mr-widget-pipeline-blocked': PipelineBlockedState, - 'mr-widget-pipeline-failed': PipelineFailedState, - 'mr-widget-merge-when-pipeline-succeeds': MergeWhenPipelineSucceedsState, - 'mr-widget-auto-merge-failed': AutoMergeFailed, - 'mr-widget-rebase': RebaseState, - SourceBranchRemovalStatus, - }, - template: ` - <div class="mr-state-widget prepend-top-default"> - <mr-widget-header :mr="mr" /> - <mr-widget-pipeline - v-if="shouldRenderPipelines" - :pipeline="mr.pipeline" - :ci-status="mr.ciStatus" - :has-ci="mr.hasCI" - /> - <deployment - v-for="deployment in mr.deployments" - :key="deployment.id" - :deployment="deployment" +}; +</script> +<template> + <div class="mr-state-widget prepend-top-default"> + <mr-widget-header + :mr="mr" + /> + <mr-widget-pipeline + v-if="shouldRenderPipelines" + :pipeline="mr.pipeline" + :ci-status="mr.ciStatus" + :has-ci="mr.hasCI" + /> + <deployment + v-for="deployment in mr.deployments" + :key="deployment.id" + :deployment="deployment" + /> + <div class="mr-widget-section"> + <component + :is="componentName" + :mr="mr" + :service="service" + /> + + <section + v-if="mr.maintainerEditAllowed" + class="mr-info-list mr-links" + > + {{ s__("mrWidget|Allows edits from maintainers") }} + </section> + + <mr-widget-related-links + v-if="shouldRenderRelatedLinks" + :state="mr.state" + :related-links="mr.relatedLinks" + /> + + <source-branch-removal-status + v-if="shouldRenderSourceBranchRemovalStatus" /> - <div class="mr-widget-section"> - <component - :is="componentName" - :mr="mr" - :service="service" /> - <mr-widget-maintainer-edit - :maintainerEditAllowed="mr.maintainerEditAllowed" /> - <mr-widget-related-links - v-if="shouldRenderRelatedLinks" - :state="mr.state" - :related-links="mr.relatedLinks" /> - <source-branch-removal-status - v-if="shouldRenderSourceBranchRemovalStatus" - /> - </div> - <div - class="mr-widget-footer" - v-if="shouldRenderMergeHelp"> - <mr-widget-merge-help /> - </div> </div> - `, -}; + <div + class="mr-widget-footer" + v-if="shouldRenderMergeHelp" + > + <mr-widget-merge-help /> + </div> + </div> +</template> diff --git a/app/controllers/concerns/send_file_upload.rb b/app/controllers/concerns/send_file_upload.rb index 55011c89886..237c93daee8 100644 --- a/app/controllers/concerns/send_file_upload.rb +++ b/app/controllers/concerns/send_file_upload.rb @@ -2,6 +2,10 @@ module SendFileUpload def send_upload(file_upload, send_params: {}, redirect_params: {}, attachment: nil, disposition: 'attachment') if attachment redirect_params[:query] = { "response-content-disposition" => "#{disposition};filename=#{attachment.inspect}" } + # By default, Rails will send uploads with an extension of .js with a + # content-type of text/javascript, which will trigger Rails' + # cross-origin JavaScript protection. + send_params[:content_type] = 'text/plain' if File.extname(attachment) == '.js' send_params.merge!(filename: attachment, disposition: disposition) end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index bc13b8ad7ba..4d4c2af2415 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -8,19 +8,6 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_create_note!, only: [:create] before_action :authorize_resolve_note!, only: [:resolve, :unresolve] - # - # This is a fix to make spinach feature tests passing: - # Controller actions are returned from AbstractController::Base and methods of parent classes are - # excluded in order to return only specific controller related methods. - # That is ok for the app (no :create method in ancestors) - # but fails for tests because there is a :create method on FactoryBot (one of the ancestors) - # - # see https://github.com/rails/rails/blob/v4.2.7/actionpack/lib/abstract_controller/base.rb#L78 - # - def create - super - end - def delete_attachment note.remove_attachment! note.update_attribute(:attachment, nil) diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 0b1b46944aa..f7417a6a5aa 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -181,4 +181,8 @@ class Projects::PipelinesController < Projects::ApplicationController # Also see https://gitlab.com/gitlab-org/gitlab-ce/issues/42343 Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42339') end + + def authorize_update_pipeline! + return access_denied! unless can?(current_user, :update_pipeline, @pipeline) + end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 0b90834d415..1f49764e7cc 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -37,12 +37,16 @@ module Ci delegate :id, to: :project, prefix: true delegate :full_path, to: :project, prefix: true - validates :source, exclusion: { in: %w(unknown), unless: :importing? }, on: :create validates :sha, presence: { unless: :importing? } validates :ref, presence: { unless: :importing? } validates :status, presence: { unless: :importing? } validate :valid_commit_sha, unless: :importing? + # Replace validator below with + # `validates :source, presence: { unless: :importing? }, on: :create` + # when removing Gitlab.rails5? code. + validate :valid_source, unless: :importing?, on: :create + after_create :keep_around_commits, unless: :importing? enum source: { @@ -601,5 +605,11 @@ module Ci project.repository.keep_around(self.sha) project.repository.keep_around(self.before_sha) end + + def valid_source + if source.nil? || source == "unknown" + errors.add(:source, "invalid source") + end + end end end diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb index 808a81cbbf9..8b65758f3e8 100644 --- a/app/policies/ci/build_policy.rb +++ b/app/policies/ci/build_policy.rb @@ -14,11 +14,20 @@ module Ci @subject.triggered_by?(@user) end + condition(:branch_allows_maintainer_push) do + @subject.project.branch_allows_maintainer_push?(@user, @subject.ref) + end + rule { protected_ref }.policy do prevent :update_build prevent :erase_build end rule { can?(:admin_build) | (can?(:update_build) & owner_of_job) }.enable :erase_build + + rule { can?(:public_access) & branch_allows_maintainer_push }.policy do + enable :update_build + enable :update_commit_status + end end end diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb index 6363c382ff8..540e4235299 100644 --- a/app/policies/ci/pipeline_policy.rb +++ b/app/policies/ci/pipeline_policy.rb @@ -4,8 +4,16 @@ module Ci condition(:protected_ref) { ref_protected?(@user, @subject.project, @subject.tag?, @subject.ref) } + condition(:branch_allows_maintainer_push) do + @subject.project.branch_allows_maintainer_push?(@user, @subject.ref) + end + rule { protected_ref }.prevent :update_pipeline + rule { can?(:public_access) & branch_allows_maintainer_push }.policy do + enable :update_pipeline + end + def ref_protected?(user, project, tag, ref) access = ::Gitlab::UserAccess.new(user, project: project) diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 5759b1a376f..99a0d7118f2 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -76,7 +76,7 @@ class ProjectPolicy < BasePolicy condition(:request_access_enabled, scope: :subject, score: 0) { project.request_access_enabled } desc "Has merge requests allowing pushes to user" - condition(:has_merge_requests_allowing_pushes, scope: :subject) do + condition(:has_merge_requests_allowing_pushes) do project.merge_requests_allowing_push_to_user(user).any? end @@ -354,9 +354,7 @@ class ProjectPolicy < BasePolicy # to run pipelines for the branches they have access to. rule { can?(:public_access) & has_merge_requests_allowing_pushes }.policy do enable :create_build - enable :update_build enable :create_pipeline - enable :update_pipeline end rule do diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 9d3d4072027..35c7dc2984a 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -28,9 +28,16 @@ = link_to project_wiki_history_path(@project, @page), class: "btn" do = s_("Wiki|Page history") - if can?(current_user, :admin_wiki, @project) - = link_to project_wiki_path(@project, @page), data: { confirm: s_("WikiPageConfirmDelete|Are you sure you want to delete this page?")}, method: :delete, class: "btn btn-danger" do - = _("Delete") + %button.btn.btn-danger{ data: { toggle: 'modal', + target: '#delete-wiki-modal', + delete_wiki_url: project_wiki_path(@project, @page), + page_title: @page.title.capitalize }, + id: 'delete-wiki-button', + type: 'button' } + = _('Delete') = render 'form' = render 'sidebar' + +#delete-wiki-modal.modal.fade diff --git a/bin/secpick b/bin/secpick index 76ae231e913..5029fe57cfe 100755 --- a/bin/secpick +++ b/bin/secpick @@ -5,7 +5,6 @@ require 'rainbow/refinement' using Rainbow BRANCH_PREFIX = 'security'.freeze -STABLE_BRANCH_SUFFIX = 'stable'.freeze REMOTE = 'dev'.freeze options = { version: nil, branch: nil, sha: nil } @@ -37,9 +36,9 @@ abort("Missing options. Use #{$0} --help to see the list of options available".r abort("Wrong version format #{options[:version].bold}".red) unless options[:version] =~ /\A\d*\-\d*\Z/ branch = [BRANCH_PREFIX, options[:branch], options[:version]].join('-').freeze -stable_branch = "#{options[:version]}-#{STABLE_BRANCH_SUFFIX}".freeze +stable_branch = "#{BRANCH_PREFIX}-#{options[:version]}".freeze -command = "git checkout #{stable_branch} && git pull #{REMOTE} #{stable_branch} && git checkout -B #{branch} && git cherry-pick #{options[:sha]} && git push #{REMOTE} #{branch}" +command = "git fetch #{REMOTE} #{stable_branch} && git checkout #{stable_branch} && git pull #{REMOTE} #{stable_branch} && git checkout -B #{branch} && git cherry-pick #{options[:sha]} && git push #{REMOTE} #{branch}" _stdin, stdout, stderr = Open3.popen3(command) diff --git a/bin/spinach b/bin/spinach deleted file mode 100755 index eda81c9ed8a..00000000000 --- a/bin/spinach +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env ruby - -# Remove this block when removing rails5? code. -gemfile = %w[1 true].include?(ENV["RAILS5"]) ? "Gemfile.rails5" : "Gemfile" -ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../#{gemfile}", __dir__) - -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -require 'bundler/setup' -load Gem.bin_path('spinach', 'spinach') diff --git a/changelogs/unreleased/46010-add-index-to-runner-type.yml b/changelogs/unreleased/46010-add-index-to-runner-type.yml new file mode 100644 index 00000000000..fb8340e37b2 --- /dev/null +++ b/changelogs/unreleased/46010-add-index-to-runner-type.yml @@ -0,0 +1,5 @@ +--- +title: Add index on runner_type for ci_runners +merge_request: 18897 +author: +type: performance diff --git a/changelogs/unreleased/46286-fix-ingress-rbac-default-value.yml b/changelogs/unreleased/46286-fix-ingress-rbac-default-value.yml new file mode 100644 index 00000000000..e9cd8977394 --- /dev/null +++ b/changelogs/unreleased/46286-fix-ingress-rbac-default-value.yml @@ -0,0 +1,5 @@ +--- +title: Disables RBAC on nginx-ingress +merge_request: 18947 +author: +type: fixed diff --git a/changelogs/unreleased/46345-kubernetes-popover-illustration-skewed.yml b/changelogs/unreleased/46345-kubernetes-popover-illustration-skewed.yml new file mode 100644 index 00000000000..a0e6b39fef6 --- /dev/null +++ b/changelogs/unreleased/46345-kubernetes-popover-illustration-skewed.yml @@ -0,0 +1,5 @@ +--- +title: Correct skewed Kubernetes popover illustration +merge_request: 18949 +author: +type: fixed diff --git a/changelogs/unreleased/blackst0ne-remove-spinach.yml b/changelogs/unreleased/blackst0ne-remove-spinach.yml new file mode 100644 index 00000000000..104da257bad --- /dev/null +++ b/changelogs/unreleased/blackst0ne-remove-spinach.yml @@ -0,0 +1,5 @@ +--- +title: Remove Spinach +merge_request: 18869 +author: '@blackst0ne' +type: other diff --git a/changelogs/unreleased/jprovazn-null-byte.yml b/changelogs/unreleased/jprovazn-null-byte.yml new file mode 100644 index 00000000000..4c4760ac412 --- /dev/null +++ b/changelogs/unreleased/jprovazn-null-byte.yml @@ -0,0 +1,5 @@ +--- +title: Fix filename matching when processing file or blob search results +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/jprovazn-pipeline-policy.yml b/changelogs/unreleased/jprovazn-pipeline-policy.yml new file mode 100644 index 00000000000..2997c6c8667 --- /dev/null +++ b/changelogs/unreleased/jprovazn-pipeline-policy.yml @@ -0,0 +1,6 @@ +--- +title: Allow maintainers to retry pipelines on forked projects (if allowed in merge + request) +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/refactor-move-squash-before-merge-vue-component.yml b/changelogs/unreleased/refactor-move-squash-before-merge-vue-component.yml new file mode 100644 index 00000000000..b8b2762a21d --- /dev/null +++ b/changelogs/unreleased/refactor-move-squash-before-merge-vue-component.yml @@ -0,0 +1,5 @@ +--- +title: Move SquashBeforeMerge vue component +merge_request: 18813 +author: George Tsiolis +type: performance diff --git a/changelogs/unreleased/sh-fix-blocked-user-account-ldap.yml b/changelogs/unreleased/sh-fix-blocked-user-account-ldap.yml new file mode 100644 index 00000000000..f7abe763ea8 --- /dev/null +++ b/changelogs/unreleased/sh-fix-blocked-user-account-ldap.yml @@ -0,0 +1,5 @@ +--- +title: Fix system hook not firing for blocked users when LDAP sign-in is used +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-cross-site-origin-uploads-js.yml b/changelogs/unreleased/sh-fix-cross-site-origin-uploads-js.yml new file mode 100644 index 00000000000..3c51aaae896 --- /dev/null +++ b/changelogs/unreleased/sh-fix-cross-site-origin-uploads-js.yml @@ -0,0 +1,5 @@ +--- +title: Fix cross-origin errors when attempting to download JavaScript attachments +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/update-wiki-modal.yml b/changelogs/unreleased/update-wiki-modal.yml new file mode 100644 index 00000000000..00f2fc4f181 --- /dev/null +++ b/changelogs/unreleased/update-wiki-modal.yml @@ -0,0 +1,5 @@ +--- +title: New design for wiki page deletion confirmation +merge_request: 18712 +author: Constance Okoghenun +type: added diff --git a/config/initializers/deprecations.rb b/config/initializers/deprecations.rb index 2476ea9e38a..c8d7f742bb1 100644 --- a/config/initializers/deprecations.rb +++ b/config/initializers/deprecations.rb @@ -1,5 +1,9 @@ -deprecator = ActiveSupport::Deprecation.new('11.0', 'GitLab') - if Gitlab.dev_env_or_com? + deprecator = ActiveSupport::Deprecation.new('11.0', 'GitLab') + + deprecator.behavior = -> (message, callstack) { + Rails.logger.warn("#{message}: #{callstack[1..20].join}") + } + ActiveSupport::Deprecation.deprecate_methods(Gitlab::GitalyClient::StorageSettings, :legacy_disk_path, deprecator: deprecator) end diff --git a/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb new file mode 100644 index 00000000000..580f56007c7 --- /dev/null +++ b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb @@ -0,0 +1,15 @@ +class AddIndexOnCiRunnersRunnerType < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :ci_runners, :runner_type + end + + def down + remove_index :ci_runners, :runner_type + end +end diff --git a/db/schema.rb b/db/schema.rb index 95adccf0d5c..4abca1789a0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180508135515) do +ActiveRecord::Schema.define(version: 20180511090724) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -503,6 +503,7 @@ ActiveRecord::Schema.define(version: 20180508135515) do add_index "ci_runners", ["contacted_at"], name: "index_ci_runners_on_contacted_at", using: :btree add_index "ci_runners", ["is_shared"], name: "index_ci_runners_on_is_shared", using: :btree add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree + add_index "ci_runners", ["runner_type"], name: "index_ci_runners_on_runner_type", using: :btree add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree create_table "ci_stages", force: :cascade do |t| diff --git a/doc/README.md b/doc/README.md index c929ba7a59e..ff8dd3fab8a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -241,7 +241,7 @@ GitLab.com is hosted, managed, and administered by GitLab, Inc., with and teams: Free, Bronze, Silver, and Gold. GitLab.com subscriptions grants access -to the same features available in GitLab self-hosted, **expect +to the same features available in GitLab self-hosted, **except [administration](administration/index.md) tools and settings**: - GitLab.com Free includes the same features available in Core diff --git a/doc/api/jobs.md b/doc/api/jobs.md index db4fe2f6880..e4e48edd9a7 100644 --- a/doc/api/jobs.md +++ b/doc/api/jobs.md @@ -82,7 +82,7 @@ Example of response "artifacts_file": null, "finished_at": "2015-12-24T17:54:24.921Z", "id": 6, - "name": "spinach:other", + "name": "rspec:other", "pipeline": { "id": 6, "ref": "master", @@ -196,7 +196,7 @@ Example of response "artifacts_file": null, "finished_at": "2015-12-24T17:54:24.921Z", "id": 6, - "name": "spinach:other", + "name": "rspec:other", "pipeline": { "id": 6, "ref": "master", diff --git a/doc/development/code_review.md b/doc/development/code_review.md index 7165b8062a7..d03b7fa23ca 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -29,6 +29,10 @@ There are a few rules to get your merge request accepted: to ask one of the [Merge request coaches][team]. 1. The reviewer will assign the merge request to a maintainer once the reviewer is satisfied with the state of the merge request. +1. Keep in mind that maintainers are also going to perform a final code review. + The ideal scenario is that the reviewer has already addressed any concerns + the maintainer would have found, and the maintainer only has to perform the + merge, but be prepared for further review comments. For more guidance, see [CONTRIBUTING.md](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md). @@ -207,3 +211,4 @@ Largely based on the [thoughtbot code review guide]. [projects]: https://about.gitlab.com/handbook/engineering/projects/ [team]: https://about.gitlab.com/team/ [build handbook]: https://about.gitlab.com/handbook/build/handbook/build#how-to-work-with-build +[^1]: Please note that specs other than JavaScript specs are considered backend code. diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md index 32f392f1303..9c31265e417 100644 --- a/doc/development/database_debugging.md +++ b/doc/development/database_debugging.md @@ -11,7 +11,7 @@ Available `RAILS_ENV` - `production` (generally not for your main GDK db, but you may need this for e.g. omnibus) - `development` (this is your main GDK db) - - `test` (used for tests like rspec and spinach) + - `test` (used for tests like rspec) ## Nuke everything and start over diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index fdfa1f10402..31addcaf675 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -65,12 +65,11 @@ To make sure that indices still fit. You could find great details in: ## Run tests In order to run the test you can use the following commands: -- `rake spinach` to run the spinach suite - `rake spec` to run the rspec suite - `rake karma` to run the karma test suite - `rake gitlab:test` to run all the tests -Note: Both `rake spinach` and `rake spec` takes significant time to pass. +Note: `rake spec` takes significant time to pass. Instead of running full test suite locally you can save a lot of time by running a single test or directory related to your changes. After you submit merge request CI will run full test suite for you. Green CI status in the merge request means @@ -82,12 +81,10 @@ files it can find, also the ones in `/tmp` To run a single test file you can use: - `bin/rspec spec/controllers/commit_controller_spec.rb` for a rspec test -- `bin/spinach features/project/issues/milestones.feature` for a spinach test To run several tests inside one directory: - `bin/rspec spec/requests/api/` for the rspec tests if you want to test API only -- `bin/spinach features/profile/` for the spinach tests if you want to test only profile pages ### Speed-up tests, rake tasks, and migrations diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md index 61fa5459b91..9d3f2935127 100644 --- a/doc/development/testing_guide/best_practices.md +++ b/doc/development/testing_guide/best_practices.md @@ -12,8 +12,7 @@ Here are some things to keep in mind regarding test performance: - `FactoryBot.build(...)` and `.build_stubbed` are faster than `.create`. - Don't `create` an object when `build`, `build_stubbed`, `attributes_for`, `spy`, or `double` will do. Database persistence is slow! -- Don't mark a feature as requiring JavaScript (through `@javascript` in - Spinach or `:js` in RSpec) unless it's _actually_ required for the test +- Don't mark a feature as requiring JavaScript (through `:js` in RSpec) unless it's _actually_ required for the test to be valid. Headless browser testing is slow! [parallelization]: ci.md#test-suite-parallelization-on-the-ci diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md index e90de55068d..0d8e150e090 100644 --- a/doc/development/testing_guide/ci.md +++ b/doc/development/testing_guide/ci.md @@ -24,8 +24,7 @@ Our current CI parallelization setup is as follows: uploaded to S3. After that, the next pipeline will use the up-to-date -`knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file. The same strategy -is used for Spinach tests as well. +`knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file. ### Monitoring diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 0d0d511582b..3b2b9c8c947 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -280,26 +280,6 @@ describe "Admin::AbuseReports", :js do end ``` -### Spinach errors due to missing JavaScript - -NOTE: **Note:** Since we are discouraging the use of Spinach when writing new -feature tests, you shouldn't ever need to use this. This information is kept -available for legacy purposes only. - -In Spinach, the JavaScript driver is enabled differently. In the `*.feature` -file for the failing spec, add the `@javascript` flag above the Scenario: - -``` -@javascript -Scenario: Developer can approve merge request - Given I am a "Shop" developer - And I visit project "Shop" merge requests page - And merge request 'Bug NS-04' must be approved - And I click link "Bug NS-04" - When I click link "Approve" - Then I should see approved merge request "Bug NS-04" -``` - [jasmine-focus]: https://jasmine.github.io/2.5/focused_specs.html [jasmine-jquery]: https://github.com/velesin/jasmine-jquery [karma]: http://karma-runner.github.io/ diff --git a/doc/development/testing_guide/index.md b/doc/development/testing_guide/index.md index 74d09eb91ff..0cd63a54b55 100644 --- a/doc/development/testing_guide/index.md +++ b/doc/development/testing_guide/index.md @@ -72,21 +72,6 @@ Everything you should know about how to run end-to-end tests using --- -## Spinach (feature) tests - -GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426) -for its feature/integration tests in September 2012. - -As of March 2016, we are [trying to avoid adding new Spinach -tests](https://gitlab.com/gitlab-org/gitlab-ce/issues/14121) going forward, -opting for [RSpec feature](#features-integration) specs. - -Adding new Spinach scenarios is acceptable _only if_ the new scenario requires -no more than one new `step` definition. If more than that is required, the -test should be re-implemented using RSpec instead. - ---- - [Return to Development documentation](../README.md) [^1]: /ci/yaml/README.html#dependencies diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md index 51794f7f4df..07ced36f0c1 100644 --- a/doc/development/testing_guide/testing_levels.md +++ b/doc/development/testing_guide/testing_levels.md @@ -81,7 +81,6 @@ possible). | Tests path | Testing engine | Notes | | ---------- | -------------- | ----- | | `spec/features/` | [Capybara] + [RSpec] | If your spec has the `:js` metadata, the browser driver will be [Poltergeist], otherwise it's using [RackTest]. | -| `features/` | Spinach | Spinach tests are deprecated, [you shouldn't add new Spinach tests](#spinach-feature-tests). | ### Consider **not** writing a system test! diff --git a/doc/user/project/merge_requests/maintainer_access.md b/doc/user/project/merge_requests/maintainer_access.md index c9763a3fe02..89f71e16a50 100644 --- a/doc/user/project/merge_requests/maintainer_access.md +++ b/doc/user/project/merge_requests/maintainer_access.md @@ -16,3 +16,5 @@ source project, and only lasts while the merge request is open. Enable this functionality while creating a merge request:  + +[ce-17395]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17395 diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb deleted file mode 100644 index 97bcca7730b..00000000000 --- a/features/steps/group/members.rb +++ /dev/null @@ -1,68 +0,0 @@ -class Spinach::Features::GroupMembers < Spinach::FeatureSteps - include WaitForRequests - include SharedAuthentication - include SharedPaths - include SharedGroup - include SharedUser - - step 'I should see user "John Doe" in team list' do - expect(group_members_list).to have_content("John Doe") - end - - step 'I should not see user "Mary Jane" in team list' do - expect(group_members_list).not_to have_content("Mary Jane") - end - - step 'I click on the "Remove User From Group" button for "John Doe"' do - find(:css, '.project-members-page li', text: "John Doe").find(:css, 'a.btn-remove').click - # poltergeist always confirms popups. - end - - step 'I click on the "Remove User From Group" button for "Mary Jane"' do - find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click - # poltergeist always confirms popups. - end - - step 'I should not see the "Remove User From Group" button for "John Doe"' do - expect(find(:css, '.project-members-page li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove') - # poltergeist always confirms popups. - end - - step 'I should not see the "Remove User From Group" button for "Mary Jane"' do - expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove') - # poltergeist always confirms popups. - end - - step 'I change the "Mary Jane" role to "Developer"' do - member = mary_jane_member - - page.within "#group_member_#{member.id}" do - click_button member.human_access - - page.within '.dropdown-menu' do - click_link 'Developer' - end - - wait_for_requests - end - end - - step 'I should see "Mary Jane" as "Developer"' do - member = mary_jane_member - - page.within "#group_member_#{member.id}" do - expect(page).to have_content "Developer" - end - end - - private - - def mary_jane_member - user = User.find_by(name: "Mary Jane") - owned_group.members.find_by(user_id: user.id) - end - - def group_members_list - find(".panel .content-list") - end -end diff --git a/features/steps/profile/notifications.rb b/features/steps/profile/notifications.rb deleted file mode 100644 index f8eb0f01de8..00000000000 --- a/features/steps/profile/notifications.rb +++ /dev/null @@ -1,20 +0,0 @@ -class Spinach::Features::ProfileNotifications < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - - step 'I visit profile notifications page' do - visit profile_notifications_path - end - - step 'I should see global notifications settings' do - expect(page).to have_content "Notifications" - end - - step 'I select Mention setting from dropdown' do - first(:link, "On mention").click - end - - step 'I should see Notification saved message' do - expect(page).to have_content 'On mention' - end -end diff --git a/features/steps/project/commits/branches.rb b/features/steps/project/commits/branches.rb deleted file mode 100644 index 3ecd4c8b672..00000000000 --- a/features/steps/project/commits/branches.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedPaths - - step 'I click link "All"' do - click_link "All" - end - - step 'I click link "Protected"' do - click_link "Protected" - end - - step 'I click new branch link' do - click_link "New branch" - end - - step 'I submit new branch form with invalid name' do - fill_in 'branch_name', with: '1.0 stable' - page.find("body").click # defocus the branch_name input - select_branch('master') - click_button 'Create branch' - end - - def select_branch(branch_name) - find('.git-revision-dropdown-toggle').click - - page.within '#new-branch-form .dropdown-menu' do - click_link branch_name - end - end -end diff --git a/features/steps/project/commits/comments.rb b/features/steps/project/commits/comments.rb deleted file mode 100644 index 3d4d8ad6368..00000000000 --- a/features/steps/project/commits/comments.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Spinach::Features::ProjectCommitsComments < Spinach::FeatureSteps - include SharedAuthentication - include SharedNote - include SharedPaths - include SharedProject -end diff --git a/features/steps/project/commits/diff_comments.rb b/features/steps/project/commits/diff_comments.rb deleted file mode 100644 index b9d8cf2c5a5..00000000000 --- a/features/steps/project/commits/diff_comments.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Spinach::Features::ProjectCommitsDiffComments < Spinach::FeatureSteps - include SharedAuthentication - include SharedDiffNote - include SharedPaths - include SharedProject -end diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb deleted file mode 100644 index 60fa232672e..00000000000 --- a/features/steps/project/create.rb +++ /dev/null @@ -1,23 +0,0 @@ -class Spinach::Features::ProjectCreate < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedUser - - step 'fill project form with valid data' do - fill_in 'project_path', with: 'Empty' - page.within '#content-body' do - click_button "Create project" - end - end - - step 'I should see project page' do - expect(page).to have_content "Empty" - expect(current_path).to eq project_path(Project.last) - end - - step 'I should see empty project instructions' do - expect(page).to have_content "git init" - expect(page).to have_content "git remote" - expect(page).to have_content Project.last.url_to_repo - end -end diff --git a/features/steps/project/issues/filter_labels.rb b/features/steps/project/issues/filter_labels.rb deleted file mode 100644 index b467af53c98..00000000000 --- a/features/steps/project/issues/filter_labels.rb +++ /dev/null @@ -1,61 +0,0 @@ -class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedPaths - include Select2Helper - - step 'I should see "Bugfix1" in issues list' do - page.within ".issues-list" do - expect(page).to have_content "Bugfix1" - end - end - - step 'I should see "Bugfix2" in issues list' do - page.within ".issues-list" do - expect(page).to have_content "Bugfix2" - end - end - - step 'I should not see "Bugfix2" in issues list' do - page.within ".issues-list" do - expect(page).not_to have_content "Bugfix2" - end - end - - step 'I should not see "Feature1" in issues list' do - page.within ".issues-list" do - expect(page).not_to have_content "Feature1" - end - end - - step 'I click "dropdown close button"' do - page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click - sleep 2 - end - - step 'I click link "feature"' do - page.within ".labels-filter" do - click_link "feature" - end - end - - step 'project "Shop" has issue "Bugfix1" with labels: "bug", "feature"' do - project = Project.find_by(name: "Shop") - issue = create(:issue, title: "Bugfix1", project: project) - issue.labels << project.labels.find_by(title: 'bug') - issue.labels << project.labels.find_by(title: 'feature') - end - - step 'project "Shop" has issue "Bugfix2" with labels: "bug", "enhancement"' do - project = Project.find_by(name: "Shop") - issue = create(:issue, title: "Bugfix2", project: project) - issue.labels << project.labels.find_by(title: 'bug') - issue.labels << project.labels.find_by(title: 'enhancement') - end - - step 'project "Shop" has issue "Feature1" with labels: "feature"' do - project = Project.find_by(name: "Shop") - issue = create(:issue, title: "Feature1", project: project) - issue.labels << project.labels.find_by(title: 'feature') - end -end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb deleted file mode 100644 index baa78c23203..00000000000 --- a/features/steps/project/issues/issues.rb +++ /dev/null @@ -1,175 +0,0 @@ -class Spinach::Features::ProjectIssues < Spinach::FeatureSteps - include SharedAuthentication - include SharedIssuable - include SharedProject - include SharedNote - include SharedPaths - include SharedMarkdown - include SharedUser - - step 'I should not see "Release 0.3" in issues' do - expect(page).not_to have_content "Release 0.3" - end - - step 'I click link "Closed"' do - find('.issues-state-filters [data-state="closed"] span', text: 'Closed').click - end - - step 'I should see "Release 0.3" in issues' do - expect(page).to have_content "Release 0.3" - end - - step 'I should not see "Release 0.4" in issues' do - expect(page).not_to have_content "Release 0.4" - end - - step 'I click link "All"' do - find('.issues-state-filters [data-state="all"] span', text: 'All').click - # Waits for load - expect(find('.issues-state-filters > .active')).to have_content 'All' - end - - step 'I should see issue "Tweet control"' do - expect(page).to have_content "Tweet control" - end - - step 'I click "author" dropdown' do - page.find('.js-author-search').click - sleep 1 - end - - step 'I see current user as the first user' do - expect(page).to have_selector('.dropdown-content', visible: true) - users = page.all('.dropdown-menu-author .dropdown-content li a') - expect(users[0].text).to eq 'Any Author' - expect(users[1].text).to eq "#{current_user.name} #{current_user.to_reference}" - end - - step 'I click link "500 error on profile"' do - click_link "500 error on profile" - end - - step 'I should see label \'bug\' with issue' do - page.within '.issuable-show-labels' do - expect(page).to have_content 'bug' - end - end - - step 'I fill in issue search with "Re"' do - filter_issue "Re" - end - - step 'I fill in issue search with "Bu"' do - filter_issue "Bu" - end - - step 'I fill in issue search with ".3"' do - filter_issue ".3" - end - - step 'I fill in issue search with "Something"' do - filter_issue "Something" - end - - step 'I fill in issue search with ""' do - filter_issue "" - end - - step 'project "Shop" has milestone "v2.2"' do - milestone = create(:milestone, title: "v2.2", project: project) - - 3.times { create(:issue, project: project, milestone: milestone) } - end - - step 'project "Shop" has milestone "v3.0"' do - milestone = create(:milestone, title: "v3.0", project: project) - - 3.times { create(:issue, project: project, milestone: milestone) } - end - - When 'I select milestone "v3.0"' do - select "v3.0", from: "milestone_id" - end - - step 'I should see selected milestone with title "v3.0"' do - issues_milestone_selector = "#issue_milestone_id_chzn > a" - expect(find(issues_milestone_selector)).to have_content("v3.0") - end - - When 'I select first assignee from "Shop" project' do - first_assignee = project.users.first - select first_assignee.name, from: "assignee_id" - end - - step 'I should see first assignee from "Shop" as selected assignee' do - issues_assignee_selector = "#issue_assignee_id_chzn > a" - - assignee_name = project.users.first.name - expect(find(issues_assignee_selector)).to have_content(assignee_name) - end - - step 'The list should be sorted by "Least popular"' do - page.within '.issues-list' do - page.within 'li.issue:nth-child(1)' do - expect(page).to have_content 'Tweet control' - expect(page).to have_content '1 2' - end - - page.within 'li.issue:nth-child(2)' do - expect(page).to have_content 'Release 0.4' - expect(page).to have_content '2 1' - end - - page.within 'li.issue:nth-child(3)' do - expect(page).to have_content 'Bugfix' - expect(page).not_to have_content '0 0' - end - end - end - - When 'I visit empty project page' do - project = Project.find_by(name: 'Empty Project') - visit project_path(project) - end - - When "I visit project \"Community\" issues page" do - project = Project.find_by(name: 'Community') - visit project_issues_path(project) - end - - step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do - create(:issue, title: 'Bugfix1', description: 'Description for issue1', project: project) - end - - step 'project \'Shop\' has issue \'Feature1\' with description: \'Feature submitted for issue1\'' do - create(:issue, title: 'Feature1', description: 'Feature submitted for issue1', project: project) - end - - step 'I fill in issue search with \'Description for issue1\'' do - filter_issue 'Description for issue' - end - - step 'I fill in issue search with \'issue1\'' do - filter_issue 'issue1' - end - - step 'I fill in issue search with \'Rock and roll\'' do - filter_issue 'Rock and roll' - end - - step 'I should see \'Bugfix1\' in issues' do - expect(page).to have_content 'Bugfix1' - end - - step 'I should see \'Feature1\' in issues' do - expect(page).to have_content 'Feature1' - end - - step 'I should not see \'Bugfix1\' in issues' do - expect(page).not_to have_content 'Bugfix1' - end - - def filter_issue(text) - fill_in 'issuable_search', with: text - end -end diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb deleted file mode 100644 index 30927306a4f..00000000000 --- a/features/steps/project/issues/milestones.rb +++ /dev/null @@ -1,20 +0,0 @@ -class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedPaths - include SharedMarkdown - - step 'project "Shop" has milestone "v2.2"' do - project = Project.find_by(name: "Shop") - milestone = create(:milestone, - title: "v2.2", - project: project, - description: "# Description header" - ) - 3.times { create(:issue, project: project, milestone: milestone) } - end - - When 'I click link "All Issues"' do - click_link 'All Issues' - end -end diff --git a/features/steps/project/issues/references.rb b/features/steps/project/issues/references.rb deleted file mode 100644 index 69e8b5cbde5..00000000000 --- a/features/steps/project/issues/references.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Spinach::Features::ProjectIssuesReferences < Spinach::FeatureSteps - include SharedAuthentication - include SharedIssuable - include SharedNote - include SharedProject - include SharedUser -end diff --git a/features/steps/project/merge_requests/references.rb b/features/steps/project/merge_requests/references.rb deleted file mode 100644 index ab2ae6847a2..00000000000 --- a/features/steps/project/merge_requests/references.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Spinach::Features::ProjectMergeRequestsReferences < Spinach::FeatureSteps - include SharedAuthentication - include SharedIssuable - include SharedNote - include SharedProject - include SharedUser -end diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb deleted file mode 100644 index afaad4b255e..00000000000 --- a/features/steps/project/source/browse_files.rb +++ /dev/null @@ -1,435 +0,0 @@ -# coding: utf-8 -class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedPaths - include RepoHelpers - include WaitForRequests - - step "I don't have write access" do - @project = create(:project, :repository, name: "Other Project", path: "other-project") - @project.add_reporter(@user) - visit project_tree_path(@project, root_ref) - end - - step 'I should see files from repository' do - expect(page).to have_content "VERSION" - expect(page).to have_content ".gitignore" - expect(page).to have_content "LICENSE" - end - - step 'I should see files from repository for "6d39438"' do - expect(current_path).to eq project_tree_path(@project, "6d39438") - expect(page).to have_content ".gitignore" - expect(page).to have_content "LICENSE" - end - - step 'I see the ".gitignore"' do - expect(page).to have_content '.gitignore' - end - - step 'I don\'t see the ".gitignore"' do - expect(page).not_to have_content '.gitignore' - end - - step 'I click on ".gitignore" file in repo' do - click_link ".gitignore" - end - - step 'I should see its content' do - wait_for_requests - expect(page).to have_content old_gitignore_content - end - - step 'I should see its new content' do - wait_for_requests - expect(page).to have_content new_gitignore_content - end - - step 'I click link "Raw"' do - click_link 'Open raw' - end - - step 'I should see raw file content' do - expect(source).to eq '' # Body is filled in by gitlab-workhorse - end - - step 'I click button "Edit"' do - find('.js-edit-blob').click - end - - step 'I cannot see the edit button' do - expect(page).not_to have_link 'edit' - end - - step 'I click button "Fork"' do - click_link 'Fork' - end - - step 'I edit code' do - expect(page).to have_selector('.file-editor') - set_new_content - end - - step 'I fill the new file name' do - fill_in :file_name, with: new_file_name - end - - step 'I fill the new branch name' do - fill_in :branch_name, with: 'new_branch_name', visible: true - end - - step 'I fill the new file name with a new directory' do - fill_in :file_name, with: new_file_name_with_directory - end - - step 'I fill the commit message' do - fill_in :commit_message, with: 'New commit message', visible: true - end - - step 'I click link "Diff"' do - click_link 'Preview changes' - end - - step 'I click on "Commit changes"' do - click_button 'Commit changes' - end - - step 'I click on "Changes" tab' do - click_link 'Changes' - end - - step 'I click on "Create directory"' do - click_button 'Create directory' - end - - step 'I click on "Delete"' do - click_on 'Delete' - end - - step 'I click on "Delete file"' do - click_button 'Delete file' - end - - step 'I click on "Replace"' do - click_on "Replace" - end - - step 'I click on "Replace file"' do - click_button 'Replace file' - end - - step 'I see diff' do - expect(page).to have_css '.line_holder.new' - end - - step 'I click on "New file" link in repo' do - find('.add-to-tree').click - click_link 'New file' - expect(page).to have_selector('.file-editor') - end - - step 'I click on "Upload file" link in repo' do - find('.add-to-tree').click - click_link 'Upload file' - end - - step 'I click on "New directory" link in repo' do - find('.add-to-tree').click - click_link 'New directory' - end - - step 'I fill the new directory name' do - fill_in :dir_name, with: new_dir_name - end - - step 'I fill an existing directory name' do - fill_in :dir_name, with: 'files' - end - - step 'I can see new file page' do - expect(page).to have_content "New File" - expect(page).to have_content "Commit message" - end - - step 'I click on "Upload file"' do - click_button 'Upload file' - end - - step 'I can see the new commit message' do - expect(page).to have_content "New commit message" - end - - step 'I upload a new text file' do - drop_in_dropzone test_text_file - end - - step 'I fill the upload file commit message' do - page.within('#modal-upload-blob') do - fill_in :commit_message, with: 'New commit message' - end - end - - step 'I replace it with a text file' do - drop_in_dropzone test_text_file - end - - step 'I fill the replace file commit message' do - page.within('#modal-upload-blob') do - fill_in :commit_message, with: 'Replacement file commit message' - end - end - - step 'I can see the replacement commit message' do - expect(page).to have_content "Replacement file commit message" - end - - step 'I can see the new text file' do - expect(page).to have_content "Lorem ipsum dolor sit amet" - expect(page).to have_content "Sed ut perspiciatis unde omnis" - end - - step 'I click on files directory' do - click_link 'files' - end - - step 'I click on History link' do - click_link 'History' - end - - step 'I see Browse dir link' do - expect(page).to have_link 'Browse Directory' - expect(page).not_to have_link 'Browse Code' - end - - step 'I click on readme file' do - page.within '.tree-table' do - click_link 'README.md' - end - end - - step 'I see Browse file link' do - expect(page).to have_link 'Browse File' - expect(page).not_to have_link 'Browse Files' - end - - step 'I see Browse code link' do - expect(page).to have_link 'Browse Files' - expect(page).not_to have_link 'Browse Directory' - end - - step 'I click on Permalink' do - click_link 'Permalink' - end - - step 'I am redirected to the files URL' do - expect(current_path).to eq project_tree_path(@project, 'master') - end - - step 'I am redirected to the ".gitignore"' do - expect(current_path).to eq(project_blob_path(@project, 'master/.gitignore')) - end - - step 'I am redirected to the permalink URL' do - expect(current_path).to( - eq(project_blob_path(@project, - @project.repository.commit.sha + - '/.gitignore')) - ) - end - - step 'I am redirected to the new file' do - expect(current_path).to eq( - project_blob_path(@project, 'master/' + new_file_name)) - end - - step 'I am redirected to the new file with directory' do - expect(current_path).to eq( - project_blob_path(@project, 'master/' + new_file_name_with_directory)) - end - - step 'I am redirected to the new merge request page' do - expect(current_path).to eq(project_new_merge_request_path(@project)) - end - - step "I am redirected to the fork's new merge request page" do - fork = @user.fork_of(@project) - expect(current_path).to eq(project_new_merge_request_path(fork)) - end - - step 'I am redirected to the root directory' do - expect(current_path).to eq( - project_tree_path(@project, 'master')) - end - - step "I don't see the permalink link" do - expect(page).not_to have_link('permalink') - end - - step 'I see "Unable to create directory"' do - expect(page).to have_content('A directory with this name already exists') - end - - step 'I see "Path can contain only..."' do - expect(page).to have_content('Path can contain only') - end - - step 'I see a commit error message' do - expect(page).to have_content('Your changes could not be committed') - end - - step "I switch ref to 'test'" do - first('.js-project-refs-dropdown').click - - page.within '.project-refs-form' do - click_link "'test'" - end - end - - step "I switch ref to fix" do - first('.js-project-refs-dropdown').click - - page.within '.project-refs-form' do - click_link 'fix' - end - end - - step "I see the ref 'test' has been selected" do - expect(page).to have_selector '.dropdown-toggle-text', text: "'test'" - end - - step "I visit the 'test' tree" do - visit project_tree_path(@project, "'test'") - end - - step "I visit the fix tree" do - visit project_tree_path(@project, "fix/.testdir") - end - - step 'I see the commit data' do - expect(page).to have_css('.tree-commit-link', visible: true) - expect(page).not_to have_content('Loading commit data...') - end - - step 'I see the commit data for a directory with a leading dot' do - expect(page).to have_css('.tree-commit-link', visible: true) - expect(page).not_to have_content('Loading commit data...') - end - - step 'I click on "files/lfs/lfs_object.iso" file in repo' do - allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(true) - visit project_tree_path(@project, "lfs") - click_link 'files' - click_link "lfs" - click_link "lfs_object.iso" - end - - step 'I should see download link and object size' do - expect(page).to have_content 'Download (1.5 MB)' - end - - step 'I should not see lfs pointer details' do - expect(page).not_to have_content 'version https://git-lfs.github.com/spec/v1' - expect(page).not_to have_content 'oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' - expect(page).not_to have_content 'size 1575078' - end - - step 'I should see buttons for allowed commands' do - page.within '.content' do - expect(page).to have_link 'Download' - expect(page).to have_content 'History' - expect(page).to have_content 'Permalink' - expect(page).not_to have_content 'Edit' - expect(page).not_to have_content 'Blame' - expect(page).to have_content 'Delete' - expect(page).to have_content 'Replace' - end - end - - step 'I should see a Fork/Cancel combo' do - expect(page).to have_link 'Fork' - expect(page).to have_button 'Cancel' - end - - step 'I should see a notice about a new fork having been created' do - expect(page).to have_content "You're not allowed to make changes to this project directly. A fork of this project has been created that you can make changes in, so you can submit a merge request." - end - - # SVG files - step 'I upload a new SVG file' do - drop_in_dropzone test_svg_file - end - - step 'I visit the SVG file' do - visit project_blob_path(@project, 'new_branch_name/logo_sample.svg') - end - - step 'I can see the new rendered SVG image' do - expect(page).to have_css('.file-content img') - end - - private - - def set_new_content - find('#editor') - execute_script("ace.edit('editor').setValue('#{new_gitignore_content}')") - end - - # Content of the gitignore file on the seed repository. - def old_gitignore_content - '*.rbc' - end - - # Constant value that differs from the content - # of the gitignore of the seed repository. - def new_gitignore_content - old_gitignore_content + 'a' - end - - # Constant value that is a valid filename and - # not a filename present at root of the seed repository. - def new_file_name - 'not_a_file.md' - end - - # Constant value that is a valid filename with directory and - # not a filename present at root of the seed repository. - def new_file_name_with_directory - 'foo/bar/baz.txt' - end - - # Constant value that is a valid directory and - # not a directory present at root of the seed repository. - def new_dir_name - 'new_dir/subdir' - end - - def drop_in_dropzone(file_path) - # Generate a fake input selector - page.execute_script <<-JS - var fakeFileInput = window.$('<input/>').attr( - {id: 'fakeFileInput', type: 'file'} - ).appendTo('body'); - JS - # Attach the file to the fake input selector with Capybara - attach_file("fakeFileInput", file_path) - # Add the file to a fileList array and trigger the fake drop event - page.execute_script <<-JS - var fileList = [$('#fakeFileInput')[0].files[0]]; - var e = jQuery.Event('drop', { dataTransfer : { files : fileList } }); - $('.dropzone')[0].dropzone.listeners[0].events.drop(e); - JS - end - - def test_text_file - File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt') - end - - def test_image_file - File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') - end - - def test_svg_file - File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg') - end -end diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb deleted file mode 100644 index 104d024fee2..00000000000 --- a/features/steps/shared/active_tab.rb +++ /dev/null @@ -1,32 +0,0 @@ -module SharedActiveTab - include Spinach::DSL - include WaitForRequests - - after do - wait_for_requests if javascript_test? - end - - def ensure_active_main_tab(content) - expect(find('.sidebar-top-level-items > li.active')).to have_content(content) - end - - def ensure_active_sub_tab(content) - expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)')).to have_content(content) - end - - def ensure_active_sub_nav(content) - expect(find('.layout-nav .controls li.active')).to have_content(content) - end - - step 'no other main tabs should be active' do - expect(page).to have_selector('.sidebar-top-level-items > li.active', count: 1) - end - - step 'no other sub tabs should be active' do - expect(page).to have_selector('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)', count: 1) - end - - step 'no other sub navs should be active' do - expect(page).to have_selector('.layout-nav .controls li.active', count: 1) - end -end diff --git a/features/steps/shared/admin.rb b/features/steps/shared/admin.rb deleted file mode 100644 index ac0a1764147..00000000000 --- a/features/steps/shared/admin.rb +++ /dev/null @@ -1,11 +0,0 @@ -module SharedAdmin - include Spinach::DSL - - step 'there are projects in system' do - 2.times { create(:project, :repository) } - end - - step 'system has users' do - 2.times { create(:user) } - end -end diff --git a/features/steps/shared/authentication.rb b/features/steps/shared/authentication.rb deleted file mode 100644 index df1428d8266..00000000000 --- a/features/steps/shared/authentication.rb +++ /dev/null @@ -1,56 +0,0 @@ -require Rails.root.join('features', 'support', 'login_helpers') - -module SharedAuthentication - include Spinach::DSL - include LoginHelpers - - step 'I sign in as a user' do - sign_out(@user) if @user - - @user = create(:user) - sign_in(@user) - end - - step 'I sign in via the UI' do - gitlab_sign_in(create(:user)) - end - - step 'I should be redirected to sign in page' do - expect(current_path).to eq new_user_session_path - end - - step "I logout directly" do - gitlab_sign_out - end - - def current_user - @user || User.reorder(nil).first - end - - private - - def gitlab_sign_in(user) - visit new_user_session_path - - fill_in "user_login", with: user.email - fill_in "user_password", with: "12345678" - check 'user_remember_me' - click_button "Sign in" - - @user = user - end - - def gitlab_sign_out - return unless @user - - if Capybara.current_driver == Capybara.javascript_driver - find('.header-user-dropdown-toggle').click - click_link 'Sign out' - expect(page).to have_button('Sign in') - else - sign_out(@user) - end - - @user = nil - end -end diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb deleted file mode 100644 index 22f397947b0..00000000000 --- a/features/steps/shared/diff_note.rb +++ /dev/null @@ -1,44 +0,0 @@ -module SharedDiffNote - include Spinach::DSL - include RepoHelpers - include WaitForRequests - - after do - wait_for_requests if javascript_test? - end - - step 'I delete a diff comment' do - find('.note').hover - find(".js-note-delete").click - end - - step 'I haven\'t written any diff comment text' do - page.within(diff_file_selector) do - fill_in "note[note]", with: "" - end - end - - step 'The diff comment preview tab should say there is nothing to do' do - page.within(diff_file_selector) do - find('.js-md-preview-button').click - expect(find('.js-md-preview')).to have_content('Nothing to preview.') - end - end - - step 'I see side-by-side diff button' do - expect(page).to have_content "Side-by-side" - end - - def diff_file_selector - '.diff-file:nth-of-type(1)' - end - - def click_diff_line(code) - find(".line_holder[id='#{code}'] button").click - end - - def click_parallel_diff_line(code, line_type) - find(".line_holder.parallel td[id='#{code}']").find(:xpath, 'preceding-sibling::*[1][self::td]').hover - find(".line_holder.parallel button[data-line-code='#{code}']").click - end -end diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb deleted file mode 100644 index 0126ce39c5a..00000000000 --- a/features/steps/shared/group.rb +++ /dev/null @@ -1,46 +0,0 @@ -module SharedGroup - include Spinach::DSL - - step 'current user is developer of group "Owned"' do - is_member_of(current_user.name, "Owned", Gitlab::Access::DEVELOPER) - end - - step '"John Doe" is guest of group "Guest"' do - is_member_of("John Doe", "Guest", Gitlab::Access::GUEST) - end - - step '"Mary Jane" is owner of group "Owned"' do - is_member_of("Mary Jane", "Owned", Gitlab::Access::OWNER) - end - - step '"Mary Jane" is guest of group "Owned"' do - is_member_of("Mary Jane", "Owned", Gitlab::Access::GUEST) - end - - step '"Mary Jane" is guest of group "Guest"' do - is_member_of("Mary Jane", "Guest", Gitlab::Access::GUEST) - end - - step 'I should see group "TestGroup"' do - expect(page).to have_content "TestGroup" - end - - step 'I should not see group "TestGroup"' do - expect(page).not_to have_content "TestGroup" - end - - protected - - def is_member_of(username, groupname, role) - user = User.find_by(name: username) || create(:user, name: username) - group = Group.find_by(name: groupname) || create(:group, name: groupname) - group.add_user(user, role) - project ||= create(:project, :repository, namespace: group) - create(:closed_issue_event, project: project) - project.add_master(user) - end - - def owned_group - @owned_group ||= Group.find_by(name: "Owned") - end -end diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb deleted file mode 100644 index cc6fd48935f..00000000000 --- a/features/steps/shared/issuable.rb +++ /dev/null @@ -1,78 +0,0 @@ -module SharedIssuable - include Spinach::DSL - - def edit_issuable - find('.js-issuable-edit', visible: true).click - end - - step 'I leave a comment referencing issue "Community issue"' do - leave_reference_comment( - issuable: Issue.find_by(title: 'Community issue'), - from_project_name: 'Enterprise' - ) - end - - step 'I click link "Edit" for the merge request' do - edit_issuable - end - - step 'I sort the list by "Least popular"' do - find('button.dropdown-toggle').click - - page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do - click_link 'Least popular' - end - end - - step 'I click link "Next" in the sidebar' do - page.within '.issuable-sidebar' do - click_link 'Next' - end - end - - def create_issuable_for_project(project_name:, title:, type: :issue) - project = Project.find_by(name: project_name) - - attrs = { - title: title, - author: project.users.first, - description: '# Description header' - } - - case type - when :issue - attrs[:project] = project - when :merge_request - attrs.merge!( - source_project: project, - target_project: project, - source_branch: 'fix', - target_branch: 'master' - ) - end - - create(type, attrs) - end - - def leave_reference_comment(issuable:, from_project_name:) - project = Project.find_by(name: from_project_name) - - page.within('.js-main-target-form') do - fill_in 'note[note]', with: "##{issuable.to_reference(project)}" - click_button 'Comment' - end - end - - def visible_note(issuable:, from_project_name:, user_name:) - project = Project.find_by(name: from_project_name) - - expect(page).to have_content(user_name) - expect(page).to have_content("mentioned in #{issuable.class.to_s.titleize.downcase} #{issuable.to_reference(project)}") - end - - def expect_sidebar_content(content) - page.within '.issuable-sidebar' do - expect(page).to have_content content - end - end -end diff --git a/features/steps/shared/markdown.rb b/features/steps/shared/markdown.rb deleted file mode 100644 index 65118f07ca2..00000000000 --- a/features/steps/shared/markdown.rb +++ /dev/null @@ -1,11 +0,0 @@ -module SharedMarkdown - include Spinach::DSL - - step 'I should not see the Markdown preview' do - expect(find('.gfm-form .js-md-preview')).not_to be_visible - end - - step 'I haven\'t written any description text' do - find('.gfm-form').fill_in 'Description', with: '' - end -end diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb deleted file mode 100644 index 4a6dee3c7b8..00000000000 --- a/features/steps/shared/note.rb +++ /dev/null @@ -1,21 +0,0 @@ -module SharedNote - include Spinach::DSL - include WaitForRequests - - after do - wait_for_requests if javascript_test? - end - - step 'I haven\'t written any comment text' do - page.within(".js-main-target-form") do - fill_in "note[note]", with: "" - end - end - - step 'The comment preview tab should say there is nothing to do' do - page.within(".js-main-target-form") do - find('.js-md-preview-button').click - expect(find('.js-md-preview')).to have_content('Nothing to preview.') - end - end -end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb deleted file mode 100644 index 8f2f5ca26a9..00000000000 --- a/features/steps/shared/paths.rb +++ /dev/null @@ -1,413 +0,0 @@ -module SharedPaths - include Spinach::DSL - include RepoHelpers - include DashboardHelper - include WaitForRequests - - step 'I visit new project page' do - visit new_project_path - end - - step 'I visit login page' do - visit new_user_session_path - end - - # ---------------------------------------- - # User - # ---------------------------------------- - - step 'I visit user "John Doe" page' do - visit user_path("john_doe") - end - - # ---------------------------------------- - # Group - # ---------------------------------------- - - step 'I visit group "Owned" page' do - visit group_path(Group.find_by(name: "Owned")) - end - - step 'I visit group "Owned" activity page' do - visit activity_group_path(Group.find_by(name: "Owned")) - end - - step 'I visit group "Owned" issues page' do - visit issues_group_path(Group.find_by(name: "Owned")) - end - - step 'I visit group "Owned" merge requests page' do - visit merge_requests_group_path(Group.find_by(name: "Owned")) - end - - step 'I visit group "Owned" milestones page' do - visit group_milestones_path(Group.find_by(name: "Owned")) - end - - step 'I visit group "Owned" members page' do - visit group_group_members_path(Group.find_by(name: "Owned")) - end - - step 'I visit group "Owned" projects page' do - visit projects_group_path(Group.find_by(name: "Owned")) - end - - step 'I visit group "Guest" page' do - visit group_path(Group.find_by(name: "Guest")) - end - - step 'I visit group "Guest" issues page' do - visit issues_group_path(Group.find_by(name: "Guest")) - end - - step 'I visit group "Guest" merge requests page' do - visit merge_requests_group_path(Group.find_by(name: "Guest")) - end - - step 'I visit group "Guest" members page' do - visit group_group_members_path(Group.find_by(name: "Guest")) - end - - step 'I visit group "Guest" settings page' do - visit edit_group_path(Group.find_by(name: "Guest")) - end - - # ---------------------------------------- - # Dashboard - # ---------------------------------------- - - step 'I visit dashboard page' do - visit dashboard_projects_path - end - - step 'I visit dashboard activity page' do - visit activity_dashboard_path - end - - step 'I visit dashboard projects page' do - visit projects_dashboard_path - end - - step 'I visit dashboard issues page' do - visit assigned_issues_dashboard_path - end - - step 'I visit dashboard search page' do - visit search_path - end - - step 'I visit dashboard help page' do - visit help_path - end - - step 'I visit dashboard groups page' do - visit dashboard_groups_path - end - - step 'I should be redirected to the dashboard groups page' do - expect(current_path).to eq dashboard_groups_path - end - - step 'I visit dashboard starred projects page' do - visit starred_dashboard_projects_path - end - - # ---------------------------------------- - # Profile - # ---------------------------------------- - - step 'I visit profile page' do - visit profile_path - end - - step 'I visit profile applications page' do - visit applications_profile_path - end - - step 'I visit profile password page' do - visit edit_profile_password_path - end - - step 'I visit profile account page' do - visit profile_account_path - end - - step 'I visit profile SSH keys page' do - visit profile_keys_path - end - - step 'I visit profile preferences page' do - visit profile_preferences_path - end - - step 'I visit Authentication log page' do - visit audit_log_profile_path - end - - # ---------------------------------------- - # Admin - # ---------------------------------------- - - step 'I visit admin page' do - visit admin_root_path - end - - step 'I visit abuse reports page' do - visit admin_abuse_reports_path - end - - step 'I visit admin projects page' do - visit admin_projects_path - end - - step 'I visit admin users page' do - visit admin_users_path - end - - step 'I visit admin logs page' do - visit admin_logs_path - end - - step 'I visit admin messages page' do - visit admin_broadcast_messages_path - end - - step 'I visit admin hooks page' do - visit admin_hooks_path - end - - step 'I visit admin Resque page' do - visit admin_background_jobs_path - end - - step 'I visit admin teams page' do - visit admin_teams_path - end - - step 'I visit spam logs page' do - visit admin_spam_logs_path - end - - # ---------------------------------------- - # Generic Project - # ---------------------------------------- - - step "I visit my project's settings page" do - visit edit_project_path(@project) - end - - step 'I visit a binary file in the repo' do - visit project_blob_path(@project, - File.join(root_ref, 'files/images/logo-black.png')) - end - - step "I visit my project's commits page" do - visit project_commits_path(@project, root_ref, { limit: 5 }) - end - - step "I visit my project's commits page for a specific path" do - visit project_commits_path(@project, root_ref + "/files/ruby/regex.rb", { limit: 5 }) - end - - step 'I visit my project\'s commits stats page' do - visit stats_project_repository_path(@project) - end - - step "I visit my project's graph page" do - # Stub Graph max_size to speed up test (10 commits vs. 650) - Network::Graph.stub(max_count: 10) - - visit project_network_path(@project, root_ref) - end - - step "I visit my project's issues page" do - visit project_issues_path(@project) - end - - step "I visit my project's merge requests page" do - visit project_merge_requests_path(@project) - end - - step "I visit my project's members page" do - visit project_project_members_path(@project) - end - - step "I visit my project's wiki page" do - visit project_wiki_path(@project, :home) - end - - step 'I visit project hooks page' do - visit project_settings_integrations_path(@project) - end - - step 'I visit project find file page' do - visit project_find_file_path(@project, root_ref) - end - - # ---------------------------------------- - # "Shop" Project - # ---------------------------------------- - - step 'I visit project "Shop" page' do - visit project_path(project) - end - - step 'I visit edit project "Shop" page' do - visit edit_project_path(project) - end - - step 'I visit compare refs page' do - visit project_compare_index_path(@project) - end - - step 'I visit project commits page' do - visit project_commits_path(@project, root_ref, { limit: 5 }) - end - - step 'I visit project commits page for stable branch' do - visit project_commits_path(@project, 'stable', { limit: 5 }) - end - - step 'I visit blob file from repo' do - visit project_blob_path(@project, File.join(sample_commit.id, sample_blob.path)) - end - - step 'I visit ".gitignore" file in repo' do - visit project_blob_path(@project, File.join(root_ref, '.gitignore')) - end - - step 'I am on the new file page' do - expect(current_path).to eq(project_create_blob_path(@project, root_ref)) - end - - step 'I am on the ".gitignore" edit file page' do - expect(current_path).to eq( - project_edit_blob_path(@project, File.join(root_ref, '.gitignore'))) - end - - step 'I visit project source page for "6d39438"' do - visit project_tree_path(@project, "6d39438") - end - - step 'I visit project source page for' \ - ' "6d394385cf567f80a8fd85055db1ab4c5295806f"' do - visit project_tree_path(@project, - '6d394385cf567f80a8fd85055db1ab4c5295806f') - end - - step 'I visit project tags page' do - visit project_tags_path(@project) - end - - step 'I visit issue page "Release 0.4"' do - issue = Issue.find_by(title: "Release 0.4") - visit project_issue_path(issue.project, issue) - end - - step 'I visit project "Forum" labels page' do - project = Project.find_by(name: 'Forum') - visit project_labels_path(project) - end - - step 'I visit project "Shop" new label page' do - project = Project.find_by(name: 'Shop') - visit new_project_label_path(project) - end - - step 'I visit project "Forum" new label page' do - project = Project.find_by(name: 'Forum') - visit new_project_label_path(project) - end - - step 'I visit merge request page "Bug NS-04"' do - visit merge_request_path("Bug NS-04") - wait_for_requests - end - - step 'I visit merge request page "Bug NS-07"' do - visit merge_request_path("Bug NS-07") - wait_for_requests - end - - step 'I visit merge request page "Bug NS-08"' do - visit merge_request_path("Bug NS-08") - wait_for_requests - end - - step 'I visit merge request page "Bug CO-01"' do - mr = MergeRequest.find_by(title: "Bug CO-01") - visit project_merge_request_path(mr.target_project, mr) - wait_for_requests - end - - step 'I visit forked project "Shop" merge requests page' do - visit project_merge_requests_path(project) - end - - step 'I visit project "Shop" team page' do - visit project_project_members_path(project) - end - - step 'I visit project wiki page' do - visit project_wiki_path(@project, :home) - end - - # ---------------------------------------- - # Visibility Projects - # ---------------------------------------- - - step 'I visit project "Community" source page' do - project = Project.find_by(name: 'Community') - visit project_tree_path(project, root_ref) - end - - step 'I visit project "Internal" page' do - project = Project.find_by(name: "Internal") - visit project_path(project) - end - - step 'I visit project "Enterprise" page' do - project = Project.find_by(name: "Enterprise") - visit project_path(project) - end - - # ---------------------------------------- - # Empty Projects - # ---------------------------------------- - - step "I should not see command line instructions" do - expect(page).not_to have_css('.empty_wrapper') - end - - # ---------------------------------------- - # Public Projects - # ---------------------------------------- - step 'I visit the public groups area' do - visit explore_groups_path - end - - # ---------------------------------------- - # Snippets - # ---------------------------------------- - - step 'I visit project "Shop" snippets page' do - visit project_snippets_path(project) - end - - step 'I visit snippets page' do - visit explore_snippets_path - end - - def root_ref - @project.repository.root_ref - end - - def project - Project.find_by!(name: 'Shop') - end - - def merge_request_path(title) - mr = MergeRequest.find_by(title: title) - project_merge_request_path(mr.target_project, mr) - end -end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb deleted file mode 100644 index d0277257320..00000000000 --- a/features/steps/shared/project.rb +++ /dev/null @@ -1,102 +0,0 @@ -module SharedProject - include Spinach::DSL - - # Create a project without caring about what it's called - step "I own a project" do - @project = create(:project, :repository, namespace: @user.namespace) - @project.add_master(@user) - end - - step "I own a project in some group namespace" do - @group = create(:group, name: 'some group') - @project = create(:project, namespace: @group) - @project.add_master(@user) - end - - def current_project - @project ||= Project.first - end - - # ---------------------------------------- - # Visibility of archived project - # ---------------------------------------- - - step 'I should not see project "Archive"' do - project = Project.find_by(name: "Archive") - expect(page).not_to have_content project.full_name - end - - step 'I should see project "Archive"' do - project = Project.find_by(name: "Archive") - expect(page).to have_content project.full_name - end - - # ---------------------------------------- - # Visibility level - # ---------------------------------------- - - step 'I should see project "Enterprise"' do - expect(page).to have_content "Enterprise" - end - - step 'I should not see project "Enterprise"' do - expect(page).not_to have_content "Enterprise" - end - - step 'internal project "Internal"' do - create(:project, :internal, :repository, name: 'Internal') - end - - step 'I should see project "Internal"' do - page.within '.js-projects-list-holder' do - expect(page).to have_content "Internal" - end - end - - step 'I should not see project "Internal"' do - page.within '.js-projects-list-holder' do - expect(page).not_to have_content "Internal" - end - end - - step 'I should see project "Community"' do - expect(page).to have_content "Community" - end - - step 'I should not see project "Community"' do - expect(page).not_to have_content "Community" - end - - step '"John Doe" owns private project "Enterprise"' do - user_owns_project( - user_name: 'John Doe', - project_name: 'Enterprise' - ) - end - - step '"John Doe" owns internal project "Internal"' do - user_owns_project( - user_name: 'John Doe', - project_name: 'Internal', - visibility: :internal - ) - end - - step 'public empty project "Empty Public Project"' do - create :project_empty_repo, :public, name: "Empty Public Project" - end - - step 'project "Shop" has labels: "bug", "feature", "enhancement"' do - project = Project.find_by(name: "Shop") - create(:label, project: project, title: 'bug') - create(:label, project: project, title: 'feature') - create(:label, project: project, title: 'enhancement') - end - - def user_owns_project(user_name:, project_name:, visibility: :private) - user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore) - project = Project.find_by(name: project_name) - project ||= create(:project, visibility, name: project_name, namespace: user.namespace) - project.add_master(user) - end -end diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb deleted file mode 100644 index 5a516ee33bc..00000000000 --- a/features/steps/shared/project_tab.rb +++ /dev/null @@ -1,66 +0,0 @@ -require_relative 'active_tab' - -module SharedProjectTab - include Spinach::DSL - include SharedActiveTab - - step 'the active main tab should be Project' do - ensure_active_main_tab('Overview') - end - - step 'the active main tab should be Repository' do - ensure_active_main_tab('Repository') - end - - step 'the active main tab should be Issues' do - ensure_active_main_tab('Issues') - end - - step 'the active sub tab should be Members' do - ensure_active_sub_tab('Members') - end - - step 'the active main tab should be Merge Requests' do - ensure_active_main_tab('Merge Requests') - end - - step 'the active main tab should be Snippets' do - ensure_active_main_tab('Snippets') - end - - step 'the active main tab should be Wiki' do - ensure_active_main_tab('Wiki') - end - - step 'the active main tab should be Members' do - ensure_active_main_tab('Members') - end - - step 'the active main tab should be Settings' do - ensure_active_main_tab('Settings') - end - - step 'the active sub tab should be Graph' do - ensure_active_sub_tab('Graph') - end - - step 'the active sub tab should be Files' do - ensure_active_sub_tab('Files') - end - - step 'the active sub tab should be Commits' do - ensure_active_sub_tab('Commits') - end - - step 'the active sub tab should be Home' do - ensure_active_sub_tab('Details') - end - - step 'the active sub tab should be Activity' do - ensure_active_sub_tab('Activity') - end - - step 'the active sub tab should be Charts' do - ensure_active_sub_tab('Charts') - end -end diff --git a/features/steps/shared/shortcuts.rb b/features/steps/shared/shortcuts.rb deleted file mode 100644 index a75a8474d26..00000000000 --- a/features/steps/shared/shortcuts.rb +++ /dev/null @@ -1,18 +0,0 @@ -module SharedShortcuts - include Spinach::DSL - - step 'I press "g" and "p"' do - find('body').native.send_key('g') - find('body').native.send_key('p') - end - - step 'I press "g" and "i"' do - find('body').native.send_key('g') - find('body').native.send_key('i') - end - - step 'I press "g" and "m"' do - find('body').native.send_key('g') - find('body').native.send_key('m') - end -end diff --git a/features/steps/shared/sidebar_active_tab.rb b/features/steps/shared/sidebar_active_tab.rb deleted file mode 100644 index 07fff16e867..00000000000 --- a/features/steps/shared/sidebar_active_tab.rb +++ /dev/null @@ -1,31 +0,0 @@ -module SharedSidebarActiveTab - include Spinach::DSL - - step 'no other main tabs should be active' do - expect(page).to have_selector('.nav-sidebar li.active', count: 1) - end - - def ensure_active_main_tab(content) - expect(find('.nav-sidebar li.active')).to have_content(content) - end - - step 'the active main tab should be Home' do - ensure_active_main_tab('Projects') - end - - step 'the active main tab should be Groups' do - ensure_active_main_tab('Groups') - end - - step 'the active main tab should be Projects' do - ensure_active_main_tab('Projects') - end - - step 'the active main tab should be Issues' do - ensure_active_main_tab('Issues') - end - - step 'the active main tab should be Merge Requests' do - ensure_active_main_tab('Merge Requests') - end -end diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb deleted file mode 100644 index 9cadc91769d..00000000000 --- a/features/steps/shared/user.rb +++ /dev/null @@ -1,41 +0,0 @@ -module SharedUser - include Spinach::DSL - - step 'User "John Doe" exists' do - user_exists("John Doe", { username: "john_doe" }) - end - - step 'User "Mary Jane" exists' do - user_exists("Mary Jane", { username: "mary_jane" }) - end - - step 'gitlab user "Mike"' do - create(:user, name: "Mike") - end - - protected - - def user_exists(name, options = {}) - User.find_by(name: name) || create(:user, { name: name, admin: false }.merge(options)) - end - - step 'I have no ssh keys' do - @user.keys.delete_all - end - - step 'I click on "Personal projects" tab' do - page.within '.nav-links' do - click_link 'Personal projects' - end - - expect(page).to have_css('.tab-content #projects.active') - end - - step 'I click on "Contributed projects" tab' do - page.within '.nav-links' do - click_link 'Contributed projects' - end - - expect(page).to have_css('.tab-content #contributed.active') - end -end diff --git a/features/support/capybara.rb b/features/support/capybara.rb deleted file mode 100644 index 8879c9ab650..00000000000 --- a/features/support/capybara.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'capybara-screenshot/spinach' - -# Give CI some extra time -timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30 - -Capybara.register_driver :chrome do |app| - capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( - # This enables access to logs with `page.driver.manage.get_log(:browser)` - loggingPrefs: { - browser: "ALL", - client: "ALL", - driver: "ALL", - server: "ALL" - } - ) - - options = Selenium::WebDriver::Chrome::Options.new - options.add_argument("window-size=1240,1400") - - # Chrome won't work properly in a Docker container in sandbox mode - options.add_argument("no-sandbox") - - # Run headless by default unless CHROME_HEADLESS specified - options.add_argument("headless") unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i - - # Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab-ee/issues/4252 - options.add_argument("disable-dev-shm-usage") if ENV['CI'] || ENV['CI_SERVER'] - - Capybara::Selenium::Driver.new( - app, - browser: :chrome, - desired_capabilities: capabilities, - options: options - ) -end - -Capybara.javascript_driver = :chrome -Capybara.default_max_wait_time = timeout -Capybara.ignore_hidden_elements = false - -# Keep only the screenshots generated from the last failing test suite -Capybara::Screenshot.prune_strategy = :keep_last_run -# From https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326 -Capybara::Screenshot.register_driver(:chrome) do |driver, path| - driver.browser.save_screenshot(path) -end - -Spinach.hooks.before_run do - TestEnv.eager_load_driver_server -end diff --git a/features/support/db_cleaner.rb b/features/support/db_cleaner.rb deleted file mode 100644 index 31c922d23c3..00000000000 --- a/features/support/db_cleaner.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'database_cleaner' - -DatabaseCleaner[:active_record].strategy = :deletion - -Spinach.hooks.before_scenario do - DatabaseCleaner.start -end - -Spinach.hooks.after_scenario do - DatabaseCleaner.clean -end diff --git a/features/support/env.rb b/features/support/env.rb deleted file mode 100644 index 8fa2fcb6e3e..00000000000 --- a/features/support/env.rb +++ /dev/null @@ -1,60 +0,0 @@ -require './spec/simplecov_env' -SimpleCovEnv.start! - -ENV['RAILS_ENV'] = 'test' -require './config/environment' -require 'rspec/expectations' - -if ENV['CI'] - require 'knapsack' - Knapsack::Adapters::SpinachAdapter.bind -end - -WebMock.enable! - -%w(select2_helper test_env repo_helpers wait_for_requests project_forks_helper).each do |f| - require Rails.root.join('spec', 'support', 'helpers', f) -end - -%w(sidekiq webmock).each do |f| - require Rails.root.join('spec', 'support', f) -end - -Dir["#{Rails.root}/features/steps/shared/*.rb"].each { |file| require file } - -Spinach.hooks.before_run do - include RSpec::Mocks::ExampleMethods - include ActiveJob::TestHelper - include FactoryBot::Syntax::Methods - include GitlabRoutingHelper - - RSpec::Mocks.setup - TestEnv.init(mailer: false) - - # skip pre-receive hook check so we can use - # web editor and merge - TestEnv.disable_pre_receive -end - -Spinach.hooks.after_scenario do |scenario_data, step_definitions| - if scenario_data.tags.include?('javascript') - include WaitForRequests - block_and_wait_for_requests_complete - end -end - -module StdoutReporterWithScenarioLocation - # Override the standard reporter to show filename and line number next to each - # scenario for easy, focused re-runs - def before_scenario_run(scenario, step_definitions = nil) - @max_step_name_length = scenario.steps.map(&:name).map(&:length).max if scenario.steps.any? # rubocop:disable Gitlab/ModuleWithInstanceVariables - name = scenario.name - - # This number has no significance, it's just to line things up - max_length = @max_step_name_length + 19 # rubocop:disable Gitlab/ModuleWithInstanceVariables - out.puts "\n #{'Scenario:'.green} #{name.light_green.ljust(max_length)}" \ - " # #{scenario.feature.filename}:#{scenario.line}" - end -end - -Spinach::Reporter::Stdout.prepend(StdoutReporterWithScenarioLocation) diff --git a/features/support/gitaly.rb b/features/support/gitaly.rb deleted file mode 100644 index 3cd5f4ce497..00000000000 --- a/features/support/gitaly.rb +++ /dev/null @@ -1,3 +0,0 @@ -Spinach.hooks.before_scenario do - allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(true) -end diff --git a/features/support/login_helpers.rb b/features/support/login_helpers.rb deleted file mode 100644 index 540ff25a4f2..00000000000 --- a/features/support/login_helpers.rb +++ /dev/null @@ -1,19 +0,0 @@ -module LoginHelpers - # After inclusion, IntegrationHelpers calls these two methods that aren't - # supported by Spinach, so we perform the end results ourselves - class << self - def setup(*args) - Spinach.hooks.before_scenario do - Warden.test_mode! - end - end - - def teardown(*args) - Spinach.hooks.after_scenario do - Warden.test_reset! - end - end - end - - include Devise::Test::IntegrationHelpers -end diff --git a/features/support/rerun.rb b/features/support/rerun.rb deleted file mode 100644 index 60b78f9d050..00000000000 --- a/features/support/rerun.rb +++ /dev/null @@ -1,16 +0,0 @@ -# The spinach-rerun-reporter doesn't define the on_undefined_step -# See it here: https://github.com/javierav/spinach-rerun-reporter/blob/master/lib/spinach/reporter/rerun.rb -require 'spinach-rerun-reporter' - -module Spinach - class Reporter - class Rerun - def on_undefined_step(step_data, failure, step_definitions = nil) - super step_data, failure, step_definitions - - # save feature file and scenario line - @rerun << "#{current_feature.filename}:#{current_scenario.line}" - end - end - end -end diff --git a/lib/gitlab/auth/blocked_user_tracker.rb b/lib/gitlab/auth/blocked_user_tracker.rb index dae03a179e4..7609a7b04f6 100644 --- a/lib/gitlab/auth/blocked_user_tracker.rb +++ b/lib/gitlab/auth/blocked_user_tracker.rb @@ -17,7 +17,9 @@ module Gitlab # message passed along by Warden. return unless message == User::BLOCKED_MESSAGE - login = env.dig(ACTIVE_RECORD_REQUEST_PARAMS, 'user', 'login') + # Check for either LDAP or regular GitLab account logins + login = env.dig(ACTIVE_RECORD_REQUEST_PARAMS, 'username') || + env.dig(ACTIVE_RECORD_REQUEST_PARAMS, 'user', 'login') return unless login.present? diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 390efda326a..2e9b6e302f5 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -59,7 +59,7 @@ module Gitlab startline = 0 result.each_line.each_with_index do |line, index| - prefix ||= line.match(/^(?<ref>[^:]*):(?<filename>.*)\x00(?<startline>\d+)\x00/)&.tap do |matches| + prefix ||= line.match(/^(?<ref>[^:]*):(?<filename>[^\x00]*)\x00(?<startline>\d+)\x00/)&.tap do |matches| ref = matches[:ref] filename = matches[:filename] startline = matches[:startline] diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb index 7ce2e9d636e..75ba0799058 100644 --- a/lib/gitlab/untrusted_regexp.rb +++ b/lib/gitlab/untrusted_regexp.rb @@ -11,7 +11,11 @@ module Gitlab class UntrustedRegexp delegate :===, to: :regexp - def initialize(pattern) + def initialize(pattern, multiline: false) + if multiline + pattern = "(?m)#{pattern}" + end + @regexp = RE2::Regexp.new(pattern, log_errors: false) raise RegexpError.new(regexp.error) unless regexp.ok? @@ -31,6 +35,19 @@ module Gitlab RE2.Replace(text, regexp, rewrite) end + # Handles regular expressions with the preferred RE2 library where possible + # via UntustedRegex. Falls back to Ruby's built-in regular expression library + # when the syntax would be invalid in RE2. + # + # One difference between these is `(?m)` multi-line mode. Ruby regex enables + # this by default, but also handles `^` and `$` differently. + # See: https://www.regular-expressions.info/modifiers.html + def self.with_fallback(pattern, multiline: false) + UntrustedRegexp.new(pattern, multiline: multiline) + rescue RegexpError + Regexp.new(pattern) + end + private attr_reader :regexp diff --git a/lib/tasks/gitlab/test.rake b/lib/tasks/gitlab/test.rake index 523b0fa055b..2222807fe13 100644 --- a/lib/tasks/gitlab/test.rake +++ b/lib/tasks/gitlab/test.rake @@ -4,7 +4,6 @@ namespace :gitlab do cmds = [ %w(rake brakeman), %w(rake rubocop), - %w(rake spinach), %w(rake spec), %w(rake karma) ] diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake deleted file mode 100644 index 19ff13f06c0..00000000000 --- a/lib/tasks/spinach.rake +++ /dev/null @@ -1,60 +0,0 @@ -Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach') - -namespace :spinach do - namespace :project do - desc "GitLab | Spinach | Run project commits, issues and merge requests spinach features" - task :half do - run_spinach_tests('@project_commits,@project_issues,@project_merge_requests') - end - - desc "GitLab | Spinach | Run remaining project spinach features" - task :rest do - run_spinach_tests('~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests') - end - end - - desc "GitLab | Spinach | Run project spinach features" - task :project do - run_spinach_tests('~@admin,~@dashboard,~@profile,~@public,~@snippets') - end - - desc "GitLab | Spinach | Run other spinach features" - task :other do - run_spinach_tests('@admin,@dashboard,@profile,@public,@snippets') - end - - desc "GitLab | Spinach | Run other spinach features" - task :builds do - run_spinach_tests('@builds') - end -end - -desc "GitLab | Run spinach" -task :spinach do - run_spinach_tests(nil) -end - -def run_system_command(cmd) - system({ 'RAILS_ENV' => 'test', 'force' => 'yes' }, *cmd) -end - -def run_spinach_command(args) - run_system_command(%w(spinach -r rerun) + args) -end - -def run_spinach_tests(tags) - success = run_spinach_command(%W(--tags #{tags})) - 3.times do |_| - break if success - break unless File.exist?('tmp/spinach-rerun.txt') - - tests = File.foreach('tmp/spinach-rerun.txt').map(&:chomp) - puts '' - puts "Spinach tests for #{tags}: Retrying tests... #{tests}".color(:red) - puts '' - sleep(3) - success = run_spinach_command(tests) - end - - raise("spinach tests for #{tags} failed!") unless success -end diff --git a/package.json b/package.json index 6d5932dcec6..59b4988b264 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { - "@gitlab-org/gitlab-svgs": "^1.20.0", + "@gitlab-org/gitlab-svgs": "^1.22.0", "autosize": "^4.0.0", "axios": "^0.17.1", "babel-core": "^6.26.3", diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb index f4c99ea4064..58bb91a0c80 100644 --- a/spec/controllers/concerns/send_file_upload_spec.rb +++ b/spec/controllers/concerns/send_file_upload_spec.rb @@ -51,6 +51,21 @@ describe SendFileUpload do end end + context 'with attachment' do + subject { controller.send_upload(uploader, attachment: 'test.js') } + + it 'sends a file with content-type of text/plain' do + expected_params = { + content_type: 'text/plain', + filename: 'test.js', + disposition: 'attachment' + } + expect(controller).to receive(:send_file).with(uploader.path, expected_params) + + subject + end + end + context 'when remote file is used' do before do stub_uploads_object_storage(uploader: uploader_class) diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb index 98566f907f9..0430762c1ff 100644 --- a/spec/factories/clusters/clusters.rb +++ b/spec/factories/clusters/clusters.rb @@ -4,8 +4,8 @@ FactoryBot.define do name 'test-cluster' trait :project do - after(:create) do |cluster, evaluator| - cluster.projects << create(:project) + before(:create) do |cluster, evaluator| + cluster.projects << create(:project, :repository) end end diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index fe334b531f0..a8a627d8806 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -182,6 +182,7 @@ feature 'Gcp Cluster', :js do it 'user sees a login page' do expect(page).to have_css('.signin-with-google') + expect(page).to have_link('Google account') end end diff --git a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb b/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb index ab9420fc38f..2c67cec6b67 100644 --- a/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_deletes_wiki_page_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'User deletes wiki page' do +feature 'User deletes wiki page', :js do let(:user) { create(:user) } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) } let(:wiki_page) { create(:wiki_page, wiki: project.wiki) } @@ -13,6 +13,7 @@ feature 'User deletes wiki page' do it 'deletes a page' do click_on('Edit') click_on('Delete') + find('.js-modal-primary-action').click expect(page).to have_content('Page was successfully deleted') end diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js index effacbcff4e..a34a1add4e0 100644 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -1,14 +1,30 @@ +import Vue from 'vue'; + +const mountComponent = (Component, props = {}, el = null) => new Component({ + propsData: props, +}).$mount(el); + export const createComponentWithStore = (Component, store, propsData = {}) => new Component({ store, propsData, }); +export const createComponentWithMixin = (mixins = [], state = {}, props = {}, template = '<div></div>') => { + const Component = Vue.extend({ + template, + mixins, + data() { + return props; + }, + }); + + return mountComponent(Component, props); +}; + export const mountComponentWithStore = (Component, { el, props, store }) => new Component({ store, propsData: props || { }, }).$mount(el); -export default (Component, props = {}, el = null) => new Component({ - propsData: props, -}).$mount(el); +export default mountComponent; diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_maintainer_edit_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_maintainer_edit_spec.js deleted file mode 100644 index cee22d5342a..00000000000 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_maintainer_edit_spec.js +++ /dev/null @@ -1,40 +0,0 @@ -import Vue from 'vue'; -import maintainerEditComponent from '~/vue_merge_request_widget/components/mr_widget_maintainer_edit.vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; - -describe('RWidgetMaintainerEdit', () => { - let Component; - let vm; - - beforeEach(() => { - Component = Vue.extend(maintainerEditComponent); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('when a maintainer is allowed to edit', () => { - beforeEach(() => { - vm = mountComponent(Component, { - maintainerEditAllowed: true, - }); - }); - - it('it renders the message', () => { - expect(vm.$el.textContent.trim()).toEqual('Allows edits from maintainers'); - }); - }); - - describe('when a maintainer is not allowed to edit', () => { - beforeEach(() => { - vm = mountComponent(Component, { - maintainerEditAllowed: false, - }); - }); - - it('hides the message', () => { - expect(vm.$el.textContent.trim()).toEqual(''); - }); - }); -}); diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index e55c7649d40..30918428da2 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options'; +import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue'; import eventHub from '~/vue_merge_request_widget/event_hub'; import notify from '~/lib/utils/notify'; import { stateKey } from '~/vue_merge_request_widget/stores/state_maps'; diff --git a/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb b/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb index 726a3c1c83a..43b68e69131 100644 --- a/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb +++ b/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb @@ -17,12 +17,8 @@ describe Gitlab::Auth::BlockedUserTracker do end context 'failed login due to blocked user' do - let(:env) do - { - 'warden.options' => { message: User::BLOCKED_MESSAGE }, - described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'user' => { 'login' => user.username } } - } - end + let(:base_env) { { 'warden.options' => { message: User::BLOCKED_MESSAGE } } } + let(:env) { base_env.merge(request_env) } subject { described_class.log_if_user_blocked(env) } @@ -30,23 +26,37 @@ describe Gitlab::Auth::BlockedUserTracker do expect_any_instance_of(SystemHooksService).to receive(:execute_hooks_for).with(user, :failed_login) end - it 'logs a blocked user' do - user.block! + context 'via GitLab login' do + let(:request_env) { { described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'user' => { 'login' => user.username } } } } - expect(subject).to be_truthy - end + it 'logs a blocked user' do + user.block! + + expect(subject).to be_truthy + end - it 'logs a blocked user by e-mail' do - user.block! - env[described_class::ACTIVE_RECORD_REQUEST_PARAMS]['user']['login'] = user.email + it 'logs a blocked user by e-mail' do + user.block! + env[described_class::ACTIVE_RECORD_REQUEST_PARAMS]['user']['login'] = user.email - expect(subject).to be_truthy + expect(subject).to be_truthy + end end - it 'logs a LDAP blocked user' do - user.ldap_block! + context 'via LDAP login' do + let(:request_env) { { described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'username' => user.username } } } + + it 'logs a blocked user' do + user.block! + + expect(subject).to be_truthy + end + + it 'logs a LDAP blocked user' do + user.ldap_block! - expect(subject).to be_truthy + expect(subject).to be_truthy + end end end end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index a34b7d9905a..e3f705d2299 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -106,6 +106,18 @@ describe Gitlab::ProjectSearchResults do end end + context 'when the matching content contains multiple null bytes' do + let(:search_result) { "master:testdata/foo.txt\x001\x00blah\x001\x00foo" } + + it 'returns a valid FoundBlob' do + expect(subject.filename).to eq('testdata/foo.txt') + expect(subject.basename).to eq('testdata/foo') + expect(subject.ref).to eq('master') + expect(subject.startline).to eq(1) + expect(subject.data).to eq("blah\x001\x00foo") + end + end + context 'when the search result ends with an empty line' do let(:results) { project.repository.search_files_by_content('Role models', 'master') } diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb index bed58d407ef..0ee7fa1e570 100644 --- a/spec/lib/gitlab/untrusted_regexp_spec.rb +++ b/spec/lib/gitlab/untrusted_regexp_spec.rb @@ -39,6 +39,14 @@ describe Gitlab::UntrustedRegexp do expect(result).to be_falsy end + + it 'can handle regular expressions in multiline mode' do + regexp = described_class.new('^\d', multiline: true) + + result = regexp === "Header\n\n1. Content" + + expect(result).to be_truthy + end end describe '#scan' do diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb index 41cf2ef7225..9ca156deaa0 100644 --- a/spec/policies/ci/build_policy_spec.rb +++ b/spec/policies/ci/build_policy_spec.rb @@ -94,6 +94,19 @@ describe Ci::BuildPolicy do end end end + + context 'when maintainer is allowed to push to pipeline branch' do + let(:project) { create(:project, :public) } + let(:owner) { user } + + it 'enables update_build if user is maintainer' do + allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false) + allow_any_instance_of(Project).to receive(:branch_allows_maintainer_push?).and_return(true) + + expect(policy).to be_allowed :update_build + expect(policy).to be_allowed :update_commit_status + end + end end describe 'rules for protected ref' do diff --git a/spec/policies/ci/pipeline_policy_spec.rb b/spec/policies/ci/pipeline_policy_spec.rb index 48a8064c5fc..a5e509cfa0f 100644 --- a/spec/policies/ci/pipeline_policy_spec.rb +++ b/spec/policies/ci/pipeline_policy_spec.rb @@ -62,5 +62,17 @@ describe Ci::PipelinePolicy, :models do end end end + + context 'when maintainer is allowed to push to pipeline branch' do + let(:project) { create(:project, :public) } + let(:owner) { user } + + it 'enables update_pipeline if user is maintainer' do + allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false) + allow_any_instance_of(Project).to receive(:branch_allows_maintainer_push?).and_return(true) + + expect(policy).to be_allowed :update_pipeline + end + end end end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 8b9c4ac0b4b..6609f5f7afd 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -404,7 +404,7 @@ describe ProjectPolicy do ) end let(:maintainer_abilities) do - %w(create_build update_build create_pipeline update_pipeline) + %w(create_build create_pipeline) end subject { described_class.new(user, project) } diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index e88e86c2998..b741308e2c5 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -114,7 +114,9 @@ describe PipelineSerializer do Gitlab::GitalyClient.reset_counts end - shared_examples 'no N+1 queries' do + context 'with the same ref' do + let(:ref) { 'feature' } + it 'verifies number of queries', :request_store do recorded = ActiveRecord::QueryRecorder.new { subject } @@ -123,12 +125,6 @@ describe PipelineSerializer do end end - context 'with the same ref' do - let(:ref) { 'feature' } - - it_behaves_like 'no N+1 queries' - end - context 'with different refs' do def ref @sequence ||= 0 @@ -136,7 +132,16 @@ describe PipelineSerializer do "feature-#{@sequence}" end - it_behaves_like 'no N+1 queries' + it 'verifies number of queries', :request_store do + recorded = ActiveRecord::QueryRecorder.new { subject } + + # For each ref there is a permission check if maintainer can update + # pipeline. With the same ref this check is cached but if refs are + # different then there is an extra query per ref + # https://gitlab.com/gitlab-org/gitlab-ce/issues/46368 + expect(recorded.count).to be_within(1).of(51) + expect(recorded.cached_count).to eq(0) + end end def create_pipeline(status) diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index f1acfc48468..a73bd7a0268 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Ci::RetryPipelineService, '#execute' do + include ProjectForksHelper + let(:user) { create(:user) } let(:project) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: project) } @@ -266,6 +268,33 @@ describe Ci::RetryPipelineService, '#execute' do end end + context 'when maintainer is allowed to push to forked project' do + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + let(:forked_project) { fork_project(project) } + let(:pipeline) { create(:ci_pipeline, project: forked_project, ref: 'fixes') } + + before do + project.add_master(user) + create(:merge_request, + source_project: forked_project, + target_project: project, + source_branch: 'fixes', + allow_maintainer_to_push: true) + create_build('rspec 1', :failed, 1) + end + + it 'allows to retry failed pipeline' do + allow_any_instance_of(Project).to receive(:fetch_branch_allows_maintainer_push?).and_return(true) + allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false) + + service.execute(pipeline) + + expect(build('rspec 1')).to be_pending + expect(pipeline.reload).to be_running + end + end + def statuses pipeline.reload.statuses end diff --git a/spec/services/clusters/create_service_spec.rb b/spec/services/clusters/create_service_spec.rb index 1c2f9c5cf43..1685dc748bd 100644 --- a/spec/services/clusters/create_service_spec.rb +++ b/spec/services/clusters/create_service_spec.rb @@ -8,80 +8,22 @@ describe Clusters::CreateService do subject { described_class.new(project, user, params).execute(access_token) } context 'when provider is gcp' do - shared_context 'valid params' do - let(:params) do - { - name: 'test-cluster', - provider_type: :gcp, - provider_gcp_attributes: { - gcp_project_id: 'gcp-project', - zone: 'us-central1-a', - num_nodes: 1, - machine_type: 'machine_type-a' - } - } - end - end - - shared_context 'invalid params' do - let(:params) do - { - name: 'test-cluster', - provider_type: :gcp, - provider_gcp_attributes: { - gcp_project_id: '!!!!!!!', - zone: 'us-central1-a', - num_nodes: 1, - machine_type: 'machine_type-a' - } - } - end - end - - shared_examples 'create cluster' do - it 'creates a cluster object and performs a worker' do - expect(ClusterProvisionWorker).to receive(:perform_async) - - expect { subject } - .to change { Clusters::Cluster.count }.by(1) - .and change { Clusters::Providers::Gcp.count }.by(1) - - expect(subject.name).to eq('test-cluster') - expect(subject.user).to eq(user) - expect(subject.project).to eq(project) - expect(subject.provider.gcp_project_id).to eq('gcp-project') - expect(subject.provider.zone).to eq('us-central1-a') - expect(subject.provider.num_nodes).to eq(1) - expect(subject.provider.machine_type).to eq('machine_type-a') - expect(subject.provider.access_token).to eq(access_token) - expect(subject.platform).to be_nil - end - end - - shared_examples 'error' do - it 'returns an error' do - expect(ClusterProvisionWorker).not_to receive(:perform_async) - expect { subject }.to change { Clusters::Cluster.count }.by(0) - expect(subject.errors[:"provider_gcp.gcp_project_id"]).to be_present - end - end - context 'when project has no clusters' do context 'when correct params' do - include_context 'valid params' + include_context 'valid cluster create params' - include_examples 'create cluster' + include_examples 'create cluster service success' end context 'when invalid params' do - include_context 'invalid params' + include_context 'invalid cluster create params' - include_examples 'error' + include_examples 'create cluster service error' end end context 'when project has a cluster' do - include_context 'valid params' + include_context 'valid cluster create params' let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) } it 'does not create a cluster' do diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/services/clusters/create_service_shared.rb new file mode 100644 index 00000000000..43a2fd05498 --- /dev/null +++ b/spec/support/services/clusters/create_service_shared.rb @@ -0,0 +1,57 @@ +shared_context 'valid cluster create params' do + let(:params) do + { + name: 'test-cluster', + provider_type: :gcp, + provider_gcp_attributes: { + gcp_project_id: 'gcp-project', + zone: 'us-central1-a', + num_nodes: 1, + machine_type: 'machine_type-a' + } + } + end +end + +shared_context 'invalid cluster create params' do + let(:params) do + { + name: 'test-cluster', + provider_type: :gcp, + provider_gcp_attributes: { + gcp_project_id: '!!!!!!!', + zone: 'us-central1-a', + num_nodes: 1, + machine_type: 'machine_type-a' + } + } + end +end + +shared_examples 'create cluster service success' do + it 'creates a cluster object and performs a worker' do + expect(ClusterProvisionWorker).to receive(:perform_async) + + expect { subject } + .to change { Clusters::Cluster.count }.by(1) + .and change { Clusters::Providers::Gcp.count }.by(1) + + expect(subject.name).to eq('test-cluster') + expect(subject.user).to eq(user) + expect(subject.project).to eq(project) + expect(subject.provider.gcp_project_id).to eq('gcp-project') + expect(subject.provider.zone).to eq('us-central1-a') + expect(subject.provider.num_nodes).to eq(1) + expect(subject.provider.machine_type).to eq('machine_type-a') + expect(subject.provider.access_token).to eq(access_token) + expect(subject.platform).to be_nil + end +end + +shared_examples 'create cluster service error' do + it 'returns an error' do + expect(ClusterProvisionWorker).not_to receive(:perform_async) + expect { subject }.to change { Clusters::Cluster.count }.by(0) + expect(subject.errors[:"provider_gcp.gcp_project_id"]).to be_present + end +end diff --git a/vendor/ingress/values.yaml b/vendor/ingress/values.yaml index a6b499953bf..d0c1673cefc 100644 --- a/vendor/ingress/values.yaml +++ b/vendor/ingress/values.yaml @@ -7,3 +7,8 @@ controller: podAnnotations: prometheus.io/scrape: "true" prometheus.io/port: "10254" + +rbac: + create: false + createRole: false + createClusterRole: false diff --git a/yarn.lock b/yarn.lock index b48a16c6ff1..f34bc81067d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54,9 +54,9 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@gitlab-org/gitlab-svgs@^1.20.0": - version "1.20.0" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.20.0.tgz#4c3fa3a91e0693114654b0066fb1ef04c0602047" +"@gitlab-org/gitlab-svgs@^1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.22.0.tgz#9f2daefebcda911cba8341313c8c464c8087fe44" "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" |