├── .github ├── FUNDING.yml ├── renovate.json └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── Appraisals ├── CHANGELOG.md ├── CHANGELOG.old.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── RELEASING.md ├── Rakefile ├── VERSION ├── bin ├── install_geckodriver.sh └── install_webpacker.sh ├── config ├── .gitignore └── cucumber.yml ├── cucumber-rails.gemspec ├── features ├── allow_rescue.feature ├── annotations.feature ├── capybara_javascript_drivers.feature ├── choose_javascript_database_strategy.feature ├── configuration.feature ├── database_cleaner.feature ├── disable_automatic_database_cleaning.feature ├── emulate_javascript.feature ├── install_cucumber_rails.feature ├── no_database.feature ├── raising_errors.feature ├── rerun_profile.feature ├── rest_api.feature ├── step_definitions │ └── cucumber_rails_steps.rb └── support │ ├── cucumber_rails_gem_helper.rb │ ├── cucumber_rails_setup_helper.rb │ ├── env.rb │ └── hooks.rb ├── gemfiles ├── rails_6_1.gemfile ├── rails_7_0.gemfile ├── rails_7_1.gemfile ├── rails_7_2.gemfile └── rails_8_0.gemfile ├── lib ├── cucumber │ ├── rails.rb │ └── rails │ │ ├── action_dispatch.rb │ │ ├── application.rb │ │ ├── capybara.rb │ │ ├── capybara │ │ ├── javascript_emulation.rb │ │ └── select_dates_and_times.rb │ │ ├── database.rb │ │ ├── database │ │ ├── deletion_strategy.rb │ │ ├── null_strategy.rb │ │ ├── shared_connection_strategy.rb │ │ ├── strategy.rb │ │ └── truncation_strategy.rb │ │ ├── hooks.rb │ │ ├── hooks │ │ ├── active_record.rb │ │ ├── allow_rescue.rb │ │ ├── database_cleaner.rb │ │ └── mail.rb │ │ ├── rspec.rb │ │ └── world.rb └── generators │ └── cucumber │ ├── USAGE │ ├── install_generator.rb │ └── templates │ ├── bin │ └── cucumber │ ├── config │ └── cucumber.yml.erb │ ├── support │ ├── edit_warning.txt │ └── env.rb.erb │ └── tasks │ └── cucumber.rake.erb └── spec ├── cucumber └── rails │ └── database_spec.rb ├── generators └── cucumber │ └── install_generator_spec.rb └── spec_helper.rb /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: cucumber 2 | github: cucumber 3 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>cucumber/renovate-config" 5 | ] 6 | } -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release to RubyGems 2 | 3 | on: 4 | push: 5 | branches: 6 | - release/* 7 | 8 | jobs: 9 | pre-release-check: 10 | uses: cucumber/.github/.github/workflows/prerelease-checks.yml@main 11 | 12 | test: 13 | uses: ./.github/workflows/test.yml 14 | 15 | publish-rubygem: 16 | name: Publish Ruby Gem 17 | needs: [pre-release-check, test] 18 | runs-on: ubuntu-latest 19 | environment: Release 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: cucumber/action-publish-rubygem@v1.0.0 23 | with: 24 | rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }} 25 | 26 | create-github-release: 27 | name: Create GitHub Release and Git tag 28 | needs: publish-rubygem 29 | runs-on: ubuntu-latest 30 | environment: Release 31 | permissions: 32 | contents: write 33 | steps: 34 | - uses: actions/checkout@v4 35 | - uses: cucumber/action-create-github-release@v1.1.1 36 | with: 37 | github-token: ${{ secrets.GITHUB_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: build 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - renovate/** 8 | pull_request: 9 | branches: 10 | - main 11 | workflow_call: 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | # Latest ruby will only be tested on 20 | # - all rails versions in current major & latest minor rails version of the previous major 21 | # 22 | # Any unsupported ruby will only be tested on 23 | # - rails versions if their patch release is within 6 months of the Ruby EOL date 24 | # - No version of the current rails major 25 | # 26 | # 3.1 -> EOL Mar '25 -> Only test Rails versions initially released before Sep '25 27 | # 3.2 -> EOL Mar '26 -> Only test Rails versions initially released before Sep '26 28 | include: 29 | - { ruby: '3.4', gemfile: 'rails_7_2' } 30 | - { ruby: '3.4', gemfile: 'rails_8_0' } 31 | # Supported rubies will test all permissible supported rails versions 32 | ruby: ['3.1', '3.2', '3.3'] 33 | gemfile: ['rails_6_1', 'rails_7_0', 'rails_7_1', 'rails_7_2', 'rails_8_0'] 34 | exclude: 35 | # Ruby 3.1+ has issues with Rails 6.1 https://github.com/rails/rails/issues/46883#issuecomment-1371325906 36 | # It (Rails 6.1.x), has been marked as a won't fix and as such it's likely this will need to just be excluded until out of support window 37 | # Ruby 3.1+ has a conflicting Psych version with Rails 6.x: https://stackoverflow.com/questions/71191685/visit-psych-nodes-alias-unknown-alias-default-psychbadalias 38 | # Rails 8.x has a minimum ruby of 3.2 39 | - { ruby: '3.1', gemfile: 'rails_6_1' } 40 | - { ruby: '3.1', gemfile: 'rails_8_0' } 41 | - { ruby: '3.2', gemfile: 'rails_6_1' } 42 | - { ruby: '3.3', gemfile: 'rails_6_1' } 43 | env: 44 | BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile 45 | steps: 46 | - uses: actions/checkout@v4 47 | - uses: ruby/setup-ruby@v1 48 | with: 49 | ruby-version: ${{ matrix.ruby }} 50 | bundler-cache: true 51 | - run: bundle exec rake spec 52 | - run: bundle exec rubocop 53 | - run: bundle exec rake cucumber 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg 2 | doc 3 | tmp 4 | coverage 5 | .bundle 6 | .rvmrc 7 | .yardoc/ 8 | .cucumber.rerun 9 | gemfiles/*.lock 10 | Gemfile.lock 11 | spec/examples.txt 12 | .idea 13 | .ruby-version 14 | .ruby-gemset 15 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format progress 2 | --color 3 | --order random 4 | --require spec_helper 5 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-packaging 3 | - rubocop-performance 4 | - rubocop-rails 5 | - rubocop-rake 6 | - rubocop-rspec 7 | 8 | AllCops: 9 | TargetRubyVersion: 3.1 10 | NewCops: enable 11 | Exclude: 12 | # These are auto-generated from a load of features that use aruba 13 | - 'tmp/**/*' 14 | # Generated by appraisal 15 | - 'gemfiles/*.gemfile' 16 | - 'vendor/**/*' 17 | 18 | # Disabled on our repo's to enable polyglot-release 19 | Gemspec/RequireMFA: 20 | Enabled: false 21 | 22 | Layout/LineLength: 23 | Max: 200 24 | AllowedPatterns: 25 | - '^Given' 26 | - '^When' 27 | - '^Then' 28 | 29 | # This cop isn't relevant for our codebase 30 | Rails/RakeEnvironment: 31 | Enabled: false 32 | 33 | # This allows us to read the chmod action in a more reproducible way 34 | Style/NumericLiteralPrefix: 35 | EnforcedOctalStyle: zero_only 36 | 37 | Style/Documentation: 38 | Enabled: false 39 | 40 | Style/RegexpLiteral: 41 | EnforcedStyle: slashes 42 | AllowInnerSlashes: true 43 | 44 | RSpec/MessageSpies: 45 | EnforcedStyle: receive 46 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | appraise 'rails_6_1' do 4 | gem 'activerecord' 5 | gem 'capybara', '< 3.38' 6 | gem 'concurrent-ruby', '< 1.3.5' 7 | gem 'factory_bot', '< 6.4' 8 | gem 'psych', '< 4' 9 | gem 'railties', '~> 6.1.7' 10 | gem 'sqlite3', '< 2' 11 | end 12 | 13 | appraise 'rails_7_0' do 14 | gem 'activerecord' 15 | gem 'concurrent-ruby', '< 1.3.5' 16 | gem 'cucumber', '< 10' 17 | gem 'factory_bot', '< 6.6' 18 | gem 'railties', '~> 7.0.8' 19 | gem 'sqlite3', '< 2' 20 | end 21 | 22 | appraise 'rails_7_1' do 23 | gem 'activerecord' 24 | gem 'railties', '~> 7.1.5' 25 | gem 'sqlite3', '~> 2.0' 26 | end 27 | 28 | appraise 'rails_7_2' do 29 | gem 'activerecord' 30 | gem 'railties', '~> 7.2.2' 31 | gem 'sqlite3', '~> 2.2' 32 | end 33 | 34 | appraise 'rails_8_0' do 35 | gem 'activerecord' 36 | gem 'railties', '~> 8.0.0' 37 | gem 'sqlite3', '~> 2.2' 38 | end 39 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. For older versions see the [changelog archive](./CHANGELOG.old.md) 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | This file is intended to be modified using the [`changelog`](https://github.com/cucumber/changelog) command-line tool. 9 | 10 | ## [Unreleased] 11 | 12 | ## [3.1.1] - 2025-01-30 13 | ### Changed 14 | - Updated some dev dependencies and improved some dev rake tasks to make testing leaner 15 | 16 | ## [3.1.0] - 2024-11-25 17 | ### Added 18 | - Add support for Rails 8.0 (No code changes required) [#590](https://github.com/cucumber/cucumber-rails/pull/590) 19 | 20 | ## [3.0.1] - 2024-11-05 21 | ### Added 22 | - Add support for Rails 7.2 / Ruby 3.3 (No code changes required) [#586](https://github.com/cucumber/cucumber-rails/pull/586) [#588](https://github.com/cucumber/cucumber-rails/pull/588) 23 | 24 | ### Fixed 25 | - Internal testing code has been refactored to handle older ruby/rails installs [#583](https://github.com/cucumber/cucumber-rails/pull/583) 26 | 27 | ## [3.0.0] - 2023-11-01 28 | ### Added 29 | - Add support for Rails 7.1 [#575](https://github.com/cucumber/cucumber-rails/pull/575) 30 | - Added new rubocop sub-gems (rails / rake) and updated repo to be rubocop 2.6 conformant [#581](https://github.com/cucumber/cucumber-rails/pull/581) 31 | 32 | ### Fixed 33 | - Some of the rails 5.2 tests were installing lots of old conflicting gems ([luke-hill](https://github.com/luke-hill)) 34 | - Generator updates (Updated the install locations for some scripts from `script/` to `bin/` and remove some legacy items) 35 | ([luke-hill](https://github.com/luke-hill)) 36 | - Update minimum versions of several gems: capybara must be `v3.11+` and cucumber must be `v5+` (cucumber v9 is also permissible) 37 | ([luke-hill](https://github.com/luke-hill)) 38 | - Fixed an issue where the World instantiation didn't re-run the inherited classes initializer ([luke-hill](https://github.com/luke-hill)) 39 | 40 | ## [3.0.0.rc.1] - 2023-09-15 41 | ### Removed 42 | - Removed runtime dependencies: `mime-types`, `rexml`, and `webrick` [#559](https://github.com/cucumber/cucumber-rails/pull/559) 43 | - Removed support for Ruby 2.5 [#558](https://github.com/cucumber/cucumber-rails/pull/558) 44 | - Removed support for Rails 5.0 and 5.1 (5.2 is still supported and 6.0+ is now expected) [#565](https://github.com/cucumber/cucumber-rails/pull/565) 45 | 46 | ## [2.6.1] - 2022-10-12 47 | ### Changed 48 | - Automate release process [#554](https://github.com/cucumber/cucumber-rails/pull/554) 49 | 50 | ## [2.6.0] - 2022-10-07 51 | ### Changed 52 | - Gem update: allowed cucumber 8 ([#538](https://github.com/cucumber/cucumber-rails/pull/538) / [#541](https://github.com/cucumber/cucumber-rails/pull/541) [mattwynne](https://github.com/mattwynne)) 53 | - New release process. Older releases are in [this changelog](./CHANGELOG.old.md) 54 | 55 | ### Fixed 56 | - Some bugs with dev dependencies have now been fixed externally, so these have been unrestricted 57 | ([#552](https://github.com/cucumber/cucumber-rails/pull/552) [BrianHawley](https://github.com/BrianHawley)) 58 | - Fixed up some rubocop offenses from updated `rubocop-performance` 59 | ([#550](https://github.com/cucumber/cucumber-rails/pull/550) [olleolleolle](https://github.com/olleolleolle)) 60 | 61 | [Unreleased]: https://github.com/cucumber/cucumber-rails/compare/v3.1.1...HEAD 62 | [3.1.1]: https://github.com/cucumber/cucumber-rails/compare/v3.1.0...v3.1.1 63 | [3.1.0]: https://github.com/cucumber/cucumber-rails/compare/v3.0.1...v3.1.0 64 | [3.0.1]: https://github.com/cucumber/cucumber-rails/compare/v3.0.0...v3.0.1 65 | [3.0.0]: https://github.com/cucumber/cucumber-rails/compare/v3.0.0.rc.1...v3.0.0 66 | [3.0.0.rc.1]: https://github.com/cucumber/cucumber-rails/compare/v2.6.1...v3.0.0.rc.1 67 | [2.6.1]: https://github.com/cucumber/cucumber-rails/compare/v2.6.0...v2.6.1 68 | [2.6.0]: https://github.com/cucumber/cucumber-rails/compare/v2.5.1...v2.6.0 69 | -------------------------------------------------------------------------------- /CHANGELOG.old.md: -------------------------------------------------------------------------------- 1 | Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) 2 | on how to contribute to Cucumber. 3 | 4 | ## [v2.5.1](https://github.com/cucumber/cucumber-rails/compare/v2.5.0...v2.5.1) (2022-04-01) 5 | 6 | ### Fixed 7 | 8 | * Date input support in Rails 7 ([#535](https://github.com/cucumber/cucumber-rails/pull/535) [mgrunberg]) 9 | 10 | ## [v2.5.0](https://github.com/cucumber/cucumber-rails/compare/v2.4.0...v2.5.0) (2022-03-07) 11 | 12 | ### New Features 13 | 14 | * Added `:none` option for `javascript_strategy` which indicates no special handling of `@javascript` tagged tests ([#522](https://github.com/cucumber/cucumber-rails/pull/522) [akostadinov]) 15 | * Added support for Rails 7 ([#526](https://github.com/cucumber/cucumber-rails/pull/526/) [mgrunberg]) 16 | * Added Ruby 3.1 support ([#529](https://github.com/cucumber/cucumber-rails/pull/529) [mgrunberg]) 17 | 18 | ### Changed 19 | 20 | * Dropped Ruby 2.4 support ([#529](https://github.com/cucumber/cucumber-rails/pull/529) [mgrunberg]) 21 | 22 | ## [v2.4.0](https://github.com/cucumber/cucumber-rails/compare/v2.3.0...v2.4.0) (2021-07-21) 23 | 24 | ### New Features 25 | 26 | * Added new docker / Makefile script to permit releasing from repo ([#502](https://github.com/cucumber/cucumber-rails/pull/502) [luke-hill]) 27 | * Gem update: allowed cucumber 6/7 ([#515](https://github.com/cucumber/cucumber-rails/pull/515) / 28 | [#519](https://github.com/cucumber/cucumber-rails/pull/519)) 29 | * Add support for Ruby 3 ([#517](https://github.com/cucumber/cucumber-rails/pull/517/)) 30 | 31 | ### Changed 32 | 33 | * Updated rubocop to use new major versions ([#504](https://github.com/cucumber/cucumber-rails/pull/504) / 34 | [#519](https://github.com/cucumber/cucumber-rails/pull/519) [luke-hill]) 35 | 36 | ### Removed 37 | 38 | * Removed traces of legacy spork setup / help (No longer used) 39 | ([#518](https://github.com/cucumber/cucumber-rails/pull/518) [luke-hill]) 40 | 41 | ## [v2.3.0](https://github.com/cucumber/cucumber-rails/compare/v2.2.0...v2.3.0) (2021-03-30) 42 | 43 | ### Changed 44 | 45 | * Changed dependency from rails to railties to avoid pulling in optional Rails gems like actioncable 46 | ([#486](https://github.com/cucumber/cucumber-rails/pull/486) [langalex]) 47 | 48 | * Lightened gem binary by removing un-needed files ([#496](https://github.com/cucumber/cucumber-rails/pull/496) [orien]) 49 | 50 | ### Fixed 51 | 52 | * Fixed up a couple of legacy capybara specs to use a better format 53 | ([#498](https://github.com/cucumber/cucumber-rails/pull/498) [luke-hill]) 54 | 55 | ## [v2.2.0](https://github.com/cucumber/cucumber-rails/compare/v2.1.0...v2.2.0) (2020-08-10) 56 | 57 | ### New Features 58 | 59 | * Added more documentation and tests around Javascript tagged scenarios 60 | ([#480](https://github.com/cucumber/cucumber-rails/pull/480) [luke-hill]) 61 | 62 | * Gem Updates 63 | * Allowed cucumber5, updated other gem dependencies 64 | * Updated travis runs and gem builds tested against 65 | ([#482](https://github.com/cucumber/cucumber-rails/pull/482) [luke-hill]) 66 | 67 | ### Changed 68 | 69 | * Dropped Rails 4.2 support ([#392](https://github.com/cucumber/cucumber-rails/pull/392) [deivid-rodriguez]) 70 | 71 | ### Fixed 72 | 73 | * Database cleaning no longer silently fails when using database_cleaner v2 adapter gems 74 | ([#467](https://github.com/cucumber/cucumber-rails/pull/467) [botandrose]) 75 | 76 | * Restored compatibility with `database_cleaner` versions earlier than 1.8.0.beta 77 | ([#473](https://github.com/cucumber/cucumber-rails/pull/473) [cgriego]) 78 | 79 | * Restored previous `database_cleaner` behavior for apps that do not use/load ActiveRecord 80 | ([#474](https://github.com/cucumber/cucumber-rails/pull/474) [cgriego]* Restored previous compatibility around standard Rails Modules not being loaded 81 | ([#484](https://github.com/cucumber/cucumber-rails/pull/484) [Draiken]) 82 | 83 | ## [v2.1.0](https://github.com/cucumber/cucumber-rails/compare/v2.0.0...v2.1.0) (2020-06-15) 84 | 85 | ### New Features 86 | 87 | * Added metadata to allow gem to be searched / indexed by rubygems 88 | ([#447](https://github.com/cucumber/cucumber-rails/pull/447) [orien]) 89 | 90 | * Allow Cucumber 4 to be used as an option for cucumber-rails 91 | * NB: Cucumber4 changes quite a lot of logic with auto-loaders. This means that any previous 92 | logic that relied implicitly on load-order "may" be affected 93 | ([#453](https://github.com/cucumber/cucumber-rails/pull/453) [luke-hill] / [deivid-rodriguez]) 94 | 95 | ### Changed 96 | 97 | * Dropped Ruby 2.3 support (target 2.4 - 2.7) ([#466](https://github.com/cucumber/cucumber-rails/pull/466) [mvz]) 98 | 99 | * Refactored suite to remove a lot of old Rails3/4.0 "ism's" 100 | * Also migrated a lot of old cucumber styles to cucumber4 101 | ([#441](https://github.com/cucumber/cucumber-rails/pull/441) [luke-hill]) 102 | 103 | * Refactored alias logic for overriding Rails methods to use `Module#prepend` 104 | ([#457](https://github.com/cucumber/cucumber-rails/pull/457) [wagenet]) 105 | 106 | * `database_cleaner` (when used), at a version `>= 1.8` will now silence all warnings 107 | * The suite internally will only test a version `>= 1.8`, so other versions will work but are un-maintained 108 | ([#463](https://github.com/cucumber/cucumber-rails/pull/463) [deivid-rodriguez]) 109 | 110 | ### Fixed 111 | 112 | * Various rubocop / rspec styling fixes 113 | ([#445](https://github.com/cucumber/cucumber-rails/pull/445) / 114 | [#449](https://github.com/cucumber/cucumber-rails/pull/449) / 115 | [#450](https://github.com/cucumber/cucumber-rails/pull/450) / 116 | [#451](https://github.com/cucumber/cucumber-rails/pull/451) / 117 | [#452](https://github.com/cucumber/cucumber-rails/pull/452) / 118 | [#465](https://github.com/cucumber/cucumber-rails/pull/465) 119 | [mvz] / [luke-hill]) 120 | 121 | * Various CI fixes 122 | ([#444](https://github.com/cucumber/cucumber-rails/pull/444) / 123 | [#460](https://github.com/cucumber/cucumber-rails/pull/460) / 124 | [#464](https://github.com/cucumber/cucumber-rails/pull/464) 125 | [mvz] / [damonjmurray] / [deivid-rodriguez]) 126 | 127 | * Avoid printing a warning about `config.cache_classes` being set to `false` when 128 | Spring is used ([#462](https://github.com/cucumber/cucumber-rails/pull/462) [janko]) 129 | 130 | ## [v2.0.0](https://github.com/cucumber/cucumber-rails/compare/v1.8.0...v2.0.0) (2019-09-13) 131 | 132 | ### New Features 133 | 134 | * New functionality allows users to Configure whether `Rack::Test` methods get auto-mixed in 135 | * Setting `ENV['CR_REMOVE_RACK_TEST_HELPERS] = "true"` will remove these methods from the `World` 136 | ([#440](https://github.com/cucumber/cucumber-rails/pull/440) [luke-hill]) 137 | 138 | ### Changed 139 | 140 | * **Breaking change:** `web_steps` have been completely removed from cucumber-rails 141 | ([#437](https://github.com/cucumber/cucumber-rails/pull/437) [luke-hill]) 142 | 143 | * Altered some branching logic in tests designed to cater for old ruby/rails versions 144 | ([#433](https://github.com/cucumber/cucumber-rails/pull/433) [luke-hill]) 145 | 146 | ## [v1.8.0](https://github.com/cucumber/cucumber-rails/compare/v1.7.0..v1.8.0) (2019-08-06) 147 | 148 | ### Changed 149 | 150 | * Dropped Ruby 2.2 support (target 2.3 and up) ([#424](https://github.com/cucumber/cucumber-rails/pull/424) [mvz]) 151 | 152 | * Begin to update the core runtime / development dependencies to something a little more recent 153 | ([#413](https://github.com/cucumber/cucumber-rails/pull/413) / 154 | [#431](https://github.com/cucumber/cucumber-rails/pull/431) / 155 | [#432](https://github.com/cucumber/cucumber-rails/pull/432) 156 | [mvz] / 157 | [luke-hill]) 158 | 159 | ### Fixed 160 | 161 | * Various rubocop / styling issues that had built up over time 162 | ([#414](https://github.com/cucumber/cucumber-rails/pull/414) / 163 | [#416](https://github.com/cucumber/cucumber-rails/pull/416) / 164 | [#419](https://github.com/cucumber/cucumber-rails/pull/419) / 165 | [#420](https://github.com/cucumber/cucumber-rails/pull/420) / 166 | [#421](https://github.com/cucumber/cucumber-rails/pull/421) / 167 | [#421](https://github.com/cucumber/cucumber-rails/pull/434) 168 | [luke-hill] / 169 | [mvz]) 170 | 171 | * Generic Travis Healthcheck (Fixed up polluted logs and use newer Ubuntu OS) 172 | ([#415](https://github.com/cucumber/cucumber-rails/pull/415) / 173 | [#417](https://github.com/cucumber/cucumber-rails/pull/417) 174 | [luke-hill]) 175 | 176 | * Rails6 not building correctly 177 | ([#418](https://github.com/cucumber/cucumber-rails/pull/418) 178 | [luke-hill]) 179 | 180 | * Provisional Ruby 2.7 builds not working 181 | ([#427](https://github.com/cucumber/cucumber-rails/pull/427) 182 | [amatsuda] / 183 | [luke-hill]) 184 | 185 | ## [v1.7.0](https://github.com/cucumber/cucumber-rails/compare/v1.6.0...v1.7.0) (2019-04-17) 186 | 187 | ### Changed 188 | 189 | * Drop rails `4.0` / `4.1` support ([#392](https://github.com/cucumber/cucumber-rails/pull/392) [deivid-rodriguez]) 190 | * Allow Ruby `2.6` users to `ERB.new` ([#399](https://github.com/cucumber/cucumber-rails/pull/399) [koic]) 191 | * Allow Rails to be used in the `6.x` series (Currently as of release only beta1 is supported) ([#405](https://github.com/cucumber/cucumber-rails/pull/405) [kotovalexarian]) 192 | * Update CI to test most relevant Rubies ([#411](https://github.com/cucumber/cucumber-rails/pull/411) [olleolleolle]) 193 | 194 | ### Fixed 195 | 196 | * Appraisals fixes (Up-to-date `geckodriver`, standardised the various `.gemfile`s) 197 | ([#389](https://github.com/cucumber/cucumber-rails/pull/389) / 198 | [#394](https://github.com/cucumber/cucumber-rails/pull/394) / 199 | [#395](https://github.com/cucumber/cucumber-rails/pull/395) / 200 | [#408](https://github.com/cucumber/cucumber-rails/pull/408) 201 | [xtrasimplicity] / 202 | [deivid-rodriguez] / 203 | [mvz]) 204 | 205 | * Various build issues that had built up over the previous year 206 | ([#403](https://github.com/cucumber/cucumber-rails/pull/403) / 207 | [#404](https://github.com/cucumber/cucumber-rails/pull/404) / 208 | [#410](https://github.com/cucumber/cucumber-rails/pull/410) 209 | [koic] / 210 | [mvz]) 211 | 212 | ## [v1.6.0](https://github.com/cucumber/cucumber-rails/compare/v1.5.0...v1.6.0) (2018-04-23) 213 | 214 | ### Changed 215 | 216 | * Renamed History.md to CHANGELOG.md, added contributing note, and this line in accordance with [cucumber/cucumber #251](https://github.com/cucumber/cucumber/issues/251) ([#345](https://github.com/cucumber/cucumber-rails/pull/345) [jaysonesmith](https://github.com/jaysonesmith)) 217 | * Update .travis.yml with ruby versions ([#341](https://github.com/cucumber/cucumber-rails/pull/341) Jun Aruga) 218 | * Removed support for Ruby <= 2.1, to keep in line with [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CHANGELOG.md#302-2017-11-11) ([#360](https://github.com/cucumber/cucumber-rails/pull/360) [xtrasimplicity](https://github.com/xtrasimplicity)). 219 | * Updated syntax to support both new and deprecated forms of tag negation. ([#348](https://github.com/cucumber/cucumber-rails/pull/348) [mirelon](https://github.com/mirelon), [#359](https://github.com/cucumber/cucumber-rails/pull/359) [xtrasimplicity](https://github.com/xtrasimplicity)) 220 | * Dependencies: Allowed `Ammeter` versions greater than 1.1.3. ([#368](https://github.com/cucumber/cucumber-rails/pull/368) [mvz](https://github.com/mvz)) 221 | * Switched to Ruby 1.9 hash syntax. ([#371](https://github.com/cucumber/cucumber-rails/pull/371) [mvz](https://github.com/mvz)) 222 | * Added support to handle rerun files with multiple lines. ([#373]((https://github.com/cucumber/cucumber-rails/pull/345)) [mvz](https://github.com/mvz)) 223 | * Added support for Rails 5.2 and Capybara 3. ([#378](https://github.com/cucumber/cucumber-rails/pull/378/) [gobijan](https://github.com/gobijan), [radar](https://github.com/radar), [xtrasimplicity](https://github.com/xtrasimplicity)) 224 | 225 | ### Fixed 226 | 227 | * Fix typo ([#343](https://github.com/cucumber/cucumber-rails/pull/343) Olle Jonsson) 228 | * History.md: Fixed markdown formatting ([#344](https://github.com/cucumber/cucumber-rails/pull/344) [Kosmas](https://github.com/Kosmas)) 229 | * Fixed tag deprecation warnings. ([#373](https://github.com/cucumber/cucumber-rails/pull/373) [mvz](https://github.com/mvz)) 230 | 231 | ## [v1.5.0](https://github.com/cucumber/cucumber-rails/compare/1.4.5...1.5.0) (2017-05-12) 232 | 233 | * Drop rails 3 support ([#334](https://github.com/cucumber/cucumber-rails/pull/334) Matijs van Zuijlen) 234 | * Add rails 5.1 support ([#337](https://github.com/cucumber/cucumber-rails/pull/337) Matijs van Zuijlen - Rafael Reggiani Manzo) 235 | 236 | ## [v1.4.5](https://github.com/cucumber/cucumber-rails/compare/1.4.4...1.4.5) (2016-09-27) 237 | 238 | * Add support for Cucumber 3+ (Steve Tooke) 239 | 240 | ## [v1.4.4](https://github.com/cucumber/cucumber-rails/compare/1.4.3...1.4.4) (2016-08-05) 241 | 242 | * Ensure support for Rails 5 243 | 244 | ## [v1.4.3](https://github.com/cucumber/cucumber-rails/compare/1.4.2...1.4.3) (2016-01-21) 245 | 246 | * Added gem version badge (Kosmas Chatzimichalis) 247 | * Fix a failing test due to rails date selectors only showing 5 years into the past by default ([#293](https://github.com/cucumber/cucumber-rails/pull/293) Thomas Walpole) 248 | * Allow cucumber 2 ([#293](https://github.com/cucumber/cucumber-rails/pull/293) Thomas Walpole) 249 | * Add rails 4.2 to test matrix ([#293](https://github.com/cucumber/cucumber-rails/pull/293) Thomas Walpole) 250 | * Depend on railties instead of rails ([#294](https://github.com/cucumber/cucumber-rails/pull/294) Alexander Lang) 251 | * Fix failing Travis CI tests ([#305](https://github.com/cucumber/cucumber-rails/pull/305) Matijs van Zuijlen) 252 | * Allow any cucumber < 3 ([#306](https://github.com/cucumber/cucumber-rails/pull/306) Matijs van Zuijlen) 253 | * Add annotations configuration in generator ([#292](https://github.com/cucumber/cucumber-rails/pull/292) Bob Showalter) 254 | * Expand support to include mime-types 3 ([#304](https://github.com/cucumber/cucumber-rails/pull/304) Austin Ziegler) 255 | * Replace badges with SVG versions ([#307](https://github.com/cucumber/cucumber-rails/pull/307) Kevin Goslar) 256 | * Add new line to end of generated database.yml ([#302](https://github.com/cucumber/cucumber-rails/pull/302) Kevin Carmody) 257 | 258 | ## [v1.4.2](https://github.com/cucumber/cucumber-rails/compare/1.4.1...v1.4.2) (2014-10-09) 259 | 260 | * Updated appraisal dependencies to rspec-rails 3.1.0, gemspec dependency to rspec <= 3.1, and removed turn from rails_4_1 appraisal (Kosmas Chatzimichalis) 261 | * Update mime-types dependency to >= 1.16, < 3 ([#289](https://github.com/cucumber/cucumber-rails/pull/289) Erik Michaels-Ober) 262 | * Upgrade to RSpec 3 ([#290](https://github.com/cucumber/cucumber-rails/pull/290) Tamir Duberstein) 263 | 264 | ## [v1.4.1](https://github.com/cucumber/cucumber-rails/compare/v1.4.0...v1.4.1) (2014-05-10) 265 | 266 | ### New Features 267 | 268 | * Added MIT licence in gemspec ([#261](https://github.com/cucumber/cucumber-rails/issues/261#issuecomment-23260956) Benjamin Fleischer) 269 | * Ensure dependency on DatabaseCleaner is not required ([#276](https://github.com/cucumber/cucumber-rails/pull/276) Matthew O'Riordan) 270 | * Added Rails 4.1 support ([#287](https://github.com/cucumber/cucumber-rails/pull/287) Felix Bünemann) 271 | * Modified appraisal instructions in README.md (Kosmas Chatzimichalis) 272 | * Added Gemnasium support (Kosmas Chatzimichalis) 273 | * Various code enhancements based on PullReview suggestions (Kosmas Chatzimichalis) 274 | 275 | ### Removed Features 276 | 277 | * Mongo step definition ([#263](https://github.com/cucumber/cucumber-rails/issues/263) Aslak Hellesøy) 278 | 279 | ### Bugfixes 280 | 281 | * Prevent MiniTest running `at_exit` when running cucumber ([#253](https://github.com/cucumber/cucumber-rails/issues/253) Steve Tooke) 282 | * `bundle exec rake` runs minitest with cucumber options and raises exception ([#252](https://github.com/cucumber/cucumber-rails/issues/252) Peter Bollenbeck) 283 | * Various bundler related fixes ([#264](https://github.com/cucumber/cucumber-rails/pull/264) Erik Michaels-Ober) 284 | * Additional gemspec cleanup ([#265](https://github.com/cucumber/cucumber-rails/pull/265) Erik Michaels-Ober) 285 | * Added mime-types runtime dependency to fix bundle install issue ([#273](https://github.com/cucumber/cucumber-rails/pull/273) Kosmas Chatzimichalis) 286 | * Removed mongoid gem from Appraisal ([#274](https://github.com/cucumber/cucumber-rails/pull/274) Kosmas Chatzimichalis) 287 | * Amend typo in select_dates_and_times.rb comments ([#268](https://github.com/cucumber/cucumber-rails/pull/268) Erik Eide) 288 | * Keep empty step_definitions directories ([#249](https://github.com/cucumber/cucumber-rails/pull/249) Iain D Broadfoot) 289 | * Remove obsolete link for config.cache_classes to false ([#271](https://github.com/cucumber/cucumber-rails/issues/271) Andrew Premdas) 290 | 291 | ## [v1.4.0](https://github.com/cucumber/cucumber-rails/compare/v1.3.1...v1.4.0) (2013-08-23) 292 | 293 | ### New Features 294 | 295 | * New test raising_errors.feature to test raising routing errors that replaced the earlier routing.feature 296 | * Added recommendation in README.md for running install scripts after upgrading (Joost Baaij) 297 | * Describe configuration option 'autorun_database_cleaner' in README ([#255](https://github.com/cucumber/cucumber-rails/pull/255) Martin Eismann) 298 | 299 | ### Changed Features 300 | 301 | * Gemspec in 1.3.1 doesn't allow usage with rails 4 ([#244](https://github.com/cucumber/cucumber-rails/issues/244) Fabian Schwahn) 302 | 303 | ### Removed Features 304 | 305 | * routing.feature code was actually testing the raising of errors so it was renamed to raising_errors.feature and slightly changed to deal with rails 4 changes in public folder 306 | * multiple_databases.feature was removed as it was actually testing DatabaseCleaner behaviour 307 | * pre_bundler.feature was removed as it is no longer relevant 308 | * mongodb feature was removed as is there is no mongodb code in cucumber-rails codebase 309 | 310 | ### Bugfixes 311 | 312 | * Fixed tests so they pass in Rails 4 and updated gemspec ([#247](https://github.com/cucumber/cucumber-rails/pull/247) Dave Brace) 313 | * Allow use with Rails 4 ([#254](https://github.com/cucumber/cucumber-rails/pull/254) Marnen Laibow-Koser) 314 | * Fix for Rails4 ([#256](https://github.com/cucumber/cucumber-rails/pull/256) Jon Rowe) 315 | * Revert "Merge pull request #256 from JonRowe/dumb_fix_for_rails4_test_he... ([#258](https://github.com/cucumber/cucumber-rails/pull/258) Kosmas Chatzimichalis) 316 | 317 | ## [v1.3.1](https://github.com/cucumber/cucumber-rails/compare/v1.3.0...v1.3.1) (2013-03-15) 318 | 319 | ### New Features 320 | 321 | * Multiple-gemfile testing and travis configuration ([#240](https://github.com/cucumber/cucumber-rails/pull/240) Simon Coffey) 322 | 323 | ### Changed Features 324 | 325 | * Fix a typo in a template ([#228](https://github.com/cucumber/cucumber-rails/pull/228) Robin Dupret) 326 | * Depend upon the generic test:prepare task ([#230](https://github.com/cucumber/cucumber-rails/pull/230) Graeme Mathieson) 327 | * Allow users to disable database_cleaner hooks ([#232](https://github.com/cucumber/cucumber-rails/pull/232) Simon Coffey) 328 | * Rails 4.0.0-beta ActionController::Integration depreciation fix ([#234](https://github.com/cucumber/cucumber-rails/pull/234) Daniel Bruns) 329 | 330 | ## [v1.3.0](https://github.com/cucumber/cucumber-rails/compare/v1.2.1...v1.3.0) (2012-02-19) 331 | 332 | ### New Features 333 | 334 | * JavaScript database strategies should take options ([#192](https://github.com/cucumber/cucumber-rails/issues/192), [#195](https://github.com/cucumber/cucumber-rails/pull/195) Thomas Walpole) 335 | 336 | ### Changed features 337 | 338 | * select_date arguments should be reversed ([#190](https://github.com/cucumber/cucumber-rails/issues/190), [#191](https://github.com/cucumber/cucumber-rails/pull/191) Gavin Hughes) 339 | 340 | ## [v1.2.1](https://github.com/cucumber/cucumber-rails/compare/v1.2.0...v1.2.1) (2011-12-04) 341 | 342 | ### New features 343 | 344 | * Allow ability to use deletion as a javascript database strategy ([#185](https://github.com/cucumber/cucumber-rails/pull/185) Thijs de Vries) 345 | * Support custom JS strategies ([#184](https://github.com/cucumber/cucumber-rails/pull/184) Michael Pabst) 346 | 347 | ## [v1.2.0](https://github.com/cucumber/cucumber-rails/compare/v1.1.1...v1.2.0) (2011-11-03) 348 | 349 | ### Removed features 350 | 351 | * The (deprecated) tableish method has been removed. See https://gist.github.com/1299371 for an alternative. (Aslak Hellesøy) 352 | 353 | ### Bugfixes 354 | 355 | * Non-threadsafe database connections shared between threads ([#166](https://github.com/cucumber/cucumber-rails/issues/166) Matt Wynne) 356 | 357 | ## [v1.1.1](https://github.com/cucumber/cucumber-rails/compare/v1.1.0...v1.1.1) (2011-10-03) 358 | 359 | ### Removed features 360 | 361 | The `cucumber:feature` generator has been removed. The reasoning behind this is the same as for 362 | removing `web_steps.rb`, `paths.rb` and `selectors.rb` in v1.1.0. 363 | 364 | ## [v1.1.0](https://github.com/cucumber/cucumber-rails/compare/v1.0.6...v1.1.0) (2011-09-28) 365 | 366 | ### Removed features 367 | 368 | The following files will no longer be generated if you are running `rails generate cucumber:install`: 369 | 370 | * features/step_definitions/web_steps.rb 371 | * features/support/paths.rb 372 | * features/support/selectors.rb 373 | 374 | The reason behind this is that the steps defined in `web_steps.rb` leads people to write scenarios of a 375 | very imperative nature that are hard to read and hard to maintain. Cucumber scenarios should not be a series 376 | of steps that describe what a user clicks. Instead, they should express what a user *does*. Example: 377 | 378 | Given I have signed up as "user@host.com" 379 | 380 | with a Step Definition that perhaps looks like this: 381 | 382 | Given /^I have signed up as "([^"]*)"$/ do |email| 383 | visit(signup_path) 384 | fill_in('Email', with: email) 385 | fill_in('Password', with: 's3cr3t') 386 | fill_in('Password Confirmation', with: 's3cr3t') 387 | click_button('Sign up') 388 | end 389 | 390 | Moving user interface details from the scenarios and down to the step definitions makes scenarios 391 | much easier to read. If you change the user interface you only have to change a step definition or two 392 | instead of a lot of scenarios that explicitly describe how to sign up. 393 | 394 | You can learn more about the reasoning behind this change at the following links: 395 | 396 | * [Cucumber mailing list: Removing web_steps.rb in Cucumber 1.1.0](http://groups.google.com/group/cukes/browse_thread/thread/26f80b93c94f2952) 397 | * [Cucumber-Rails issue #174: Remove web_steps.rb since it encourages people to write poor tests.](https://github.com/cucumber/cucumber-rails/issues/174) 398 | * [Refuctoring your Cukes by Matt Wynne](http://skillsmatter.com/podcast/home/refuctoring-your-cukes) 399 | * [Imperative vs Declarative Scenarios in User Stories by Ben Mabey](http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html) 400 | * [Whose domain is it anyway? by Dan North](http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/) 401 | * [You're Cuking it Wrong by Jonas Nicklas](http://elabs.se/blog/15-you-re-cuking-it-wrong) 402 | 403 | You can learn more about what Capybara has to offer in Capybara's [README](https://github.com/jnicklas/capybara). 404 | 405 | ## [v1.0.6](https://github.com/cucumber/cucumber-rails/compare/v1.0.5...v1.0.6) (2011-09-25) 406 | 407 | ### Bugfixes 408 | 409 | * Fix deprecation warnings ([#169](https://github.com/cucumber/cucumber-rails/issues/169), [#170](https://github.com/cucumber/cucumber-rails/pull/170) Micah Geisel) 410 | * Deprecate #tableish. The Capybara::Node::Finders API has obsoleted the need for it. ([#145](https://github.com/cucumber/cucumber-rails/issues/145) Aslak Hellesøy) 411 | 412 | ## [v1.0.5](https://github.com/cucumber/cucumber-rails/compare/v1.0.4...v1.0.5) (2011-09-14) 413 | 414 | ### Bugfixes 415 | 416 | * No = dependencies in gemspec, since rubies with old YAML (sych) can't read them. (Aslak Hellesøy) 417 | 418 | ## [v1.0.4](https://github.com/cucumber/cucumber-rails/compare/v1.0.3...v1.0.4) (2011-09-12) 419 | 420 | ### Bugfixes 421 | 422 | * Active_record hook prevents features that access multiple database from running correctly ([#152](https://github.com/cucumber/cucumber-rails/issues/152) winnipegtransit) 423 | 424 | ## [v1.0.3](https://github.com/cucumber/cucumber-rails/compare/v1.0.2...v1.0.3) (2011-09-11) 425 | 426 | ### Bugfixes 427 | 428 | * sqlite3-ruby is now sqlite3 ([#158](https://github.com/cucumber/cucumber-rails/pull/158) Trung Le) 429 | * Broken link in the USAGE file of the features generator ([#156](https://github.com/cucumber/cucumber-rails/pull/156) Pablo Alonso García) 430 | * Rails destroy cucumber:feature deletes the steps folder, even though it's not empty. ([#154](https://github.com/cucumber/cucumber-rails/pull/154]), [#111](https://github.com/cucumber/cucumber-rails/issues/111) mblake) 431 | * Adjust select_date, select_time xpaths so they work when scoped in the document ([#151](https://github.com/cucumber/cucumber-rails/pull/151) Thomas Walpole) 432 | * Extend javascript emulation to handle rails CSRF protection ([#164](https://github.com/cucumber/cucumber-rails/pull/164) Jonathon M. Abbott) 433 | * Add steps for finding fields with errors ([#162](https://github.com/cucumber/cucumber-rails/pull/162) Mike Burns) 434 | 435 | ## [v1.0.2](https://github.com/cucumber/cucumber-rails/compare/v1.0.1...v1.0.2) (2011-06-26) 436 | 437 | ### Bugfixes 438 | 439 | * Removed the dependency on rack-test, since it is not used directly. v1.0.1 was incompatible with Rails 3.0.9. (Aslak Hellesøy) 440 | 441 | ## [v1.0.1](https://github.com/cucumber/cucumber-rails/compare/v1.0.0...v1.0.1) (2011-06-25) 442 | 443 | ### New Features 444 | 445 | * Added a `@no-database-cleaner` tag you can add if you don't want to run DatabaseCleaner. Useful for debugging if you want to leave data in the database. Typical use case is to run `rails server --environment test` to look at/try app with data from test. (Aslak Hellesøy) 446 | * History file is now in Markdown format. (Aslak Hellesøy) 447 | 448 | ## [v1.0.0](https://github.com/cucumber/cucumber-rails/compare/v0.5.2...v1.0.0) (2011-06-20) 449 | 450 | ### New Features 451 | 452 | * Upgraded Cucumber dependency to `1.0.0` (Aslak Hellesøy) 453 | 454 | ## [v0.5.2](https://github.com/cucumber/cucumber-rails/compare/v0.5.1...v0.5.2) (2011-06-07) 455 | 456 | ### New Features 457 | 458 | * Upgraded to Capybara 1.0.0.rc1 (Aslak Hellesøy) 459 | * Add stats to generator (#144 Aslak Hellesøy) 460 | 461 | ## [v0.5.1](https://github.com/cucumber/cucumber-rails/compare/v0.5.0...v0.5.1) (2011-05-25) 462 | 463 | ### Bugfixes 464 | 465 | * Mixed DB access feature for @javascript drivers (#142 Alexander Mankuta) 466 | * cucumber:feature and integers. not creating feature correctly (#30 John Ivanoff, Aslak Hellesøy) 467 | * New project can't find capybara's "visit" (#143 Aslak Hellesøy) 468 | * rails generate cucumber:install attempts double run (#140 Aslak Hellesøy) 469 | 470 | ## [v0.5.0](https://github.com/cucumber/cucumber-rails/compare/v0.5.0.beta1...v0.5.0) (2011-05-17) 471 | 472 | ### Bugfixes 473 | 474 | * undefined method `add_assertion' for nil:NilClass (#96, #97, #98 Aslak Hellesøy) 475 | * Capybara name error from env.rb (#125 Aslak Hellesøy) 476 | * Fixed date-localization bug (#138 Michael Opitz) 477 | 478 | ## [v0.5.0.beta1](https://github.com/cucumber/cucumber-rails/compare/v0.4.1...v0.5.0.beta1) (2011-05-09) 479 | 480 | ### Removed features 481 | 482 | * Dropped support for Rails 2 (Aslak Hellesøy) 483 | * Dropped support for Webrat (Aslak Hellesøy) 484 | * Removed database cleaner strategy overrides (#134 Daniel Morrison, Daniel Duvall) 485 | 486 | ### Improvements 487 | 488 | * Upgrade to Capybara 1.0.0.beta1 or newer (#129, #130 Simon Menke, Klaus Hartl, Aslak Hellesøy) 489 | * Generated paths.rb cleanup (#133 Tim Pope) 490 | * Allow css pseudo-classes in scopers (#122 twalpole) 491 | 492 | ## [v0.4.1](https://github.com/cucumber/cucumber-rails/compare/v0.4.0...v0.4.1) (2011-04-05) 493 | 494 | ### Bugfixes 495 | 496 | * Fixed incorrect warning in generated files. (#115 Emanuele Vicentini) 497 | * Fixed incorrect hooks for DatabaseCleaner (#113 Markus Bengts) 498 | * Throw an error if the user forgot to add DatabaseCleaner to the Gemfile, allowing them to decide whether or not to use it. (#36 Aslak Hellesøy, Ryan Bigg) 499 | 500 | ## [v0.4.0](https://github.com/cucumber/cucumber-rails/compare/v0.3.2...v0.4.0) (2011-03-20) 501 | 502 | ### New Features 503 | 504 | * Add selectors helper (#63 Bodaniel Jeanes) 505 | * Capybara date stepdefs (#66 Rob Holland) 506 | * The World now includes Rack::Test::Methods, allowing get, post, put delete (Aslak Hellesøy) 507 | 508 | ### Bugfixes 509 | 510 | * Allow setting rails root (#102, #103, Fabio Kreusch) 511 | * Fix Date selection steps and helpers (#93, #99, #100, #101, #109 James Herdman, John Ferlito, twalpole, Geoff Drake, Ricky Robinson, Michael Fleet) 512 | * Can not run cucumber-rails (0.4.0.beta.1) with cucumber (0.10.0) (#89 Aslak Hellesøy) 513 | * Installing cucumber-rails without a database.yml fails (#61 Aslak Hellesøy) 514 | * Fix #click_link compatibility with Capybara 0.4. (#54, #77, #78, #80 Aslak Hellesøy) 515 | * Support for projects using other ORMs than ActiveRecord (or nothing at all). (#18, #22, #46, #85, #86, #87, #90 Aslak Hellesøy) 516 | * @allow-rescue not working in rails 3 (#31 Joe Ferris, Aslak Hellesøy) 517 | * Gem dependency on Nokogiri (for #tableish). (#53 Aslak Hellesøy) 518 | 519 | ### Removed Features 520 | 521 | * Automatic detection of RSpec, Capybara and Webrat for install generator. Set gems explicitly in your Gemfile (Rails3) or environment.rb (Rails2) 522 | * Then /^(?:|I )should see JSON:$/. Use #get and #last_response instead. See features/rails3.feature. (Aslak Hellesøy) 523 | * Cucumber::Rails::World.use_transactional_fixtures. TODO: Explain what to do instead. 524 | No more Cucumber::Rails::World.use_transactional_fixtures = false. Use DatabaseCleaner.strategy = :none instead 525 | 526 | ## [v0.3.2](https://github.com/cucumber/cucumber-rails/compare/v0.3.1...v0.3.2) (2010-06-06) The `rails-2.3.x` support branch branches off from here. 527 | * web_steps.rb uses "([^"]*)" instead of "([^\"]*)" (Aslak Hellesøy) 528 | * Renamed cucumber:skeleton to cucumber:install (Rails 3 generator). (Aslak Hellesøy) 529 | * Upgraded to be compatible with rspec 2.0.0.beta.10 (#35 Gabor Ratky, Pete Yandell) 530 | 531 | ## [v0.3.1](https://github.com/cucumber/cucumber-rails/compare/v0.3.0...v0.3.1) (2010-05-04) 532 | 533 | This release has a lot of bugfixes! The test suite (based on Aruba) verifies that Cucumber-Rails 534 | now works with various combinations of: 535 | 536 | * Cucumber 0.6.x/0.7.2 537 | * Rails2/Rails3 538 | * RSpec1/RSpec2/Test-Unit/Mini-Uint 539 | * Webrat/CapyBara 540 | * MRI 1.8.6/1.8.7/1.9.1 541 | 542 | This one requires a special mention: The handy (but error prone) rerun functionality has moved to a 543 | separate rerun profile, so all rerun.txt related issues should now be gone. 544 | 545 | * New Capybara Step Definitions for Dates. (Rob Holland) 546 | * Steps now recognise "Given I am on the users page" etc. (Solomon White) 547 | * Relegate rerun to its own profile and rake task (Mike Sassak) 548 | * Added new Then /^(?:|I )should see JSON:$/ step definition to Capybara - useful for REST. (Aslak Hellesøy) 549 | * The #tableish method now understands tables with colspan and rowspan 550 | * Support RSpec 2 (Johan Kiviniemi, Aslak Hellesøy, Rolf Bjaanes) 551 | * Added @no-js-emulation, which turns off javascript emulation for delete links when not using browser testing. (Rob Holland, Aslak Hellesøy) 552 | * Korean translation (Shim Taewon) 553 | 554 | ### Bugfixes 555 | * No longer need to install the test-unit gem on 1.9.1 (Aslak Hellesøy) 556 | * capybara rack-test field should contain step support for textarea (#28 Nicholas Rutherford) 557 | * "Rspec is not missing constant Matchers!" error. (#27 David Chelimsky, Aslak Hellesøy) 558 | * @culerity tag breaks Rails 3 RESTful helpers (#17 Aslak Hellesøy) 559 | 560 | ## [v0.3.0](https://github.com/cucumber/cucumber-rails/compare/v0.2.4...v0.3.0) (2010-02-26) 561 | 562 | This is a major release since we're now supporting both Rails 3 and RSpec 2. Older versions 563 | (Rails 2 and RSpec 1) are still supported. 564 | 565 | ### New Features 566 | 567 | * Support for both Rails-2.x and Rails-3.x (#10 Kristian Mandrup, Aleksey Gureiev, Ashley Moran, Aslak Hellesøy) 568 | * Support for both RSpec-1.x and RSpec-2.x (Louis Rose, Aslak Hellesøy) 569 | * Features will default to Javascript emulation unless you turn it off with @culerity, (Aslak Hellesøy) 570 | * Japanese translation. (MOROHASHI Kyosuke) 571 | 572 | ### Bugfixes 573 | 574 | * Support projects that don't use ActiveRecord (#14 Aslak Hellesøy) 575 | * Running test/unit tests when creating a skeleton (#12 Aleksey Gureiev) 576 | * Inform that config/database.yml is overwritten, be smarter about it and inform the user that it is forced. (#15 Aslak Hellesøy) 577 | * Reverts changes from issue #5 where verification of query string params was added to the step for being on a page. Adds step discussed in issue #11 for verification of query string. (#5, #11 Eric Milford) 578 | * Fixed typos in Capybara's web steps (has_not_xpath? => has_no_xpath?) (Thorbjørn Hermansen, Carlos Antonio da Silva) 579 | 580 | ### New Features 581 | 582 | * Added Danish translation (Kristian Mandrup) 583 | * Using Gemfile for Rails 3. Tidies it up and adds cucumber gems only if not already present! (Kristian Mandrup) 584 | * Added Generators wrapper module for Rails 3 generators so they are now all in Cucumber::Generators (Kristian Mandrup) 585 | 586 | ### Bugfixes 587 | 588 | * Started to work on solutions for generating suitable support files depending on Rails version 589 | - see skeleton_base.rb#create_feature_support and templates/support 590 | 591 | ## [v0.2.4](https://github.com/cucumber/cucumber-rails/compare/v0.2.3...v0.2.4) (2010-01-18) 592 | 593 | ### New Features 594 | 595 | * Added Spanish translation (Gabriel) 596 | 597 | ### Bugfixes 598 | 599 | * Fixed some broken Webrat/Test::Unit step definitions. (Aslak Hellesøy) 600 | * Better Javascript emulation with Capybara (#7 Thorbjørn Hermansen) 601 | * Removed stray quote in i18n web_steps.rb (Gabriel) 602 | * Update hooks to new boolean logic (#6 Jon Larkowski, Michael MacDonald) 603 | * Fixed incompatibility with Rails 3 (Mutwin Kraus) 604 | * Fixed broken "fill in the following" step for Capybara web_steps (Lenny Marks) 605 | * Capybara web_steps.rb with_scope didn't work in nested steps (Lenny Marks) 606 | * Fixed "should not see" steps in web_steps.rb to use the correct selector (Toni Tuominen) 607 | 608 | ## [v0.2.3](https://github.com/cucumber/cucumber-rails/compare/v0.2.2...v0.2.3) (2010-01-03) 609 | 610 | ### New Features 611 | 612 | * The #tableish Proc can return Strings as well as Nokogiri nodes now. (Aslak Hellesøy) 613 | 614 | ### Bugfixes 615 | 616 | * Handle all types of URIs in "I should be on ..." steps. (#5 Andrew D. Smith) 617 | 618 | ## [v0.2.2](https://github.com/cucumber/cucumber-rails/compare/v0.2.1...v0.2.2) (2009-12-21) 619 | 620 | ### Bugfixes 621 | * Fix bad link in gemspec. (Aslak Hellesøy) 622 | * Unified file attaching step names. (Jiří Zajpt) 623 | * Fix typos in variable names in several step definitions. (Jiří Zajpt) 624 | * cucumber.rake finds the wrong vendored cucumber when a plugin name starts with "cucumber" (#4 James Herdman, Paco Benavent, Aslak Hellesøy) 625 | 626 | ### New features 627 | 628 | * Czech translations. (Jiří Zajpt) 629 | 630 | ## [v0.2.1](https://github.com/cucumber/cucumber-rails/compare/v0.2.0...v0.2.1) (2009-12-16) 631 | 632 | Small bugfix release 633 | 634 | ### Bugfixes 635 | * Made sure database_cleaner is always set up as a dependency in config/environments/cucumber.rb. (Aslak Hellesøy) 636 | 637 | 638 | This is the first release of cucumber-rails, which was factored out of cucumber. 639 | We're calling it 0.2.0 just because we did some prereleases in the 0.1 series, 640 | and to celebrate that cucumber-rails now supports Capybara as an alternative to Webrat. 641 | 642 | ### UPGRADING FROM A PREVIOUS CUCUMBER SETUP 643 | 644 | 1. Remove your `features/support/version_check.rb` 645 | 2. Remove your `features/step_definitions/webrat_steps.rb` 646 | If you have added your own custom step definitions to this file, 647 | put them in a different file under features/step_definitions 648 | 3. run `ruby script/generate cucumber --help` to see what options you have. 649 | 4. run `ruby script/generate cucumber` - plus whatever options you think fit for you. 650 | Answer "n" (no) when asked to overwrite paths.rb. 651 | Answer "y" (yes) when asked to overwrite other files, but do "d" (diff) first. 652 | If you have edits in some of these files that you want to keep, store the diff 653 | in a temporary file and reapply after you have overwritten the file. ALso consider 654 | adding your custom code to another file that won't be overwritten the next time 655 | you upgrade. 656 | 657 | Many people have edits in their `env.rb` file. This is something you should try 658 | to avoid in the future. Instead, keep your custom setup in a different file 659 | under `features/support` - Cucumber loads all files under `features/**/*.rb` anyway. 660 | 661 | If you have a Spork setup, see the end of this thread: 662 | http://groups.google.com/group/cukes/browse_thread/thread/475385cc26377215 663 | 664 | ### New features 665 | * Added an @emulate_rails_javascript tag that will emulate onclick with Capybara (Aslak Hellesøy, Rob Holland) 666 | * Added a smart config/cucumber.yml file that will rerun previously failing scenarios (Aslak Hellesøy) 667 | * Support for Capybara. Run "script/generate --capybara" if you want that. (Rob Holland, Aslak Hellesøy) 668 | * New #tableish method to extract table-like data from a HTML page. Pure Nokogiri/CSS3/XPath. (Aslak Hellesøy) 669 | 670 | ### Bugfixes 671 | * Webrat step "Then I should be on" should use request_uri instead of path for missing query string parameters (ZhangJinzhu) 672 | * Added MIME type parameter to attach file step definition (Felix Flores) 673 | * Added check when including ActiveSupport::Testing::SetupAndTeardown for older Rails versions (Jeremy Durham) 674 | 675 | [deivid-rodriguez]: https://github.com/deivid-rodriguez 676 | [koic]: https://github.com/koic 677 | [kotovalexarian]: https://github.com/kotovalexarian 678 | [mvz]: https://github.com/mvz 679 | [olleolleolle]: https://github.com/olleolleolle 680 | [luke-hill]: https://github.com/luke-hill 681 | [amatsuda]: https://github.com/amatsuda 682 | [xtrasimplicity]: https://github.com/xtrasimplicity 683 | [janko]: https://github.com/janko 684 | [damonjmurray]: https://github.com/damonjmurray 685 | [orien]: https://github.com/orien 686 | [wagenet]: https://github.com/wagenet 687 | [cgriego]: https://github.com/cgriego 688 | [botandrose]: https://github.com/botandrose 689 | [Draiken]: https://github.com/Draiken 690 | [langalex]: https://github.com/langalex 691 | [orien]: https://github.com/orien 692 | [mgrunberg]: https://github.com/mgrunberg 693 | [BrianHawley]: https://github.com/BrianHawley 694 | [mattwynne]: https://github.com/mattwynne 695 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## About to create a new GitHub Issue? 2 | 3 | We appreciate that. But before you do, please learn our basic rules: 4 | 5 | * This is not a support forum. If you have a question, please go to [The Cukes Google Group](http://groups.google.com/group/cukes). 6 | * Do you have an idea for a new feature? Then don't expect it to be implemented unless you or someone else 7 | sends a [pull request](https://help.github.com/articles/using-pull-requests). You might be better to start a discussion on [the google group](http://groups.google.com/group/cukes). 8 | * Reporting a bug? Please tell us: 9 | * which version of Cucumber you're using 10 | * which version of Ruby you're using 11 | * which version of Rails you're using (include all associated gems) 12 | * How to reproduce it. Bugs with a failing test in a [pull request](https://help.github.com/articles/using-pull-requests) get fixed much quicker. Some bugs may never be fixed. 13 | * Want to paste some code or output? Put ``` on a line above and below your code/output. See [GFM](https://help.github.com/articles/github-flavored-markdown)'s *Fenced Code Blocks* for details. 14 | * We love [pull requests](https://help.github.com/articles/using-pull-requests). But if you don't have a test to go with it we probably won't merge it. 15 | 16 | # Contributing to cucumber-rails 17 | 18 | This document is a guide for those maintaining Cucumber-Rails, and others who would like to submit patches. 19 | 20 | ## Note on Patches/Pull Requests 21 | 22 | * Fork the project. Make a branch for your change. 23 | * Make your feature addition or bug fix. 24 | * Make sure your patch is well covered by tests. We don't accept changes to `cucumber-rails` that aren't tested. 25 | * Please do not change the Rakefile, version, or CHANGELOG. 26 | * Send us a pull request. 27 | 28 | ## Running tests 29 | 30 | gem install bundler 31 | bundle install 32 | bin/install_geckodriver.sh 33 | bin/install_webpacker.sh 34 | # Then to run tests on one version-specific Gemfile (e.g. gemfiles/rails_8_0.gemfile), run 35 | bundle exec appraisal rails_8_0 rake test 36 | # Or run tests across the full supported stack. Note that because we support many versions. This takes 5-10 minutes 37 | bundle exec rake appraisal 38 | 39 | ## Updating Appraisal gems / dependencies of cucumber-rails 40 | 41 | To remove and rebuild the different gemfiles (for example, to update a rails version or its 42 | dependencies), use the following: 43 | 44 | [bundle exec] rake gemfiles:reinstall 45 | 46 | If you've changed versions of the dependencies, you may find it helpful to forcefully clean 47 | each appraisal's gem lock file in `gemfiles/`. You can do this using: 48 | 49 | [bundle exec] rake gemfiles:clean 50 | 51 | ## Release Process 52 | 53 | We now use polyglot-release. Consult [RELEASING.md](./RELEASING.md) for more info 54 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2008 Aslak Hellesøy and contributors 4 | Copyright (c) 2013 Cucumber Ltd and contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cucumber-Rails 2 | 3 | [![Gem Version](https://badge.fury.io/rb/cucumber-rails.svg)](http://badge.fury.io/rb/cucumber-rails) 4 | [![build](https://github.com/cucumber/cucumber-rails/actions/workflows/test.yml/badge.svg)](https://github.com/cucumber/cucumber-rails/actions/workflows/test.yml) 5 | [![Open Source Helpers](https://www.codetriage.com/cucumber/cucumber-rails/badges/users.svg)](https://www.codetriage.com/cucumber/cucumber-rails) 6 | 7 | Cucumber-Rails brings Cucumber to Rails 6.1, 7.x, and 8.x. 8 | 9 | ## Installation 10 | 11 | Before you can use the generator, add the gem to your project's Gemfile as follows: 12 | 13 | ```ruby 14 | group :test do 15 | gem 'cucumber-rails', require: false 16 | # database_cleaner is not required, but highly recommended 17 | gem 'database_cleaner' 18 | end 19 | ``` 20 | 21 | Then install it by running: 22 | 23 | bundle install 24 | 25 | Learn about the various options: 26 | 27 | rails generate cucumber:install --help 28 | 29 | Finally, bootstrap your Rails app, for example: 30 | 31 | rails generate cucumber:install 32 | 33 | ## Running Cucumber 34 | 35 | [bundle exec] cucumber 36 | 37 | ## Configuration options 38 | 39 | By default, cucumber-rails runs `DatabaseCleaner.start` and `DatabaseCleaner.clean` 40 | before and after your scenarios. You can disable this behaviour like so: 41 | 42 | ```ruby 43 | # features/support/env.rb 44 | # ... 45 | Cucumber::Rails::Database.autorun_database_cleaner = false 46 | ``` 47 | 48 | By default, cucumber-rails will auto mix-in the helpers from `Rack::Test` into your default Cucumber World instance. 49 | You can prevent this behaviour like so: 50 | ```ruby 51 | # features/support/env.rb 52 | ENV['CR_REMOVE_RACK_TEST_HELPERS'] = 'true' 53 | ``` 54 | 55 | ## Upgrading from a previous version 56 | 57 | When upgrading from a previous version it is recommended that you rerun: 58 | 59 | rails generate cucumber:install 60 | 61 | ## Bugs and feature requests 62 | 63 | The *only* way to have a bug fixed or a new feature accepted is to describe it with a 64 | Cucumber feature. Let's say you think you have found a bug in the cucumber:install generator. 65 | Fork this project, clone it to your workstation and check out a branch with a descriptive name: 66 | 67 | git clone git@github.com:you/cucumber-rails.git 68 | git checkout -b bugfix/generator-fails-on-bundle-exec 69 | 70 | Start by making sure you can run the existing features. Now, create a feature that demonstrates 71 | what's wrong. See the existing features for examples. When you have a failing feature that 72 | reproduces the bug, commit, push and send a pull request. Someone from the Cucumber-Rails team 73 | will review it and hopefully create a fix. 74 | 75 | If you know how to fix the bug yourself, make a second commit (after committing the failing 76 | feature) before you send the pull request. 77 | 78 | ### Setting up your environment 79 | 80 | Make sure you have a supported ruby installed, cd into your `cucumber-rails` repository and run 81 | 82 | gem install bundler 83 | bundle install 84 | bin/install_geckodriver.sh 85 | bin/install_webpacker.sh 86 | 87 | ### Running all tests 88 | 89 | With all dependencies installed, all specs and features should pass: 90 | 91 | [bundle exec] rake 92 | 93 | ### Running Appraisal suite 94 | 95 | In order to test against multiple versions of key dependencies, the [Appraisal](https://github.com/thoughtbot/appraisal) 96 | gem is used to generate multiple gemfiles, stored in the `gemfiles/` directory. 97 | Normally these will only run on GitHub via GitHub Actions; however if you want to run the full test 98 | suite against all gemfiles, run the following commands: 99 | 100 | [bundle exec] appraisal install 101 | [bundle exec] appraisal rake test 102 | 103 | To run the suite against a named gemfile, use the following: 104 | 105 | [bundle exec] appraisal rails_8_0 rake test 106 | 107 | ### Adding dependencies 108 | 109 | To support the multiple-gemfile testing, when adding a new dependency the following rules apply: 110 | 111 | 1. If it's a runtime dependency of the gem, add it to the gemspec 112 | 2. If it's a primary development dependency, add it to the gemspec 113 | 3. If it's a dependency of a generated rails app in a test, add it to [the helper method] that modifies the `Gemfile` 114 | 115 | For example, rspec is a primary development dependency, so it lives in the gemspec. 116 | 117 | [the helper method]: ./features/support/cucumber_rails_gem_helper.rb 118 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | See [.github/RELEASING](https://github.com/cucumber/.github/blob/main/RELEASING.md). -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'appraisal' 4 | require 'cucumber/rake/task' 5 | require 'rspec/core/rake_task' 6 | 7 | Cucumber::Rake::Task.new 8 | RSpec::Core::RakeTask.new 9 | 10 | $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib") 11 | 12 | task default: :test 13 | task test: %i[spec cucumber] 14 | 15 | namespace :test do 16 | desc 'Run tests against all gemfiles' 17 | task :all do 18 | Rake::Task['appraisal'].invoke('test') 19 | end 20 | 21 | desc 'Run tests against specified gemfile, e.g. rake test:gemfile[rails_6_0]' 22 | task :gemfile, :name do |_task, args| 23 | unless args.name && File.exist?("gemfiles/#{args.name}.gemfile") 24 | raise ArgumentError, "You must provide the name of an existing Appraisal gemfile, 25 | e.g. 'rake test:gemfile[rails_6_0]'" 26 | end 27 | 28 | Rake::Task["appraisal:#{args.name}"].invoke('test') 29 | end 30 | end 31 | 32 | namespace :gemfiles do 33 | desc 'Re-install dependencies for all gemfiles' 34 | task :reinstall do 35 | system 'bundle exec appraisal update' 36 | end 37 | 38 | desc 'Remove all generated gemfiles' 39 | task :clean do 40 | system 'bundle exec appraisal clean' 41 | end 42 | 43 | desc 'Remove all generated gemfiles and re-install dependencies' 44 | task rebuild: %i[clean reinstall] 45 | end 46 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 3.1.1 2 | -------------------------------------------------------------------------------- /bin/install_geckodriver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xeuo pipefail 4 | 5 | curl --silent \ 6 | --show-error \ 7 | --location \ 8 | --fail \ 9 | --retry 3 \ 10 | --output /tmp/geckodriver_linux64.tar.gz \ 11 | https://github.com/mozilla/geckodriver/releases/download/v0.33.0/geckodriver-v0.33.0-linux64.tar.gz 12 | 13 | sudo tar -C /usr/local/bin -xvzf /tmp/geckodriver_linux64.tar.gz geckodriver 14 | 15 | rm /tmp/geckodriver_linux64.tar.gz 16 | 17 | geckodriver --version 18 | 19 | set +x 20 | -------------------------------------------------------------------------------- /bin/install_webpacker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xeuo pipefail 4 | 5 | curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - 6 | echo 'deb https://dl.yarnpkg.com/debian/ stable main' | sudo tee /etc/apt/sources.list.d/yarn.list 7 | sudo apt update && sudo apt install --yes yarn 8 | 9 | set +x 10 | -------------------------------------------------------------------------------- /config/.gitignore: -------------------------------------------------------------------------------- 1 | aruba-rvm.yml 2 | -------------------------------------------------------------------------------- /config/cucumber.yml: -------------------------------------------------------------------------------- 1 | <% 2 | rerun_file = '.cucumber.rerun' 3 | failing_scenarios = IO.read(rerun_file) rescue '' 4 | path = if failing_scenarios.empty? 5 | 'features' 6 | else 7 | puts "Running failed scenarios" 8 | failing_scenarios.gsub /\s/, ' ' 9 | end 10 | opts = [ 11 | "--format #{path == 'features' ? 'progress' : 'pretty'}", 12 | "--format rerun -o #{rerun_file}" 13 | ] 14 | optlist = opts.join(' ') 15 | %> 16 | default: <%= path %> <%= optlist %> 17 | -------------------------------------------------------------------------------- /cucumber-rails.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.unshift File.expand_path('lib', __dir__) 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'cucumber-rails' 7 | s.version = File.read("#{__dir__}/VERSION") 8 | s.authors = ['Aslak Hellesøy', 'Dennis Blöte', 'Rob Holland'] 9 | s.description = 'Cucumber Generator and Runtime for Rails' 10 | s.summary = "#{s.name}-#{s.version}" 11 | s.email = 'cukes@googlegroups.com' 12 | s.homepage = 'https://cucumber.io' 13 | 14 | s.license = 'MIT' 15 | 16 | s.required_ruby_version = '>= 3.1.0' 17 | s.required_rubygems_version = '>= 3.2.8' 18 | 19 | s.metadata = { 20 | 'bug_tracker_uri' => 'https://github.com/cucumber/cucumber-rails/issues', 21 | 'changelog_uri' => "https://github.com/cucumber/cucumber-rails/blob/v#{s.version}/CHANGELOG.md", 22 | 'documentation_uri' => 'https://cucumber.io/docs', 23 | 'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/cukes', 24 | 'source_code_uri' => "https://github.com/cucumber/cucumber-rails/tree/v#{s.version}" 25 | } 26 | 27 | s.add_runtime_dependency('capybara', '>= 3.11', '< 4') 28 | s.add_runtime_dependency('cucumber', '>= 5', '< 10') 29 | s.add_runtime_dependency('railties', '>= 5.2', '< 9') 30 | 31 | # Main development dependencies 32 | s.add_development_dependency('ammeter', '>= 1.1.5') 33 | s.add_development_dependency('appraisal', '>= 2.4.1', '< 3') 34 | s.add_development_dependency('aruba', '>= 1.1.2', '< 3') 35 | s.add_development_dependency('database_cleaner', '~> 2.0') 36 | s.add_development_dependency('rails', '>= 5.2', '< 9') 37 | s.add_development_dependency('rake', '~> 13.2') 38 | s.add_development_dependency('rspec', '~> 3.13') 39 | s.add_development_dependency('rubocop', '~> 1.45.0') 40 | s.add_development_dependency('rubocop-packaging', '~> 0.5.2') 41 | s.add_development_dependency('rubocop-performance', '~> 1.17.1') 42 | s.add_development_dependency('rubocop-rails', '~> 2.18.0') 43 | s.add_development_dependency('rubocop-rake', '~> 0.6.0') 44 | s.add_development_dependency('rubocop-rspec', '~> 2.17.0') 45 | 46 | # For Documentation: 47 | s.add_development_dependency('yard', '~> 0.9.10') 48 | 49 | s.require_path = 'lib' 50 | s.files = Dir['lib/**/*', 'CHANGELOG.md', 'LICENSE', 'README.md'] 51 | end 52 | -------------------------------------------------------------------------------- /features/allow_rescue.feature: -------------------------------------------------------------------------------- 1 | Feature: Allow Cucumber to rescue exceptions 2 | 3 | Background: A controller that raises an exception 4 | Given I have created a new Rails app and installed cucumber-rails 5 | When I write to "app/controllers/posts_controller.rb" with: 6 | """ 7 | class PostsController < ApplicationController 8 | def index 9 | raise 'There is an error in index' 10 | end 11 | end 12 | """ 13 | And I write to "config/routes.rb" with: 14 | """ 15 | TestApp::Application.routes.draw do 16 | resources :posts 17 | end 18 | """ 19 | 20 | Scenario: Allow rescue 21 | When I write to "features/posts.feature" with: 22 | """ 23 | Feature: Posts 24 | @allow-rescue 25 | Scenario: See posts 26 | When I look at the posts 27 | Then I should see the public error page 28 | """ 29 | And I write to "features/step_definitions/posts_steps.rb" with: 30 | """ 31 | When('I look at the posts') do 32 | visit '/posts' 33 | end 34 | 35 | Then('I should see the public error page') do 36 | expect(page).to have_content "sorry, but something went wrong." 37 | end 38 | """ 39 | And I run `bundle exec rake db:migrate` 40 | And I run `bundle exec cucumber` 41 | Then the feature should pass with: 42 | """ 43 | 1 scenario (1 passed) 44 | 2 steps (2 passed) 45 | """ 46 | 47 | Scenario: Don't allow rescue 48 | When I write to "features/posts.feature" with: 49 | """ 50 | Feature: Posts 51 | Scenario: See them 52 | When I look at the posts 53 | """ 54 | And I write to "features/step_definitions/posts_steps.rb" with: 55 | """ 56 | When('I look at the posts') do 57 | visit '/posts' 58 | end 59 | """ 60 | And I run `bundle exec rake db:migrate` 61 | And I run `bundle exec cucumber` 62 | Then it should fail with: 63 | """ 64 | 1 scenario (1 failed) 65 | 1 step (1 failed) 66 | """ 67 | -------------------------------------------------------------------------------- /features/annotations.feature: -------------------------------------------------------------------------------- 1 | Feature: Annotations 2 | 3 | In order to track my development progress 4 | As a developer 5 | I should be able to list annotations in my features 6 | 7 | Scenario: See annotations in feature file 8 | Given I have created a new Rails app with no database and installed cucumber-rails 9 | When I write to "features/products.feature" with: 10 | """ 11 | Feature: Products 12 | Scenario: Test a Product 13 | # TODO: When I go to the products page 14 | """ 15 | When I run `bundle exec rails notes` 16 | Then it should pass with: 17 | """ 18 | features/products.feature: 19 | * [3] [TODO] When I go to the products page 20 | """ 21 | -------------------------------------------------------------------------------- /features/capybara_javascript_drivers.feature: -------------------------------------------------------------------------------- 1 | Feature: Capybara Javascript Drivers 2 | 3 | Scenario: Use Browser UI backed by DB 4 | Given I have created a new Rails app and installed cucumber-rails 5 | And I force selenium to run Firefox in headless mode 6 | When I run `bundle exec rails g scaffold appointment name:string when:datetime` 7 | And I write to "features/create_appointment.feature" with: 8 | """ 9 | @javascript 10 | Feature: Create appointments 11 | Scenario: Create an appointment using the Web Interface 12 | Given I am on the new appointment page 13 | When I fill in "Cucumber Trainee" for "Name" 14 | And I select "2026-02-20 15:10:00 UTC" as the "When" date and time 15 | And I press "Create Appointment" 16 | Then I should see "Cucumber Trainee" 17 | And I should see "2026-02-20 15:10:00 UTC" 18 | """ 19 | And I write to "features/step_definitions/create_appointment_steps.rb" with: 20 | """ 21 | Given('I am on the new appointment page') do 22 | visit new_appointment_path 23 | end 24 | 25 | When('I fill in {string} for {string}') do |value, field| 26 | fill_in(field, with: value) 27 | end 28 | 29 | When('I press {string}') do |button| 30 | click_button(button) 31 | end 32 | 33 | When('I select {string} as the {string} date and time') do |datetime, selector| 34 | select_datetime(datetime, from: selector) 35 | end 36 | 37 | Then('I should see {string}') do |text| 38 | expect(page).to have_content(text) 39 | end 40 | """ 41 | And I run `bundle exec rake db:migrate` 42 | And I run `bundle exec rake cucumber` 43 | Then the feature should pass with: 44 | """ 45 | 1 scenario (1 passed) 46 | 6 steps (6 passed) 47 | """ 48 | 49 | Scenario: Support non HTML5 date inputs 50 | Given I have created a new Rails app and installed cucumber-rails 51 | And I force selenium to run Firefox in headless mode 52 | When I run `bundle exec rails g scaffold appointment name:string when:datetime` 53 | And I force "app/views/appointments/_form.html.erb" to use select boxes for dates 54 | And I write to "features/create_appointment.feature" with: 55 | """ 56 | @javascript 57 | Feature: Create appointments 58 | Scenario: Create an appointment using the Web Interface 59 | Given I am on the new appointment page 60 | When I fill in "Cucumber Trainee" for "Name" 61 | And I select "2026-02-20 15:10:00 UTC" as the "When" date and time 62 | And I press "Create Appointment" 63 | Then I should see "Cucumber Trainee" 64 | And I should see "2026-02-20 15:10:00 UTC" 65 | """ 66 | And I write to "features/step_definitions/create_appointment_steps.rb" with: 67 | """ 68 | Given('I am on the new appointment page') do 69 | visit new_appointment_path 70 | end 71 | 72 | When('I fill in {string} for {string}') do |value, field| 73 | fill_in(field, with: value) 74 | end 75 | 76 | When('I press {string}') do |button| 77 | click_button(button) 78 | end 79 | 80 | When('I select {string} as the {string} date and time') do |datetime, selector| 81 | select_datetime(datetime, from: selector) 82 | end 83 | 84 | Then('I should see {string}') do |text| 85 | expect(page).to have_content(text) 86 | end 87 | """ 88 | And I run `bundle exec rake db:migrate` 89 | And I run `bundle exec rake cucumber` 90 | Then the feature should pass with: 91 | """ 92 | 1 scenario (1 passed) 93 | 6 steps (6 passed) 94 | """ 95 | 96 | Scenario: Use direct DB injection 97 | Given I have created a new Rails app and installed cucumber-rails 98 | And I force selenium to run Firefox in headless mode 99 | When I run `bundle exec rails g scaffold appointment name:string when:datetime` 100 | And I write to "features/create_appointment.feature" with: 101 | """ 102 | @javascript 103 | Feature: Create appointments 104 | Scenario: Create appointment using DB injection 105 | Given an appointment for today 106 | When I view the newly created appointment 107 | Then I should see the correct appointment details 108 | """ 109 | And I write to "features/step_definitions/create_appointment_steps.rb" with: 110 | """ 111 | Given('an appointment for today') do 112 | @when = Time.now.utc 113 | @name = 'Random appointment for Cucumber Trainee' 114 | @appointment = Appointment.create!(name: @name, when: @when) 115 | end 116 | 117 | Given('I view the newly created appointment') do 118 | visit appointment_path(@appointment) 119 | end 120 | 121 | Then('I should see the correct appointment details') do 122 | expect(page).to have_text(@name) 123 | 124 | expect(page).to have_text(@when) 125 | end 126 | """ 127 | And I run `bundle exec rake db:migrate` 128 | And I run `bundle exec rake cucumber` 129 | Then the feature should pass with: 130 | """ 131 | 1 scenario (1 passed) 132 | 3 steps (3 passed) 133 | """ 134 | 135 | Scenario: Use Browser UI without a DB 136 | Given I have created a new Rails app and installed cucumber-rails without database_cleaner 137 | And I force selenium to run Firefox in headless mode 138 | When I run `bundle exec rails g scaffold appointment name:string when:datetime` 139 | And I write to "features/create_appointment.feature" with: 140 | """ 141 | @javascript 142 | Feature: Create appointments 143 | Scenario: Visit the Constitution on May 17 144 | Given I am on the new appointment page 145 | When I fill in "Cucumber Trainee" for "Name" 146 | And I select "2026-02-20 15:10:00 UTC" as the "When" date and time 147 | And I press "Create Appointment" 148 | Then I should see "Cucumber Trainee" 149 | And I should see "2026-02-20 15:10:00 UTC" 150 | """ 151 | And I write to "features/step_definitions/create_appointment_steps.rb" with: 152 | """ 153 | Given('I am on the new appointment page') do 154 | visit new_appointment_path 155 | end 156 | 157 | When('I fill in {string} for {string}') do |value, field| 158 | fill_in(field, with: value) 159 | end 160 | 161 | When('I press {string}') do |button| 162 | click_button(button) 163 | end 164 | 165 | When('I select {string} as the {string} date and time') do |datetime, selector| 166 | select_datetime(datetime, from: selector) 167 | end 168 | 169 | Then('I should see {string}') do |text| 170 | expect(page).to have_content(text) 171 | end 172 | """ 173 | And I append to "features/support/env.rb" with: 174 | """ 175 | Cucumber::Rails::Database.autorun_database_cleaner = false 176 | """ 177 | And I run `bundle exec rake db:migrate` 178 | And I run `bundle exec rake cucumber` 179 | Then the feature should pass with: 180 | """ 181 | 1 scenario (1 passed) 182 | 6 steps (6 passed) 183 | """ 184 | -------------------------------------------------------------------------------- /features/choose_javascript_database_strategy.feature: -------------------------------------------------------------------------------- 1 | Feature: Choose javascript database strategy 2 | 3 | Whilst running a scenario with the @javascript tag, Capybara will fire up a web server 4 | in the same process in a separate thread to your cukes. By default, this means ActiveRecord will give it a 5 | separate database connection, which in turn means data you put into your database from 6 | Cucumber step definitions (e.g. using FactoryBot) won't be visible to the web server 7 | until the database transaction is committed. 8 | 9 | So if you use a transaction strategy for cleaning up your database at the end of a scenario, 10 | it won't work for javascript scenarios by default. 11 | 12 | There are two ways around this. One is to switch to a truncation strategy for javascript 13 | scenarios. This is slower, but more reliable. 14 | 15 | The alternative is to patch ActiveRecord to share a single database connection between 16 | threads. This means you still get the speed benefits of using a transaction to roll back 17 | your database, but you run the risk of the two threads stomping on one another as they 18 | talk to the database. 19 | 20 | Right now, the default behavior is to use truncation, but you can override this by telling 21 | cucumber-rails which strategy to use for javascript scenarios. 22 | 23 | The `:deletion` strategy can be quicker for situations where truncation causes locks which 24 | has been reported by some Oracle users. 25 | 26 | The `:none` strategy can be used when user doesn't want special handling of scenario 27 | database clean-up regardless of tags set for said scenario. 28 | 29 | Background: 30 | Given I have created a new Rails app and installed cucumber-rails 31 | And I have a "Widget" ActiveRecord model object 32 | When I write to "features/step_definitions/widget_steps.rb" with: 33 | """ 34 | When('I create {int} widgets') do |number| 35 | number.times { Widget.create! } 36 | end 37 | 38 | Then('I should have {int} widgets') do |number| 39 | expect(Widget.count).to eq(number) 40 | end 41 | 42 | Then('the DatabaseCleaner strategy should be {word}') do |strategy_name| 43 | expect(DatabaseCleaner.cleaners.values.first.strategy.to_s).to match(/#{strategy_name}/i) 44 | end 45 | """ 46 | 47 | Scenario: Set the strategy to truncation and run a javascript scenario. 48 | When I append to "features/env.rb" with: 49 | """ 50 | DatabaseCleaner.strategy = :transaction 51 | Cucumber::Rails::Database.javascript_strategy = :truncation 52 | """ 53 | And I write to "features/widgets.feature" with: 54 | """ 55 | Feature: 56 | Background: 57 | When I create 2 widgets 58 | 59 | @javascript 60 | Scenario: 61 | When I create 3 widgets 62 | Then the DatabaseCleaner strategy should be truncation 63 | And I should have 5 widgets 64 | 65 | @javascript 66 | Scenario: 67 | Then the DatabaseCleaner strategy should be truncation 68 | And I should have 2 widgets 69 | 70 | Scenario: 71 | Then the DatabaseCleaner strategy should be transaction 72 | And I should have 2 widgets 73 | """ 74 | And I run `bundle exec cucumber` 75 | Then the feature should pass with: 76 | """ 77 | 3 scenarios (3 passed) 78 | 10 steps (10 passed) 79 | """ 80 | 81 | Scenario: Set the strategy to deletion and run a javascript scenario. 82 | When I append to "features/env.rb" with: 83 | """ 84 | Cucumber::Rails::Database.javascript_strategy = :deletion 85 | """ 86 | And I write to "features/widgets.feature" with: 87 | """ 88 | @javascript 89 | Feature: 90 | Background: 91 | When I create 2 widgets 92 | 93 | Scenario: 94 | When I create 3 widgets 95 | Then I should have 5 widgets 96 | 97 | Scenario: 98 | Then I should have 2 widgets 99 | """ 100 | And I run `bundle exec cucumber` 101 | Then the feature should pass with: 102 | """ 103 | 2 scenarios (2 passed) 104 | 5 steps (5 passed) 105 | """ 106 | 107 | Scenario: Set the strategy to none and run a javascript scenario. 108 | When I append to "features/env.rb" with: 109 | """ 110 | DatabaseCleaner.strategy = :transaction 111 | Cucumber::Rails::Database.javascript_strategy = :none 112 | """ 113 | And I write to "features/widgets.feature" with: 114 | """ 115 | Feature: 116 | Background: 117 | When I create 2 widgets 118 | 119 | @javascript 120 | Scenario: 121 | When I create 3 widgets 122 | Then I should have 5 widgets 123 | And the DatabaseCleaner strategy should be transaction 124 | 125 | Scenario: 126 | Then I should have 2 widgets 127 | And the DatabaseCleaner strategy should be transaction 128 | """ 129 | And I run `bundle exec cucumber` 130 | Then the feature should pass with: 131 | """ 132 | 2 scenarios (2 passed) 133 | 7 steps (7 passed) 134 | """ 135 | 136 | Scenario: Set the strategy to truncation with an except option and run a javascript scenario. 137 | When I append to "features/env.rb" with: 138 | """ 139 | Cucumber::Rails::Database.javascript_strategy = :truncation, { except: %w[widgets] } 140 | """ 141 | And I write to "features/widgets.feature" with: 142 | """ 143 | @javascript 144 | Feature: 145 | Scenario: 146 | When I create 3 widgets 147 | Then I should have 3 widgets 148 | 149 | Scenario: 150 | Then I should have 3 widgets 151 | """ 152 | And I run `bundle exec cucumber` 153 | Then the feature should pass with: 154 | """ 155 | 2 scenarios (2 passed) 156 | 3 steps (3 passed) 157 | """ 158 | -------------------------------------------------------------------------------- /features/configuration.feature: -------------------------------------------------------------------------------- 1 | Feature: Cucumber Rails Configuration 2 | 3 | In order to configure how Cucumber-Rails runs 4 | As a developer 5 | I should be able to alter the settings of my project 6 | 7 | Scenario: Default configuration 8 | Given I have created a new Rails app with no database and installed cucumber-rails 9 | When I write to "features/config.feature" with: 10 | """ 11 | Feature: Rack Test Methods 12 | Scenario: Default Methods are added to the World 13 | Then Rack Test should be mixed into the Cucumber World 14 | """ 15 | When I write to "features/step_definitions/config_steps.rb" with: 16 | """ 17 | Then('Rack Test should be mixed into the Cucumber World') do 18 | expect(self.class.ancestors).to include(Rack::Test::Methods) 19 | end 20 | """ 21 | And I run `bundle exec rake cucumber` 22 | Then the feature should pass with: 23 | """ 24 | 1 scenario (1 passed) 25 | 1 step (1 passed) 26 | """ 27 | 28 | Scenario: Altered configuration 29 | Given I have created a new Rails app with no database and installed cucumber-rails 30 | And I set the environment variable "CR_REMOVE_RACK_TEST_HELPERS" to "true" 31 | When I write to "features/config.feature" with: 32 | """ 33 | Feature: Rack Test Methods 34 | Scenario: Rack Test Methods can be removed from the world 35 | Then Rack Test should not be mixed into the Cucumber World 36 | """ 37 | When I write to "features/step_definitions/config_steps.rb" with: 38 | """ 39 | Then('Rack Test should not be mixed into the Cucumber World') do 40 | expect(self.class.ancestors).not_to include(Rack::Test::Methods) 41 | end 42 | """ 43 | And I run `bundle exec rake cucumber` 44 | Then the feature should pass with: 45 | """ 46 | 1 scenario (1 passed) 47 | 1 step (1 passed) 48 | """ 49 | -------------------------------------------------------------------------------- /features/database_cleaner.feature: -------------------------------------------------------------------------------- 1 | Feature: Database Cleaner 2 | 3 | Scenario: Create records in background with database_cleaner 4 | Given I have created a new Rails app and installed cucumber-rails 5 | When I write to "features/widgets.feature" with: 6 | """ 7 | Feature: Create widgets 8 | Background: 2 initial widgets 9 | Given I have 2 widgets 10 | 11 | Scenario: Add 3 widgets 12 | When I create 3 more widgets 13 | Then I should have 5 widgets 14 | 15 | Scenario: Add 7 widgets 16 | When I create 7 more widgets 17 | Then I should have 9 widgets 18 | """ 19 | And I run `bundle exec rails generate model widget name:string` 20 | And I write to "features/step_definitions/widget_steps.rb" with: 21 | """ 22 | Given('I have {int} widgets') do |number| 23 | number.times do |i| 24 | Widget.create! name: "Widget #{Widget.count + i}" 25 | end 26 | end 27 | 28 | When('I create {int} more widgets') do |number| 29 | number.times do |i| 30 | Widget.create! name: "Widget #{Widget.count + i}" 31 | end 32 | end 33 | 34 | Then('I should have {int} widgets') do |number| 35 | expect(Widget.count).to eq(number) 36 | end 37 | """ 38 | And I run `bundle exec rake db:migrate` 39 | And I run `bundle exec rake cucumber` 40 | Then the feature should pass with: 41 | """ 42 | 2 scenarios (2 passed) 43 | 6 steps (6 passed) 44 | """ 45 | 46 | Scenario: Create records in background with database_cleaner-active_record 47 | Given I have created a new Rails app and installed cucumber-rails with database_cleaner-active_record 48 | When I write to "features/widgets.feature" with: 49 | """ 50 | Feature: Create widgets 51 | Background: 2 initial widgets 52 | Given I have 2 widgets 53 | 54 | Scenario: Add 3 widgets 55 | When I create 3 more widgets 56 | Then I should have 5 widgets 57 | 58 | Scenario: Add 7 widgets 59 | When I create 7 more widgets 60 | Then I should have 9 widgets 61 | """ 62 | And I run `bundle exec rails generate model widget name:string` 63 | And I write to "features/step_definitions/widget_steps.rb" with: 64 | """ 65 | Given('I have {int} widgets') do |number| 66 | number.times do |i| 67 | Widget.create! name: "Widget #{Widget.count + i}" 68 | end 69 | end 70 | 71 | When('I create {int} more widgets') do |number| 72 | number.times do |i| 73 | Widget.create! name: "Widget #{Widget.count + i}" 74 | end 75 | end 76 | 77 | Then('I should have {int} widgets') do |number| 78 | expect(Widget.count).to eq(number) 79 | end 80 | """ 81 | And I run `bundle exec rake db:migrate` 82 | And I run `bundle exec rake cucumber` 83 | Then the feature should pass with: 84 | """ 85 | 2 scenarios (2 passed) 86 | 6 steps (6 passed) 87 | """ 88 | 89 | Scenario: Create records in background without using database_cleaner 90 | Given I have created a new Rails app and installed cucumber-rails without database_cleaner 91 | When I write to "features/widgets.feature" with: 92 | """ 93 | Feature: Create widgets 94 | Background: 2 initial widgets 95 | # Note this creates 2 widgets each time 96 | Given I have 2 widgets 97 | 98 | Scenario: Add 3 widgets 99 | When I create 3 more widgets 100 | Then I should have 5 widgets 101 | 102 | Scenario: Add 7 widgets 103 | When I create 7 more widgets 104 | Then I should have 14 widgets 105 | """ 106 | And I run `bundle exec rails generate model widget name:string` 107 | And I write to "features/step_definitions/widget_steps.rb" with: 108 | """ 109 | Given('I have {int} widgets') do |number| 110 | number.times do |i| 111 | Widget.create! name: "Widget #{Widget.count + i}" 112 | end 113 | end 114 | 115 | When('I create {int} more widgets') do |number| 116 | number.times do |i| 117 | Widget.create! name: "Widget #{Widget.count + i}" 118 | end 119 | end 120 | 121 | Then('I should have {int} widgets') do |number| 122 | expect(Widget.count).to eq(number) 123 | end 124 | """ 125 | And I run `bundle exec rake db:migrate` 126 | And I run `bundle exec rake cucumber` 127 | Then the feature should pass with: 128 | """ 129 | 2 scenarios (2 passed) 130 | 6 steps (6 passed) 131 | """ 132 | -------------------------------------------------------------------------------- /features/disable_automatic_database_cleaning.feature: -------------------------------------------------------------------------------- 1 | Feature: Disable automatic database cleaning 2 | 3 | By default, a set of Before/After hooks are installed to 4 | invoke database_cleaner on every scenario except those tagged 5 | "@no-database-cleaner". Sometimes when a user is switching between 6 | cleaning strategies, this can initiate an undesired database 7 | transaction. 8 | 9 | To avoid the need for users making frequent strategy switches to apply 10 | this tag, a configuration option is provided so that the user can 11 | control the invocation of database_cleaner explicitly. 12 | 13 | Scenario: Disabling automatic cleaning 14 | Given I have created a new Rails app and installed cucumber-rails 15 | When I append to "features/env.rb" with: 16 | """ 17 | Cucumber::Rails::Database.autorun_database_cleaner = false 18 | """ 19 | And I write to "features/widgets.feature" with: 20 | """ 21 | Feature: Create widgets 22 | Scenario: Create 3 widgets 23 | When I create 3 widgets 24 | Then I should have 3 widgets 25 | 26 | Scenario: Create 5 widgets 27 | When I create 5 widgets 28 | Then I should have 8 widgets 29 | """ 30 | And I run `bundle exec rails generate model widget name:string` 31 | And I write to "features/step_definitions/widget_steps.rb" with: 32 | """ 33 | When('I create {int} widgets') do |number| 34 | number.times do |i| 35 | Widget.create! name: "Widget #{Widget.count + i}" 36 | end 37 | end 38 | 39 | Then('I should have {int} widgets') do |number| 40 | expect(Widget.count).to eq(number) 41 | end 42 | """ 43 | And I run `bundle exec rake db:migrate` 44 | And I run `bundle exec rake cucumber` 45 | Then the feature should pass with: 46 | """ 47 | 2 scenarios (2 passed) 48 | 4 steps (4 passed) 49 | """ 50 | -------------------------------------------------------------------------------- /features/emulate_javascript.feature: -------------------------------------------------------------------------------- 1 | Feature: Emulate Javascript 2 | 3 | Background: 4 | Given I have created a new Rails app and installed cucumber-rails 5 | And I force selenium to run Firefox in headless mode 6 | When I run `bundle exec rails generate scaffold widget name:string` 7 | And I write to "features/step_definitions/widget_steps.rb" with: 8 | """ 9 | Given('there is a widget named {string}') do |name| 10 | FactoryBot.create(:widget, name: name) 11 | end 12 | 13 | When('I am on the widgets page') do 14 | visit widgets_path 15 | end 16 | 17 | Then('I should see {string}') do |text| 18 | expect(page).to have_content(text) 19 | end 20 | """ 21 | And I write to "features/support/factories/widget.rb" with: 22 | """ 23 | FactoryBot.define do 24 | factory :widget do 25 | name { 'testwidget' } 26 | end 27 | end 28 | """ 29 | 30 | Scenario: See a widget 31 | When I write to "features/widgets.feature" with: 32 | """ 33 | @javascript 34 | Feature: Widget inventory 35 | Scenario: Delete a widget 36 | Given there is a widget named "wrench" 37 | And I am on the widgets page 38 | Then I should see "wrench" 39 | """ 40 | And I run `bundle exec rake db:migrate` 41 | And I run `bundle exec rake cucumber` 42 | Then the feature should pass with: 43 | """ 44 | 1 scenario (1 passed) 45 | 3 steps (3 passed) 46 | """ 47 | 48 | Scenario: Pass on the CSRF token 49 | When I run `sed -i -e 's/forgery_protection *= false/forgery_protection = true/' config/environments/test.rb` 50 | And I run `bundle exec rails generate controller session establish` 51 | And I write to "app/controllers/session_controller.rb" with: 52 | """ 53 | class SessionController < ApplicationController 54 | def establish 55 | session[:verified] = true 56 | end 57 | end 58 | """ 59 | And I write to "app/controllers/application_controller.rb" with: 60 | """ 61 | class ApplicationController < ActionController::Base 62 | protect_from_forgery 63 | 64 | before_action except: :establish do 65 | render text: 'denied', status: :forbidden and return false unless session[:verified] 66 | end 67 | 68 | # Rails 7.1 introduces raise_on_missing_callback_conditionals and it's on by default 69 | def establish 70 | raise "This action must be implemented in child controllers" 71 | end 72 | end 73 | """ 74 | And I write to "features/widgets.feature" with: 75 | """ 76 | Feature: Widget inventory 77 | Scenario: Delete a widget 78 | Given there is a widget named "wrench" 79 | And I am on the session establish page 80 | And I navigate to destroy "wrench" page 81 | Then I should see "wrench" 82 | And I should see "Destroy" 83 | When I follow "Destroy" 84 | Then I should not see "denied" 85 | And I should be on the widgets page 86 | And I should not see "wrench" 87 | """ 88 | And I append the following lines to "features/step_definitions/widget_steps.rb": 89 | """ 90 | Given('I am on the session establish page') do 91 | visit session_establish_path 92 | end 93 | 94 | Given('I navigate to destroy {string} page') do |name| 95 | if ::Rails::VERSION::MAJOR >= 7 96 | visit widget_path(Widget.find_by(name: name)) 97 | else 98 | visit widgets_path 99 | end 100 | end 101 | 102 | When('I follow {string}') do |link| 103 | if ::Rails::VERSION::MAJOR >= 7 104 | click_button(link) 105 | else 106 | click_link(link) 107 | end 108 | end 109 | 110 | Then('I should not see {string}') do |text| 111 | expect(page).not_to have_content(text) 112 | end 113 | 114 | Then('I should be on the widgets page') do 115 | current_path = URI.parse(current_url).path 116 | expect(current_path).to eq(widgets_path) 117 | end 118 | """ 119 | And I run `bundle exec rake db:migrate` 120 | And I run `bundle exec rake cucumber` 121 | Then the feature should pass with: 122 | """ 123 | 1 scenario (1 passed) 124 | 9 steps (9 passed) 125 | """ 126 | -------------------------------------------------------------------------------- /features/install_cucumber_rails.feature: -------------------------------------------------------------------------------- 1 | Feature: Install Cucumber Rails 2 | 3 | Cucumber-Rails should work on supported versions 4 | of Ruby on Rails, with Capybara and DatabaseCleaner 5 | 6 | Scenario: Install Cucumber-Rails 7 | Given I have created a new Rails app and installed cucumber-rails 8 | Then the following files should exist: 9 | | config/cucumber.yml | 10 | | bin/cucumber | 11 | | features/support/env.rb | 12 | | features/step_definitions/.keep | 13 | | lib/tasks/cucumber.rake | 14 | And the file "features/support/env.rb" should contain "require 'cucumber/rails'" 15 | -------------------------------------------------------------------------------- /features/no_database.feature: -------------------------------------------------------------------------------- 1 | Feature: No Database 2 | 3 | Allow Cucumber to work with a Rails app without a database 4 | 5 | Scenario: No ActiveRecord and DatabaseCleaner 6 | Given I have created a new Rails app with no database and installed cucumber-rails 7 | # Turn off ActiveRecord 8 | When I write to "config/application.rb" with: 9 | """ 10 | require File.expand_path('../boot', __FILE__) 11 | 12 | require 'action_controller/railtie' 13 | 14 | Bundler.require(:default, Rails.env) if defined?(Bundler) 15 | 16 | module TestApp 17 | class Application < Rails::Application 18 | config.encoding = 'utf-8' 19 | config.filter_parameters += [:password] 20 | end 21 | end 22 | """ 23 | And I overwrite "features/support/env.rb" with: 24 | """ 25 | require 'cucumber/rails' 26 | """ 27 | And I remove the 'database_cleaner' gem from the Gemfile 28 | And I remove the 'sqlite' gem from the Gemfile 29 | And I write to "app/controllers/posts_controller.rb" with: 30 | """ 31 | class PostsController < ApplicationController 32 | def index 33 | raise 'There is an error in index' 34 | end 35 | end 36 | """ 37 | And I write to "config/routes.rb" with: 38 | """ 39 | TestApp::Application.routes.draw do 40 | resources :posts 41 | end 42 | """ 43 | And I write to "features/posts.feature" with: 44 | """ 45 | Feature: posts 46 | Scenario: See them 47 | When I view the posts 48 | """ 49 | And I write to "features/step_definitions/posts_steps.rb" with: 50 | """ 51 | When('I view the posts') do 52 | visit '/posts' 53 | end 54 | """ 55 | And I run `bundle exec rake cucumber` 56 | Then it should fail with: 57 | """ 58 | There is an error in index 59 | """ 60 | -------------------------------------------------------------------------------- /features/raising_errors.feature: -------------------------------------------------------------------------------- 1 | Feature: Raise Errors 2 | 3 | Scenario: Raise error for undefined route 4 | Given I have created a new Rails app with no database and installed cucumber-rails 5 | When I write to "features/home.feature" with: 6 | """ 7 | Feature: Tests 8 | Scenario: Tests 9 | When I go to the home page 10 | """ 11 | And I write to "features/step_definitions/home_steps.rb" with: 12 | """ 13 | When('I go to the home page') do 14 | visit('/') 15 | end 16 | """ 17 | And I run `bundle exec cucumber` 18 | Then it should fail with: 19 | """ 20 | 1 scenario (1 failed) 21 | 1 step (1 failed) 22 | """ 23 | -------------------------------------------------------------------------------- /features/rerun_profile.feature: -------------------------------------------------------------------------------- 1 | Feature: Rerun profile 2 | 3 | In order to concentrate on failing features 4 | As a Rails developer working with Cucumber 5 | I want to rerun only failing features 6 | 7 | Scenario: Rerun 8 | Given I have created a new Rails app and installed cucumber-rails 9 | And a file named "rerun.txt" with: 10 | """ 11 | features/rerun_test.feature:2 12 | features/rerun_test.feature:5 13 | """ 14 | And a file named "features/rerun_test.feature" with: 15 | """ 16 | Feature: Rerun test 17 | Scenario: failing before 18 | Given fixed now 19 | 20 | Scenario: still failing 21 | Given broken 22 | 23 | Scenario: always passing 24 | Given passing 25 | """ 26 | And a file named "features/step_definitions/rerun_steps.rb" with: 27 | """ 28 | Given('fixed now') do 29 | $stdout.puts 'All fixed now' 30 | end 31 | 32 | Given('broken') do 33 | raise "I'm broken" 34 | end 35 | 36 | Given('passing') do 37 | $stdout.puts "I've always been passing" 38 | end 39 | """ 40 | When I run `bundle exec cucumber -p rerun` 41 | Then it should fail with: 42 | """ 43 | 2 scenarios (1 failed, 1 passed) 44 | 2 steps (1 failed, 1 passed) 45 | """ 46 | And the file "rerun.txt" should not contain "features/rerun_test.feature:2" 47 | And the file "rerun.txt" should contain "features/rerun_test.feature:5" 48 | -------------------------------------------------------------------------------- /features/rest_api.feature: -------------------------------------------------------------------------------- 1 | Feature: REST API 2 | 3 | Scenario: Compare JSON 4 | Given I have created a new Rails app and installed cucumber-rails 5 | When I write to "app/controllers/posts_controller.rb" with: 6 | """ 7 | class PostsController < ApplicationController 8 | def index 9 | render json: {'hello' => 'world'}.to_json 10 | end 11 | end 12 | """ 13 | And I write to "config/routes.rb" with: 14 | """ 15 | TestApp::Application.routes.draw do 16 | resources :posts 17 | end 18 | """ 19 | And I write to "features/posts.feature" with: 20 | """ 21 | Feature: posts 22 | Scenario: See them 23 | When the client requests GET /posts 24 | Then the response should be JSON: 25 | \"\"\" 26 | { 27 | "hello": "world" 28 | } 29 | \"\"\" 30 | """ 31 | And I write to "features/step_definitions/post_steps.rb" with: 32 | """ 33 | When('the client requests GET \/{word}') do |path| 34 | get(path) 35 | end 36 | 37 | Then('the response should be JSON:') do |json| 38 | expect(JSON.parse(last_response.body)).to eq(JSON.parse(json)) 39 | end 40 | """ 41 | And I run `bundle exec rake db:migrate` 42 | And I run `bundle exec rake cucumber` 43 | Then the feature should pass with: 44 | """ 45 | 1 scenario (1 passed) 46 | 2 steps (2 passed) 47 | """ 48 | -------------------------------------------------------------------------------- /features/step_definitions/cucumber_rails_steps.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Given('I have created a new Rails app and installed cucumber-rails, accidentally outside of the test group in my Gemfile') do 4 | rails_new 5 | install_cucumber_rails :not_in_test_group 6 | end 7 | 8 | Given('I have created a new Rails app and installed cucumber-rails') do 9 | rails_new 10 | install_cucumber_rails 11 | end 12 | 13 | Given('I have created a new Rails app and installed cucumber-rails with database_cleaner-active_record') do 14 | rails_new 15 | install_cucumber_rails :no_database_cleaner, :database_cleaner_active_record 16 | end 17 | 18 | Given('I have created a new Rails app and installed cucumber-rails without database_cleaner') do 19 | rails_new 20 | install_cucumber_rails :no_database_cleaner 21 | overwrite_file('features/support/env.rb', "require 'cucumber/rails'\n") 22 | end 23 | 24 | Given('I have created a new Rails app with no database and installed cucumber-rails') do 25 | rails_new args: '--skip-active-record' 26 | install_cucumber_rails :no_database_cleaner, :no_factory_bot 27 | overwrite_file('features/support/env.rb', "require 'cucumber/rails'\n") 28 | end 29 | 30 | Given('I have a {string} ActiveRecord model object') do |name| 31 | run_command_and_stop("bundle exec rails g model #{name}") 32 | run_command_and_stop('bundle exec rake db:migrate RAILS_ENV=test') 33 | end 34 | 35 | Given('I remove the {string} gem from the Gemfile') do |gem_name| 36 | content = File.open(expand_path('Gemfile'), 'r').readlines 37 | new_content = [] 38 | 39 | content.each do |line| 40 | next if /gem ["|']#{gem_name}["|'].*/.match?(line) 41 | 42 | new_content << line 43 | end 44 | 45 | overwrite_file('Gemfile', new_content.join("\r\n")) 46 | end 47 | 48 | Given('I force selenium to run Firefox in headless mode') do 49 | selenium_config = %{ 50 | Capybara.register_driver :selenium do |app| 51 | http_client = Selenium::WebDriver::Remote::Http::Default.new 52 | http_client.read_timeout = 180 53 | 54 | browser_options = Selenium::WebDriver::Firefox::Options.new 55 | browser_options.args << '--headless' 56 | Capybara::Selenium::Driver.new( 57 | app, 58 | browser: :firefox, 59 | options: browser_options, 60 | http_client: http_client 61 | ) 62 | end 63 | 64 | Capybara.server = :webrick 65 | } 66 | append_to_file('features/support/env.rb', selenium_config) 67 | append_to_file('Gemfile', "gem 'webrick', group: :test\n") 68 | run_command_and_stop('bundle install --jobs 4') 69 | end 70 | 71 | Given('I force {string} to use select boxes for dates') do |file| 72 | content = File.read(expand_path(file)) 73 | 74 | overwrite_file(file, content.gsub(/\.(datetime|time|date)_field/, '.\1_select')) 75 | end 76 | -------------------------------------------------------------------------------- /features/support/cucumber_rails_gem_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails' 4 | require 'cucumber' 5 | require 'capybara' 6 | 7 | module CucumberRailsGemHelper 8 | def install_cucumber_rails(*options) 9 | configure_rails_gems 10 | add_cucumber_rails(options) 11 | add_sqlite3_gem 12 | add_selenium_webdriver_gem 13 | add_remaining_gems(options) 14 | bundle_install 15 | run_command_and_stop 'bundle exec rails generate cucumber:install' 16 | end 17 | 18 | private 19 | 20 | def configure_rails_gems 21 | %w[bootsnap byebug jbuilder listen rails sass-rails turbolinks webpacker].each { |gem| remove_gem(gem) } 22 | %w[railties activerecord actionpack].each { |rails_gem| add_gem(rails_gem, Rails.version) } 23 | add_gem 'concurrent-ruby', '< 1.3.5' unless rails_equal_or_higher_than?('7.1') 24 | end 25 | 26 | def add_cucumber_rails(options) 27 | if options.include?(:not_in_test_group) 28 | add_gem 'cucumber-rails', path: File.expand_path('.').to_s 29 | else 30 | add_gem 'cucumber-rails', group: :test, require: false, path: File.expand_path('.').to_s 31 | end 32 | end 33 | 34 | def add_sqlite3_gem 35 | if rails_equal_or_higher_than?('7.1') 36 | add_gem 'sqlite3', '~> 2.0' 37 | elsif rails_equal_or_higher_than?('6.0') 38 | add_gem 'sqlite3', '~> 1.4' 39 | else 40 | add_gem 'sqlite3', '~> 1.3.13' 41 | end 42 | end 43 | 44 | def add_selenium_webdriver_gem 45 | if rails_equal_or_higher_than?('7.0') 46 | add_gem 'selenium-webdriver', '~> 4.22', group: :test 47 | elsif rails_equal_or_higher_than?('6.0') 48 | add_gem 'selenium-webdriver', '~> 4.0', group: :test 49 | add_gem 'webdrivers', '~> 5.0', group: :test 50 | else 51 | add_gem 'selenium-webdriver', '< 4', group: :test 52 | add_gem 'webdrivers', '~> 4.0', group: :test 53 | remove_gem 'chromedriver-helper' 54 | end 55 | end 56 | 57 | def add_remaining_gems(options) 58 | add_gem 'cucumber', Cucumber::VERSION, group: :test 59 | add_gem 'capybara', Capybara::VERSION, group: :test 60 | add_gem 'database_cleaner', '>= 2.0.0', group: :test unless options.include?(:no_database_cleaner) 61 | add_gem 'database_cleaner-active_record', '>= 2.0.0', group: :test if options.include?(:database_cleaner_active_record) 62 | if rails_equal_or_higher_than?('6.0') 63 | add_gem 'factory_bot', '>= 6.4', group: :test unless options.include?(:no_factory_bot) 64 | else 65 | add_gem 'factory_bot', '< 6.4', group: :test unless options.include?(:no_factory_bot) 66 | end 67 | add_gem 'rspec-expectations', '~> 3.12', group: :test 68 | end 69 | 70 | def bundle_install 71 | run_command_and_stop 'bundle config set --local without "development"' 72 | run_command_and_stop "bundle config set --local path '#{ENV.fetch('GITHUB_WORKSPACE')}/vendor/bundle'" if ENV.key?('GITHUB_WORKSPACE') 73 | run_command_and_stop 'bundle install --jobs 4' 74 | end 75 | 76 | def convert_gem_opts_to_string(name, *args) 77 | options = args.last.is_a?(Hash) ? args.pop : {} 78 | parts = ["'#{name}'"] 79 | parts << args.map(&:inspect) if args.any? 80 | parts << options.inspect[1..-2] if options.any? 81 | "gem #{parts.flatten.join(', ')}\n" 82 | end 83 | 84 | def remove_gem(name) 85 | content = File.read(expand_path('Gemfile')).gsub(/^\s*gem ["']#{name}["'].*$/, '') 86 | overwrite_file('Gemfile', content) 87 | end 88 | 89 | def add_gem(name, *args) 90 | line = convert_gem_opts_to_string(name, *args) 91 | gem_regexp = /gem ["']#{name}["'].*$/ 92 | gemfile_content = File.read(expand_path('Gemfile')) 93 | 94 | if gemfile_content.match?(gem_regexp) 95 | updated_gemfile_content = gemfile_content.gsub(gem_regexp, line) 96 | overwrite_file('Gemfile', updated_gemfile_content) 97 | else 98 | append_to_file('Gemfile', line) 99 | end 100 | end 101 | 102 | def rails_equal_or_higher_than?(version) 103 | Rails.gem_version >= Gem::Version.new(version) 104 | end 105 | end 106 | 107 | World(CucumberRailsGemHelper) 108 | -------------------------------------------------------------------------------- /features/support/cucumber_rails_setup_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails' 4 | require 'cucumber' 5 | require 'capybara' 6 | 7 | module CucumberRailsSetupHelper 8 | def rails_new(options = {}) 9 | # This expectation allows us to wait until the command line monitor has output a README file (i.e. the command has completed) 10 | expect(run_rails_new_command(options)).to have_output(/README/) 11 | 12 | cd 'test_app' 13 | configure_rails_requires 14 | configure_rails_layout 15 | clear_bundle_env_vars 16 | end 17 | 18 | private 19 | 20 | def run_rails_new_command(options) 21 | flags = %w[--skip-action-cable --skip-action-mailer --skip-active-job --skip-bootsnap --skip-bundle --skip-javascript 22 | --skip-jbuilder --skip-listen --skip-spring --skip-sprockets --skip-test-unit --skip-turbolinks --skip-active-storage] 23 | flags += %w[--skip-action-mailbox --skip-action-text] if rails_equal_or_higher_than?('6.0') 24 | run_command "bundle exec rails new test_app #{flags.join(' ')} #{options[:args]}" 25 | end 26 | 27 | def configure_rails_requires 28 | content = File.read(expand_path('config/application.rb')) 29 | %w[active_job/railtie active_storage/engine action_mailer/railtie action_mailbox/engine 30 | action_text/engine action_cable/engine rails/test_unit/railtie sprockets/railtie].each do |require| 31 | content = content.gsub(/^.*require ["']#{require}["']\s*$/, '') 32 | end 33 | overwrite_file('config/application.rb', content) 34 | end 35 | 36 | def configure_rails_layout 37 | file = 'app/views/layouts/application.html.erb' 38 | content = File.read(expand_path(file)).gsub(/^\s*<%= stylesheet_link_tag .*%>\s*$/, '') 39 | overwrite_file(file, content) 40 | end 41 | 42 | def clear_bundle_env_vars 43 | unset_bundler_env_vars 44 | delete_environment_variable 'BUNDLE_GEMFILE' 45 | end 46 | 47 | def rails_equal_or_higher_than?(version) 48 | Rails.gem_version >= Gem::Version.new(version) 49 | end 50 | end 51 | 52 | World(CucumberRailsSetupHelper) 53 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../../lib") 4 | 5 | require 'rspec/expectations' 6 | require 'aruba/cucumber' 7 | 8 | Aruba.configure do |config| 9 | config.exit_timeout = 360 10 | end 11 | -------------------------------------------------------------------------------- /features/support/hooks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | After do |scenario| 4 | if scenario.failed? 5 | log last_command_stopped.stdout 6 | log last_command_stopped.stderr 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /gemfiles/rails_6_1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord" 6 | gem "capybara", "< 3.38" 7 | gem "concurrent-ruby", "< 1.3.5" 8 | gem "factory_bot", "< 6.4" 9 | gem "psych", "< 4" 10 | gem "railties", "~> 6.1.7" 11 | gem "sqlite3", "< 2" 12 | 13 | gemspec path: "../" 14 | -------------------------------------------------------------------------------- /gemfiles/rails_7_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord" 6 | gem "concurrent-ruby", "< 1.3.5" 7 | gem "cucumber", "< 10" 8 | gem "factory_bot", "< 6.6" 9 | gem "railties", "~> 7.0.8" 10 | gem "sqlite3", "< 2" 11 | 12 | gemspec path: "../" 13 | -------------------------------------------------------------------------------- /gemfiles/rails_7_1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord" 6 | gem "railties", "~> 7.1.5" 7 | gem "sqlite3", "~> 2.0" 8 | 9 | gemspec path: "../" 10 | -------------------------------------------------------------------------------- /gemfiles/rails_7_2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord" 6 | gem "railties", "~> 7.2.2" 7 | gem "sqlite3", "~> 2.2" 8 | 9 | gemspec path: "../" 10 | -------------------------------------------------------------------------------- /gemfiles/rails_8_0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord" 6 | gem "railties", "~> 8.0.0" 7 | gem "sqlite3", "~> 2.2" 8 | 9 | gemspec path: "../" 10 | -------------------------------------------------------------------------------- /lib/cucumber/rails.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | called_from_env_rb = caller.detect { |f| f.include? '/env.rb:' } 4 | 5 | if called_from_env_rb 6 | env_caller = File.dirname(called_from_env_rb) 7 | 8 | require 'rails' 9 | require 'cucumber/rails/application' 10 | ENV['RAILS_ENV'] ||= 'test' 11 | ENV['RAILS_ROOT'] ||= File.expand_path("#{env_caller}/../..") 12 | require File.expand_path("#{ENV.fetch('RAILS_ROOT')}/config/environment") 13 | require 'cucumber/rails/action_dispatch' 14 | require 'rails/test_help' 15 | 16 | unless Rails.application.config.cache_classes || defined?(Spring) 17 | warn <<~MESSAGE 18 | WARNING: You have set Rails' config.cache_classes to false (Spring needs cache_classes set to false). 19 | This is known to cause problems with database transactions. 20 | 21 | Set config.cache_classes to true if you want to use transactions. 22 | MESSAGE 23 | end 24 | 25 | require 'cucumber/rails/world' 26 | require 'cucumber/rails/hooks' 27 | require 'cucumber/rails/capybara' 28 | require 'cucumber/rails/database/strategy' 29 | require 'cucumber/rails/database/deletion_strategy' 30 | require 'cucumber/rails/database/null_strategy' 31 | require 'cucumber/rails/database/shared_connection_strategy' 32 | require 'cucumber/rails/database/truncation_strategy' 33 | require 'cucumber/rails/database' 34 | 35 | MultiTest.disable_autorun 36 | else 37 | warn <<~MESSAGE 38 | WARNING: Cucumber-rails has been required outside of env.rb. The rest of loading is being deferred until env.rb is called. 39 | 40 | To avoid this warning, move `gem 'cucumber-rails', require: false` under `group :test` in your Gemfile. 41 | If it is already in the `:test` group, be sure you are specifying 'require: false'. 42 | MESSAGE 43 | end 44 | -------------------------------------------------------------------------------- /lib/cucumber/rails/action_dispatch.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActionController::Base.class_eval do 4 | cattr_accessor :allow_rescue 5 | end 6 | 7 | module Cucumber 8 | module Rails 9 | module ActionDispatch 10 | module ShowExceptions 11 | def call(env) 12 | env['action_dispatch.show_detailed_exceptions'] = !ActionController::Base.allow_rescue 13 | 14 | show_exceptions = !env['action_dispatch.show_detailed_exceptions'] 15 | if ::Rails.gem_version >= Gem::Version.new('7.1.0') 16 | # Rails 7.1 deprecated `show_exceptions` boolean in in favor of symbols 17 | show_exceptions = show_exceptions ? :all : :none 18 | end 19 | 20 | env['action_dispatch.show_exceptions'] = show_exceptions 21 | super(env) 22 | end 23 | end 24 | end 25 | end 26 | end 27 | 28 | ActionDispatch::ShowExceptions.prepend(Cucumber::Rails::ActionDispatch::ShowExceptions) 29 | -------------------------------------------------------------------------------- /lib/cucumber/rails/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails/application' 4 | 5 | # Make sure the ActionDispatch::ShowExceptions middleware is always enabled, 6 | # regardless of what is in config/environments/test.rb 7 | # Instead we are overriding ActionDispatch::ShowExceptions to be able to 8 | # toggle whether or not exceptions are raised. 9 | 10 | module Cucumber 11 | module Rails 12 | module Application 13 | def initialize! 14 | ad = config.action_dispatch 15 | 16 | def ad.show_exceptions 17 | true 18 | end 19 | 20 | super 21 | end 22 | end 23 | end 24 | end 25 | 26 | Rails::Application.prepend(Cucumber::Rails::Application) 27 | -------------------------------------------------------------------------------- /lib/cucumber/rails/capybara.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'capybara' 4 | require 'capybara/rails' 5 | require 'capybara/cucumber' 6 | require 'capybara/session' 7 | require 'cucumber/rails/capybara/javascript_emulation' 8 | require 'cucumber/rails/capybara/select_dates_and_times' 9 | -------------------------------------------------------------------------------- /lib/cucumber/rails/capybara/javascript_emulation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cucumber 4 | module Rails 5 | module Capybara 6 | module JavascriptEmulation 7 | def self.included(base) 8 | base.class_eval do 9 | alias_method :click_without_javascript_emulation, :click 10 | alias_method :click, :click_with_javascript_emulation 11 | end 12 | end 13 | 14 | def click_with_javascript_emulation(*) 15 | if link_with_non_get_http_method? 16 | ::Capybara::RackTest::Form.new( 17 | driver, js_form(element_node.document, self[:href], emulated_method) 18 | ).submit(self) 19 | else 20 | click_without_javascript_emulation 21 | end 22 | end 23 | 24 | private 25 | 26 | def csrf? 27 | csrf_param_node && csrf_token_node 28 | end 29 | 30 | def csrf_param_node 31 | element_node.document.at_xpath("//meta[@name='csrf-param']") 32 | end 33 | 34 | def csrf_param 35 | csrf_param_node['content'] 36 | end 37 | 38 | def csrf_token_node 39 | element_node.document.at_xpath("//meta[@name='csrf-token']") 40 | end 41 | 42 | def csrf_token 43 | csrf_token_node['content'] 44 | end 45 | 46 | def js_form(document, action, emulated_method, method = 'POST') 47 | js_form = document.create_element('form') 48 | js_form['action'] = action 49 | js_form['method'] = method 50 | 51 | add_hidden_method_input(document, js_form) unless same?(emulated_method, method) 52 | 53 | # rails will wipe the session if the CSRF token is not sent with non-GET requests 54 | add_hidden_csrf_input(document, js_form) if csrf? && !get?(emulated_method) 55 | 56 | js_form 57 | end 58 | 59 | def same?(emulated_method, method) 60 | emulated_method.casecmp(method).zero? 61 | end 62 | 63 | def add_hidden_method_input(document, js_form) 64 | input = document.create_element('input') 65 | input['type'] = 'hidden' 66 | input['name'] = '_method' 67 | input['value'] = emulated_method 68 | js_form.add_child(input) 69 | end 70 | 71 | def get?(emulated_method) 72 | same?(emulated_method, 'get') 73 | end 74 | 75 | def add_hidden_csrf_input(document, js_form) 76 | input = document.create_element('input') 77 | input['type'] = 'hidden' 78 | input['name'] = csrf_param 79 | input['value'] = csrf_token 80 | js_form.add_child(input) 81 | end 82 | 83 | def link_with_non_get_http_method? 84 | tag_name == 'a' && 85 | element_node['data-method'] && 86 | element_node['data-method'] =~ /(?:delete|put|post)/ 87 | end 88 | 89 | def emulated_method 90 | element_node['data-method'] 91 | end 92 | 93 | def element_node 94 | native 95 | end 96 | end 97 | end 98 | end 99 | end 100 | 101 | module Capybara 102 | module RackTest 103 | class Node 104 | include ::Cucumber::Rails::Capybara::JavascriptEmulation 105 | end 106 | end 107 | end 108 | 109 | Before('not @no-js-emulation') do 110 | # Enable javascript emulation 111 | Capybara::RackTest::Node.class_eval do 112 | alias_method :click, :click_with_javascript_emulation 113 | end 114 | end 115 | 116 | Before('@no-js-emulation') do 117 | # Disable javascript emulation 118 | Capybara::RackTest::Node.class_eval do 119 | alias_method :click, :click_without_javascript_emulation 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /lib/cucumber/rails/capybara/select_dates_and_times.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cucumber 4 | module Rails 5 | module Capybara 6 | # This module defines methods for selecting dates and times 7 | module SelectDatesAndTimes 8 | # Select a Rails date. Options hash must include from: +label+ 9 | def select_date(date, options) 10 | date = Date.parse(date) 11 | base_dom_id = get_base_dom_from_options(options) 12 | 13 | # Rails 7 use HTML5 input type="date" by default. If input is not present fallback to plain select boxes alternative. 14 | # It's safe to use has_css? without waiting/retry. We already know field's label is visible 15 | if html5_input_field_present?(base_dom_id) 16 | fill_in options[:from], with: date 17 | else 18 | find(:xpath, ".//select[@id='#{base_dom_id}_1i']").select(date.year.to_s) 19 | find(:xpath, ".//select[@id='#{base_dom_id}_2i']").select(I18n.l(date, format: '%B')) 20 | find(:xpath, ".//select[@id='#{base_dom_id}_3i']").select(date.day.to_s) 21 | end 22 | end 23 | 24 | # Select a Rails time. Options hash must include from: +label+ 25 | def select_time(time, options) 26 | time = Time.zone.parse(time) 27 | base_dom_id = get_base_dom_from_options(options) 28 | 29 | # Rails 7 use HTML5 input type="time" by default. If input is not present fallback to plain select boxes alternative. 30 | # It's safe to use has_css? without waiting/retry. We already know field's label is visible 31 | if html5_input_field_present?(base_dom_id) 32 | fill_in options[:from], with: time 33 | else 34 | find(:xpath, ".//select[@id='#{base_dom_id}_4i']").select(time.hour.to_s.rjust(2, '0')) 35 | find(:xpath, ".//select[@id='#{base_dom_id}_5i']").select(time.min.to_s.rjust(2, '0')) 36 | end 37 | end 38 | 39 | # Select a Rails datetime. Options hash must include from: +label+ 40 | def select_datetime(datetime, options) 41 | base_dom_id = get_base_dom_id_from_label_tag(options[:from]) 42 | 43 | # Rails 7 use HTML5 input type="datetime-local" by default. If input is not present fallback to plain select boxes alternative. 44 | # It's safe to use has_css? without waiting/retry. We already know field's label is visible 45 | if html5_input_field_present?(base_dom_id) 46 | fill_in options[:from], with: DateTime.parse(datetime) 47 | else 48 | extended_options = options.merge(base_dom_id:) 49 | select_date(datetime, extended_options) 50 | select_time(datetime, extended_options) 51 | end 52 | end 53 | 54 | private 55 | 56 | def html5_input_field_present?(base_dom_id) 57 | ::Rails::VERSION::MAJOR >= 7 && page.has_css?("##{base_dom_id}", wait: 0) 58 | end 59 | 60 | def get_base_dom_from_options(options) 61 | options[:base_dom_id] || get_base_dom_id_from_label_tag(options[:from]) 62 | end 63 | 64 | # @example "event_starts_at_" 65 | def get_base_dom_id_from_label_tag(field) 66 | find(:xpath, ".//label[contains(., '#{field}')]")['for'].gsub(/(_[1-5]i)$/, '') 67 | end 68 | end 69 | end 70 | end 71 | end 72 | 73 | World(Cucumber::Rails::Capybara::SelectDatesAndTimes) 74 | -------------------------------------------------------------------------------- /lib/cucumber/rails/database.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'database/strategy' 4 | require_relative 'database/deletion_strategy' 5 | require_relative 'database/null_strategy' 6 | require_relative 'database/shared_connection_strategy' 7 | require_relative 'database/truncation_strategy' 8 | 9 | module Cucumber 10 | module Rails 11 | module Database 12 | CUSTOM_STRATEGY_INTERFACE = %w[before_js before_non_js].freeze 13 | 14 | class InvalidStrategy < ArgumentError; end 15 | 16 | class << self 17 | attr_accessor :autorun_database_cleaner 18 | 19 | def javascript_strategy=(args) 20 | strategy, *strategy_opts = args 21 | strategy_type = 22 | case strategy 23 | when Symbol 24 | map[strategy] || throw_invalid_strategy_error(strategy) 25 | when Class 26 | strategy 27 | end 28 | 29 | @strategy = strategy_type.new(*strategy_opts) 30 | 31 | validate_interface! 32 | end 33 | 34 | def default_strategy! 35 | self.javascript_strategy = :truncation 36 | self.autorun_database_cleaner = true 37 | end 38 | 39 | def before_js 40 | @strategy.before_js 41 | end 42 | 43 | def before_non_js 44 | @strategy.before_non_js 45 | end 46 | 47 | def after 48 | @strategy.after 49 | end 50 | 51 | private 52 | 53 | def map 54 | { 55 | truncation: TruncationStrategy, 56 | shared_connection: SharedConnectionStrategy, 57 | transaction: SharedConnectionStrategy, 58 | deletion: DeletionStrategy, 59 | none: NullStrategy 60 | } 61 | end 62 | 63 | def throw_invalid_strategy_error(strategy) 64 | raise(InvalidStrategy, "The strategy '#{strategy}' is not understood. Please use one of #{mapped_keys}") 65 | end 66 | 67 | def mapped_keys 68 | map.keys.join(', ') 69 | end 70 | 71 | def validate_interface! 72 | return if CUSTOM_STRATEGY_INTERFACE.all? { |m| @strategy.respond_to?(m) } 73 | 74 | throw_invalid_strategy_interface_error 75 | end 76 | 77 | def throw_invalid_strategy_interface_error 78 | raise( 79 | ArgumentError, 80 | "Strategy must respond to all of: #{CUSTOM_STRATEGY_INTERFACE.map { |method| "##{method}" } * ' '} !" 81 | ) 82 | end 83 | end 84 | 85 | default_strategy! 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/cucumber/rails/database/deletion_strategy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cucumber 4 | module Rails 5 | module Database 6 | class DeletionStrategy < Strategy 7 | def before_js 8 | super :deletion 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/cucumber/rails/database/null_strategy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cucumber 4 | module Rails 5 | module Database 6 | class NullStrategy 7 | def before_js; end 8 | 9 | def before_non_js; end 10 | 11 | def after; end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/cucumber/rails/database/shared_connection_strategy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cucumber 4 | module Rails 5 | module Database 6 | class SharedConnectionStrategy < Strategy 7 | def before_js 8 | # Forces all threads to share a connection on a per-model basis, 9 | # as connections may vary per model as per establish_connection. This works 10 | # on Capybara because it starts the web server in a thread. 11 | ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection 12 | ActiveRecord::Base.descendants.each do |model| 13 | model.shared_connection = model.connection 14 | end 15 | end 16 | 17 | def before_non_js 18 | # Do not use a shared connection unless we're in a @javascript scenario 19 | ActiveRecord::Base.shared_connection = nil 20 | ActiveRecord::Base.descendants.each do |model| 21 | model.shared_connection = nil 22 | end 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/cucumber/rails/database/strategy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cucumber 4 | module Rails 5 | module Database 6 | class Strategy 7 | def initialize(options = {}) 8 | @options = options 9 | end 10 | 11 | def before_js(strategy) 12 | @original_strategy = if defined?(DatabaseCleaner::VERSION) && Gem::Version.new(DatabaseCleaner::VERSION) >= Gem::Version.new('1.8.0.beta') 13 | raise "No DatabaseCleaner strategies found. Make sure you have required one of DatabaseCleaner's adapters" if DatabaseCleaner.cleaners.empty? 14 | 15 | DatabaseCleaner.cleaners.values.first.strategy # that feels like a nasty hack 16 | else 17 | DatabaseCleaner.connections.first.strategy # that feels like a nasty hack 18 | end 19 | DatabaseCleaner.strategy = strategy, @options 20 | end 21 | 22 | def before_non_js 23 | # no-op 24 | end 25 | 26 | def after 27 | return unless @original_strategy 28 | 29 | DatabaseCleaner.strategy = @original_strategy 30 | @original_strategy = nil 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/cucumber/rails/database/truncation_strategy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Cucumber 4 | module Rails 5 | module Database 6 | class TruncationStrategy < Strategy 7 | def before_js 8 | super :truncation 9 | end 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/cucumber/rails/hooks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cucumber/rails/hooks/active_record' 4 | require 'cucumber/rails/hooks/database_cleaner' 5 | require 'cucumber/rails/hooks/allow_rescue' 6 | require 'cucumber/rails/hooks/mail' 7 | -------------------------------------------------------------------------------- /lib/cucumber/rails/hooks/active_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if defined?(ActiveRecord::Base) 4 | module ActiveRecord 5 | class Base 6 | class_attribute :shared_connection 7 | 8 | def self.connection 9 | shared_connection || retrieve_connection 10 | end 11 | end 12 | end 13 | 14 | Before('@javascript') do 15 | Cucumber::Rails::Database.before_js if Cucumber::Rails::Database.autorun_database_cleaner 16 | end 17 | 18 | Before('not @javascript') do 19 | Cucumber::Rails::Database.before_non_js if Cucumber::Rails::Database.autorun_database_cleaner 20 | end 21 | 22 | After do 23 | Cucumber::Rails::Database.after if Cucumber::Rails::Database.autorun_database_cleaner 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/cucumber/rails/hooks/allow_rescue.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Before('@allow-rescue') do 4 | @__orig_allow_rescue = ActionController::Base.allow_rescue 5 | ActionController::Base.allow_rescue = true 6 | end 7 | 8 | After('@allow-rescue') do 9 | ActionController::Base.allow_rescue = @__orig_allow_rescue 10 | end 11 | -------------------------------------------------------------------------------- /lib/cucumber/rails/hooks/database_cleaner.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | begin 4 | require 'database_cleaner/core' 5 | rescue LoadError 6 | begin 7 | require 'database_cleaner' 8 | rescue LoadError 9 | Cucumber.logger.debug('neither database_cleaner v1 or v2 present') 10 | end 11 | end 12 | 13 | if defined?(DatabaseCleaner) 14 | Before('not @no-database-cleaner') do 15 | DatabaseCleaner.start if Cucumber::Rails::Database.autorun_database_cleaner 16 | end 17 | 18 | After('not @no-database-cleaner') do 19 | DatabaseCleaner.clean if Cucumber::Rails::Database.autorun_database_cleaner 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/cucumber/rails/hooks/mail.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if defined?(ActionMailer) 4 | Before do 5 | ActionMailer::Base.deliveries = [] 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/cucumber/rails/rspec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cucumber/rails/world' 4 | 5 | begin 6 | require 'rspec/rails/matchers' 7 | 8 | [Cucumber::Rails::World, ActionDispatch::Integration::Session].each do |klass| 9 | klass.class_eval do 10 | include RSpec::Matchers 11 | end 12 | end 13 | rescue LoadError 14 | require 'spec/expectations' 15 | require 'spec/rails' 16 | 17 | [Cucumber::Rails::World, ActionDispatch::Integration::Session].each do |klass| 18 | klass.class_eval do 19 | include Spec::Matchers 20 | include Spec::Rails::Matchers if defined?(Spec::Rails::Matchers) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/cucumber/rails/world.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | begin 4 | # Try to load it so we can assign @_result below if needed. 5 | require 'test/unit/testresult' 6 | rescue LoadError 7 | Cucumber.logger.debug('Minitest not found.') 8 | end 9 | 10 | module Cucumber 11 | module Rails 12 | class << self 13 | def include_rack_test_helpers? 14 | # Using ActiveModel Boolean casting here will give false positives more often than not! 15 | !ENV.fetch('CR_REMOVE_RACK_TEST_HELPERS', '').casecmp('true').zero? 16 | end 17 | end 18 | end 19 | end 20 | 21 | module Cucumber 22 | module Rails 23 | class World < ::ActionDispatch::IntegrationTest 24 | include Rack::Test::Methods if Cucumber::Rails.include_rack_test_helpers? 25 | include ActiveSupport::Testing::SetupAndTeardown if ActiveSupport::Testing.const_defined?(:SetupAndTeardown) 26 | 27 | def initialize 28 | super('MiniTest run-name if needed') 29 | end 30 | 31 | unless defined?(ActiveRecord::Base) 32 | # Workaround for projects that don't use ActiveRecord 33 | def self.fixture_table_names 34 | [] 35 | end 36 | end 37 | end 38 | end 39 | end 40 | 41 | World do 42 | Cucumber::Rails::World.new 43 | end 44 | -------------------------------------------------------------------------------- /lib/generators/cucumber/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Sets up Cucumber in your Rails project. After running this generator you will 3 | get a new rake task called cucumber. 4 | 5 | This also generates the necessary files in the features directory. 6 | 7 | Examples: 8 | `rails generate cucumber:install` 9 | 10 | `rails generate cucumber:install --help` 11 | -------------------------------------------------------------------------------- /lib/generators/cucumber/install_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rbconfig' 4 | 5 | module Cucumber 6 | class InstallGenerator < ::Rails::Generators::Base 7 | source_root File.expand_path('templates', __dir__) 8 | 9 | DEFAULT_SHEBANG = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) 10 | 11 | class_option :skip_database, 12 | type: :boolean, 13 | desc: 'Skip modification of database.yml', 14 | aliases: '-D', 15 | default: false 16 | 17 | def create_templates 18 | template 'config/cucumber.yml.erb', 'config/cucumber.yml' 19 | end 20 | 21 | def create_scripts 22 | copy_file 'bin/cucumber', 'bin/cucumber' 23 | chmod 'bin/cucumber', 0755 24 | end 25 | 26 | def create_step_definitions 27 | empty_directory 'features/step_definitions' 28 | create_file 'features/step_definitions/.keep' 29 | end 30 | 31 | def create_feature_support 32 | empty_directory 'features/support' 33 | template 'support/env.rb.erb', 'features/support/env.rb' 34 | end 35 | 36 | def configure_environment 37 | environment(<<~CONFIG, env: %w[development test]) if ::Rails::VERSION::MAJOR >= 6 38 | # Configure 'rails notes' to inspect Cucumber files 39 | config.annotations.register_directories('features') 40 | config.annotations.register_extensions('feature') { |tag| /#\\s*(\#{tag}):?\\s*(.*)$/ } 41 | 42 | CONFIG 43 | end 44 | 45 | def create_tasks 46 | empty_directory 'lib/tasks' 47 | template 'tasks/cucumber.rake.erb', 'lib/tasks/cucumber.rake' 48 | end 49 | 50 | def create_database 51 | return unless File.exist?('config/database.yml') 52 | return unless File.read('config/database.yml').include? 'cucumber:' 53 | 54 | gsub_file 'config/database.yml', /^test:.*\n/, "test: &test\n" 55 | gsub_file 'config/database.yml', /\z/, "\ncucumber:\n <<: *test\n" 56 | end 57 | 58 | protected 59 | 60 | def embed_file(source, indent = '') 61 | File.read(File.join(self.class.source_root, source)).gsub(/^/, indent) 62 | end 63 | 64 | def embed_template(source, indent = '') 65 | template = File.join(self.class.source_root, source) 66 | ERB.new(File.read(template), trim_mode: '-').result(binding).gsub(/^/, indent) 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/generators/cucumber/templates/bin/cucumber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 5 | if vendored_cucumber_bin 6 | load File.expand_path(vendored_cucumber_bin) 7 | else 8 | require 'rubygems' unless ENV['NO_RUBYGEMS'] 9 | require 'cucumber' 10 | load Cucumber::BINARY 11 | end 12 | -------------------------------------------------------------------------------- /lib/generators/cucumber/templates/config/cucumber.yml.erb: -------------------------------------------------------------------------------- 1 | <%% 2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" 3 | rerun = rerun.strip.gsub /\s/, ' ' 4 | rerun_opts = rerun.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" 5 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags 'not @wip'" 6 | %> 7 | default: <%%= std_opts %> features 8 | rerun: <%%= rerun_opts %> --format rerun --out rerun.txt --strict --tags 'not @wip' 9 | -------------------------------------------------------------------------------- /lib/generators/cucumber/templates/support/edit_warning.txt: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | -------------------------------------------------------------------------------- /lib/generators/cucumber/templates/support/env.rb.erb: -------------------------------------------------------------------------------- 1 | <%= embed_file('support/edit_warning.txt') %> 2 | 3 | require 'cucumber/rails' 4 | 5 | # By default, any exception happening in your Rails application will bubble up 6 | # to Cucumber so that your scenario will fail. This is a different from how 7 | # your application behaves in the production environment, where an error page will 8 | # be rendered instead. 9 | # 10 | # Sometimes we want to override this default behaviour and allow Rails to rescue 11 | # exceptions and display an error page (just like when the app is running in production). 12 | # Typical scenarios where you want to do this is when you test your error pages. 13 | # There are two ways to allow Rails to rescue exceptions: 14 | # 15 | # 1) Tag your scenario (or feature) with @allow-rescue 16 | # 17 | # 2) Set the value below to true. Beware that doing this globally is not 18 | # recommended as it will mask a lot of errors for you! 19 | # 20 | ActionController::Base.allow_rescue = false 21 | 22 | # Remove/comment out the lines below if your app doesn't have a database. 23 | # For some databases (like MongoDB and CouchDB) you may need to use :truncation instead. 24 | begin 25 | DatabaseCleaner.strategy = :transaction 26 | rescue NameError 27 | raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it." 28 | end 29 | 30 | # You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios. 31 | # See the DatabaseCleaner documentation for details. Example: 32 | # 33 | # Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do 34 | # # { except: [:widgets] } may not do what you expect here 35 | # # as Cucumber::Rails::Database.javascript_strategy overrides 36 | # # this setting. 37 | # DatabaseCleaner.strategy = :truncation 38 | # end 39 | # 40 | # Before('not @no-txn', 'not @selenium', 'not @culerity', 'not @celerity', 'not @javascript') do 41 | # DatabaseCleaner.strategy = :transaction 42 | # end 43 | # 44 | 45 | # Possible values are :truncation and :transaction 46 | # The :transaction strategy is faster, but might give you threading problems. 47 | # See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature 48 | Cucumber::Rails::Database.javascript_strategy = :truncation 49 | -------------------------------------------------------------------------------- /lib/generators/cucumber/templates/tasks/cucumber.rake.erb: -------------------------------------------------------------------------------- 1 | <%= embed_file('support/edit_warning.txt') %> 2 | 3 | unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks 4 | 5 | vendored_cucumber_bin = Dir["#{<%= defined?(Rails.root) ? 'Rails.root' : 'RAILS_ROOT' %>}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 6 | $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? 7 | 8 | begin 9 | require 'cucumber/rake/task' 10 | 11 | namespace :cucumber do 12 | Cucumber::Rake::Task.new({ok: 'test:prepare'}, 'Run features that should pass') do |t| 13 | t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. 14 | t.fork = true # You may get faster startup if you set this to false 15 | t.profile = 'default' 16 | end 17 | 18 | Cucumber::Rake::Task.new({wip: 'test:prepare'}, 'Run features that are being worked on') do |t| 19 | t.binary = vendored_cucumber_bin 20 | t.fork = true # You may get faster startup if you set this to false 21 | t.profile = 'wip' 22 | end 23 | 24 | Cucumber::Rake::Task.new({rerun: 'test:prepare'}, 'Record failing features and run only them if any exist') do |t| 25 | t.binary = vendored_cucumber_bin 26 | t.fork = true # You may get faster startup if you set this to false 27 | t.profile = 'rerun' 28 | end 29 | 30 | desc 'Run all features' 31 | task all: [:ok, :wip] 32 | 33 | task :statsetup do 34 | require 'rails/code_statistics' 35 | ::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features') 36 | ::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features') 37 | end 38 | <% if ::Rails::VERSION::MAJOR < 6 %> 39 | 40 | task :annotations_setup do 41 | Rails.application.configure do 42 | if config.respond_to?(:annotations) 43 | config.annotations.directories << 'features' 44 | config.annotations.register_extensions('feature') { |tag| /#\s*(#{tag}):?\s*(.*)$/ } 45 | end 46 | end 47 | end 48 | <% end %> 49 | end 50 | 51 | desc 'Alias for cucumber:ok' 52 | task cucumber: 'cucumber:ok' 53 | 54 | task default: :cucumber 55 | 56 | task features: :cucumber do 57 | STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" 58 | end 59 | 60 | # In case we don't have the generic Rails test:prepare hook, append a no-op task that we can depend upon. 61 | task 'test:prepare' do 62 | end 63 | 64 | task stats: 'cucumber:statsetup' 65 | 66 | <% if ::Rails::VERSION::MAJOR < 6 %> 67 | task notes: 'cucumber:annotations_setup' 68 | <% end %> 69 | rescue LoadError 70 | desc 'cucumber rake task not available (cucumber not installed)' 71 | task :cucumber do 72 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' 73 | end 74 | end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /spec/cucumber/rails/database_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'database_cleaner' 4 | require 'cucumber/rails/database' 5 | 6 | describe Cucumber::Rails::Database do 7 | let(:strategy) { described_class.instance_variable_get(:@strategy) } 8 | 9 | context 'when using a valid pre-determined strategy' do 10 | before { described_class.javascript_strategy = :truncation } 11 | 12 | it 'forwards a `before_non_js` event to the selected strategy' do 13 | expect(strategy).to receive(:before_non_js) 14 | 15 | described_class.before_non_js 16 | end 17 | 18 | it 'forwards a `before_js` event to the selected strategy' do 19 | expect(strategy).to receive(:before_js) 20 | 21 | described_class.before_js 22 | end 23 | 24 | it 'raises an error on `before_js` if no DatabaseCleaner cleaners exist' do 25 | allow(DatabaseCleaner).to receive(:cleaners).and_return({}) 26 | 27 | expect { described_class.before_js }.to raise_error(/No DatabaseCleaner strategies found/) 28 | end 29 | end 30 | 31 | context 'when using an invalid pre-determined strategy' do 32 | it 'raises an error if you use a non-understood strategy' do 33 | expect { described_class.javascript_strategy = :invalid }.to raise_error(Cucumber::Rails::Database::InvalidStrategy) 34 | end 35 | end 36 | 37 | context 'when using a valid custom strategy' do 38 | let(:strategy_type) do 39 | Class.new do 40 | def before_js 41 | # Anything 42 | end 43 | 44 | def before_non_js 45 | # Likewise 46 | end 47 | end 48 | end 49 | 50 | before { described_class.javascript_strategy = strategy_type } 51 | 52 | it 'forwards a `before_non_js` event to the strategy' do 53 | expect(strategy).to receive(:before_non_js) 54 | 55 | described_class.before_non_js 56 | end 57 | 58 | it 'forwards a `before_js` event to the strategy' do 59 | expect(strategy).to receive(:before_js) 60 | 61 | described_class.before_js 62 | end 63 | end 64 | 65 | context 'when using an invalid custom strategy' do 66 | let(:invalid_strategy) { Class.new } 67 | 68 | it 'raises an error if the strategy does not have a valid interface' do 69 | expect { described_class.javascript_strategy = invalid_strategy }.to raise_error(ArgumentError) 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/generators/cucumber/install_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Generators are not automatically loaded by Rails 4 | require 'generators/cucumber/install_generator' 5 | 6 | describe Cucumber::InstallGenerator do 7 | # Tell the generator where to put its output (what it thinks of as Rails.root) 8 | destination File.expand_path('../../../../tmp', __dir__) 9 | 10 | before do 11 | prepare_destination 12 | create_config_files_the_generator_is_expected_to_modify 13 | end 14 | 15 | def create_config_files_the_generator_is_expected_to_modify 16 | FileUtils.mkdir_p File.join(destination_root, 'config', 'environments') 17 | %w[development test].each do |environment| 18 | File.write( 19 | File.join(destination_root, 'config', 'environments', "#{environment}.rb"), 20 | "Rails.application.configure do\nend" 21 | ) 22 | end 23 | end 24 | 25 | let(:auto_generated_message) do 26 | '# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.' 27 | end 28 | 29 | context 'without arguments' do 30 | before { run_generator } 31 | 32 | describe 'config/cucumber.yml' do 33 | subject { file('config/cucumber.yml') } 34 | 35 | it { is_expected.to exist } 36 | it { is_expected.to contain 'default: <%= std_opts %> features' } 37 | end 38 | 39 | describe 'features/step_definitions folder' do 40 | subject { file('features/step_definitions') } 41 | 42 | it { is_expected.to exist } 43 | end 44 | 45 | describe 'features/support/env.rb' do 46 | subject { file('features/support/env.rb') } 47 | 48 | it { is_expected.to exist } 49 | it { is_expected.to contain auto_generated_message } 50 | it { is_expected.to contain "require 'cucumber/rails'" } 51 | end 52 | 53 | describe 'lib/tasks/cucumber.rake' do 54 | subject { file('lib/tasks/cucumber.rake') } 55 | 56 | it { is_expected.to exist } 57 | it { is_expected.to contain auto_generated_message } 58 | it { is_expected.to contain "task cucumber: 'cucumber:ok'" } 59 | end 60 | 61 | describe 'bin/cucumber' do 62 | subject { file('bin/cucumber') } 63 | 64 | it { is_expected.to exist } 65 | it { is_expected.to contain 'load Cucumber::BINARY' } 66 | end 67 | 68 | if Rails::VERSION::MAJOR >= 6 69 | %w[development test].each do |environment| 70 | describe "config/environments/#{environment}.rb" do 71 | subject { file("config/environments/#{environment}.rb") } 72 | 73 | it { is_expected.to contain "config.annotations.register_extensions('feature') { |tag| /#\\s*(\#{tag}):?\\s*(.*)$/ }" } 74 | it { is_expected.to contain "config.annotations.register_directories('features')" } 75 | end 76 | end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails/all' 4 | 5 | ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:' 6 | 7 | module CucumberRails 8 | class Application < ::Rails::Application 9 | config.secret_key_base = 'ASecretString' 10 | end 11 | end 12 | 13 | require 'rspec/support/spec' 14 | require 'ammeter/init' 15 | --------------------------------------------------------------------------------