├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .standard.yml
├── .travis.yml
├── CHANGELOG.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── cypress-rails.gemspec
├── example
├── .browserslistrc
├── .gitignore
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── app
│ ├── assets
│ │ ├── config
│ │ │ └── manifest.js
│ │ └── stylesheets
│ │ │ └── application.css
│ ├── controllers
│ │ ├── application_controller.rb
│ │ ├── compliments_controller.rb
│ │ ├── forms_controller.rb
│ │ └── static_pages_controller.rb
│ ├── helpers
│ │ └── application_helper.rb
│ ├── javascript
│ │ └── packs
│ │ │ └── application.js
│ ├── jobs
│ │ └── application_job.rb
│ ├── models
│ │ ├── application_record.rb
│ │ └── compliment.rb
│ └── views
│ │ ├── compliments
│ │ └── index.html.erb
│ │ ├── forms
│ │ └── static.html.erb
│ │ ├── layouts
│ │ └── application.html.erb
│ │ └── static_pages
│ │ └── external.html.erb
├── babel.config.js
├── bin
│ ├── bundle
│ ├── rails
│ ├── rake
│ ├── setup
│ ├── webpack
│ ├── webpack-dev-server
│ └── yarn
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── credentials.yml.enc
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── application_controller_renderer.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── content_security_policy.rb
│ │ ├── cookies_serializer.rb
│ │ ├── cypress_rails_initializer.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ └── en.yml
│ ├── puma.rb
│ ├── routes.rb
│ ├── webpack
│ │ ├── development.js
│ │ ├── environment.js
│ │ ├── production.js
│ │ └── test.js
│ └── webpacker.yml
├── cypress.config.js
├── cypress
│ ├── e2e
│ │ ├── compliments_test.cy.js
│ │ ├── external-service.cy.js
│ │ └── static-html.cy.js
│ └── support
│ │ ├── commands.js
│ │ └── e2e.js
├── db
│ ├── migrate
│ │ └── 20200304023531_create_compliments.rb
│ ├── schema.rb
│ └── seeds.rb
├── external_service
│ └── compliment.json
├── lib
│ └── external_service.rb
├── package.json
├── postcss.config.js
├── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── apple-touch-icon-precomposed.png
│ ├── apple-touch-icon.png
│ ├── favicon.ico
│ └── robots.txt
├── test
│ ├── application_system_test_case.rb
│ ├── fixtures
│ │ └── compliments.yml
│ └── test_helper.rb
└── yarn.lock
├── exe
└── cypress-rails
├── lib
├── cypress-rails.rb
└── cypress-rails
│ ├── config.rb
│ ├── env.rb
│ ├── finds_bin.rb
│ ├── init.rb
│ ├── initializer_hooks.rb
│ ├── launches_cypress.rb
│ ├── manages_transactions.rb
│ ├── open.rb
│ ├── railtie.rb
│ ├── rake.rb
│ ├── resets_state.rb
│ ├── run.rb
│ ├── server.rb
│ ├── server
│ ├── checker.rb
│ ├── middleware.rb
│ ├── puma.rb
│ └── timer.rb
│ ├── starts_rails_server.rb
│ ├── tracks_resets.rb
│ └── version.rb
├── script
├── test
└── test_example_app
└── test
├── config_test.rb
├── cypress_rails_test.rb
└── test_helper.rb
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request, workflow_dispatch]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | strategy:
10 | matrix:
11 | ruby-version: ['3.0', '3.3']
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: ruby/setup-ruby@v1
16 | with:
17 | ruby-version: ${{ matrix.ruby-version }}
18 | bundler-cache: true
19 |
20 | - name: Run tests
21 | run: ./script/test
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /coverage/
5 | /doc/
6 | /pkg/
7 | /spec/reports/
8 | /tmp/
9 |
--------------------------------------------------------------------------------
/.standard.yml:
--------------------------------------------------------------------------------
1 | ruby_version: 2.5
2 | ignore:
3 | - 'vendor/bundle/**/*'
4 | - 'example/node_modules/**/*'
5 | - 'example/vendor/bundle/**/*'
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | sudo: false
3 | language: ruby
4 | cache: bundler
5 | rvm:
6 | - 2.6.3
7 | before_install: gem install bundler -v 1.17.3
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## 0.7.1
4 | * Add Rack 3.1 support [#163](https://github.com/testdouble/cypress-rails/pull/163)
5 |
6 | ## 0.7.0
7 | git
8 | * Add a `CYPRESS_RAILS_CYPRESS_DIR` option for cases where
9 | the cypress tests live outside the CYPRESS_RAILS_DIR [#159](https://github.com/testdouble/cypress-rails/pull/159)
10 |
11 | ## 0.6.1
12 |
13 | * Fix a deprecation warning in Rails
14 | [#157](https://github.com/testdouble/cypress-rails/pull/157)
15 |
16 | ## 0.6.0
17 |
18 | * Update initializer task to generate valid Cypress v10+ configurations
19 | [#156](https://github.com/testdouble/cypress-rails/pull/156)
20 |
21 | ## 0.5.5
22 |
23 | * Add Puma 6 support
24 | [#136](https://github.com/testdouble/cypress-rails/pull/136)
25 |
26 | ## 0.5.4
27 |
28 | * Fix Rails 5 support
29 | [#126](https://github.com/testdouble/cypress-rails/pull/126)
30 |
31 | ## 0.5.3
32 |
33 | * Fix 2.5 & 2.6 compatibility
34 | [#100](https://github.com/testdouble/cypress-rails/issues/100)
35 |
36 | ## 0.5.2
37 |
38 | * Fixes a puma deprecation warning
39 | [#95](https://github.com/testdouble/cypress-rails/pull/95)
40 |
41 | ## 0.5.1
42 |
43 | * Sprinkles two instance variables to the custom transaction manager that cribs
44 | its implementation from ActiveRecord::TestFixtures (see f75f280)
45 | [#88](https://github.com/testdouble/cypress-rails/issues/88)
46 | [#89](https://github.com/testdouble/cypress-rails/pull/89)
47 |
48 | ## 0.5.0
49 |
50 | * Add hook `after_server_start`
51 | [#63](https://github.com/testdouble/cypress-rails/pull/63)
52 | * Fix namespace bug
53 | [#64](https://github.com/testdouble/cypress-rails/pull/64)
54 |
55 | ## 0.4.2
56 |
57 | * Add support to Rails 6.1 ([#52](https://github.com/testdouble/cypress-rails/issue/52))
58 |
59 | ## 0.4.1
60 |
61 | * Add backcompat for Ruby 2.4
62 | ([#47](https://github.com/testdouble/cypress-rails/pull/47))
63 |
64 | ## 0.4.0
65 |
66 | * Add a `CYPRESS_RAILS_HOST` option that allows a hostname to be specified (as
67 | opposed to 127.0.0.1). Puma will still bind to 127.0.0.1, but Cypress will use
68 | the hostname in its `baseUrl`, which may be necessary for some folks' tests to
69 | work
70 |
71 | ## 0.3.0
72 |
73 | * Add a `CYPRESS_RAILS_BASE_PATH` option which will be appended to the
74 | `CYPRESS_BASE_URL` option that cypress-rails sets when launching cypress
75 | commands. Apps that set `baseUrl` to something other than "/" can set this env
76 | var to match for consistent behavior (or else set it using Cypress.config in a
77 | support file)
78 |
79 | ## 0.2.0
80 |
81 | * If `RAILS_ENV` has been explicitly set when the CLI or rake task is run,
82 | respect that set value instead of overriding it to "test"
83 |
84 | ## 0.1.3
85 |
86 | * Improve behavior of SIGINT (Ctrl-C) so a traceback isn't printed and stdout
87 | isn't flushed after the program exits
88 |
89 | ## 0.1.2
90 |
91 | * Drop the hard deps on capybara and selenium-webdrivers (instead inlining
92 | portions of the Capybara server logic). Additionally, add a hard dep on puma
93 | since this gem is useless without it
94 |
95 | ## 0.1.1
96 |
97 | * Fix the `before_server_stop` hook by rolling back transactions first so that
98 | it can clean out test data
99 |
100 | ## 0.1.0
101 |
102 | * **[Breaking]** Remove `CypressRails::TestCase`. Use `rake cypress:run` instead
103 | * **[Breaking]** cypress-rails now starts a transaction immediately after
104 | launching the server, which could result in other processes not being able
105 | to observe your changes. To revert to the old behavior, set the env var
106 | `CYPRESS_RAILS_TRANSACTIONAL_SERVER=false`
107 | * Add configuration variables `CYPRESS_RAILS_DIR`,
108 | `CYPRESS_RAILS_TRANSACTIONAL_SERVER`. Rename port and Cypress CLI forwarding
109 | to `CYPRESS_RAILS_PORT` and `CYPRESS_RAILS_CYPRESS_OPTS`
110 | * Add test data configuration hooks (to be run in an initializer):
111 | * `CypressRails.hooks.before_server_start`
112 | * `CypressRails.hooks.after_transaction_start`
113 | * `CypressRails.hooks.after_state_reset` - after a transaction rollback
114 | * `CypressRails.hooks.before_server_stop` - called in an `at_exit` hook
115 |
116 | ## 0.0.4
117 |
118 | * Started a changelog
119 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4 |
5 | # Specify your gem's dependencies in cypress-rails.gemspec
6 | gemspec
7 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | cypress-rails (0.7.1)
5 | puma (>= 3.8.0)
6 | railties (>= 5.2.0)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actionpack (7.1.3)
12 | actionview (= 7.1.3)
13 | activesupport (= 7.1.3)
14 | nokogiri (>= 1.8.5)
15 | racc
16 | rack (>= 2.2.4)
17 | rack-session (>= 1.0.1)
18 | rack-test (>= 0.6.3)
19 | rails-dom-testing (~> 2.2)
20 | rails-html-sanitizer (~> 1.6)
21 | actionview (7.1.3)
22 | activesupport (= 7.1.3)
23 | builder (~> 3.1)
24 | erubi (~> 1.11)
25 | rails-dom-testing (~> 2.2)
26 | rails-html-sanitizer (~> 1.6)
27 | activesupport (7.1.3)
28 | base64
29 | bigdecimal
30 | concurrent-ruby (~> 1.0, >= 1.0.2)
31 | connection_pool (>= 2.2.5)
32 | drb
33 | i18n (>= 1.6, < 2)
34 | minitest (>= 5.1)
35 | mutex_m
36 | tzinfo (~> 2.0)
37 | ast (2.4.2)
38 | base64 (0.2.0)
39 | bigdecimal (3.1.6)
40 | builder (3.2.4)
41 | concurrent-ruby (1.2.3)
42 | connection_pool (2.4.1)
43 | crass (1.0.6)
44 | drb (2.2.0)
45 | ruby2_keywords
46 | erubi (1.12.0)
47 | i18n (1.14.1)
48 | concurrent-ruby (~> 1.0)
49 | io-console (0.7.2)
50 | irb (1.13.1)
51 | rdoc (>= 4.0.0)
52 | reline (>= 0.4.2)
53 | json (2.7.1)
54 | language_server-protocol (3.17.0.3)
55 | lint_roller (1.1.0)
56 | loofah (2.22.0)
57 | crass (~> 1.0.2)
58 | nokogiri (>= 1.12.0)
59 | minitest (5.22.2)
60 | mutex_m (0.2.0)
61 | nio4r (2.7.0)
62 | nokogiri (1.16.2)
63 | racc (~> 1.4)
64 | parallel (1.24.0)
65 | parser (3.3.0.5)
66 | ast (~> 2.4.1)
67 | racc
68 | psych (5.1.2)
69 | stringio
70 | puma (6.4.2)
71 | nio4r (~> 2.0)
72 | racc (1.7.3)
73 | rack (3.1.7)
74 | rack-session (2.0.0)
75 | rack (>= 3.0.0)
76 | rack-test (2.1.0)
77 | rack (>= 1.3)
78 | rackup (2.1.0)
79 | rack (>= 3)
80 | webrick (~> 1.8)
81 | rails-dom-testing (2.2.0)
82 | activesupport (>= 5.0.0)
83 | minitest
84 | nokogiri (>= 1.6)
85 | rails-html-sanitizer (1.6.0)
86 | loofah (~> 2.21)
87 | nokogiri (~> 1.14)
88 | railties (7.1.3)
89 | actionpack (= 7.1.3)
90 | activesupport (= 7.1.3)
91 | irb
92 | rackup (>= 1.0.0)
93 | rake (>= 12.2)
94 | thor (~> 1.0, >= 1.2.2)
95 | zeitwerk (~> 2.6)
96 | rainbow (3.1.1)
97 | rake (13.1.0)
98 | rdoc (6.6.3.1)
99 | psych (>= 4.0.0)
100 | regexp_parser (2.9.0)
101 | reline (0.5.7)
102 | io-console (~> 0.5)
103 | rexml (3.2.6)
104 | rubocop (1.59.0)
105 | json (~> 2.3)
106 | language_server-protocol (>= 3.17.0)
107 | parallel (~> 1.10)
108 | parser (>= 3.2.2.4)
109 | rainbow (>= 2.2.2, < 4.0)
110 | regexp_parser (>= 1.8, < 3.0)
111 | rexml (>= 3.2.5, < 4.0)
112 | rubocop-ast (>= 1.30.0, < 2.0)
113 | ruby-progressbar (~> 1.7)
114 | unicode-display_width (>= 2.4.0, < 3.0)
115 | rubocop-ast (1.30.0)
116 | parser (>= 3.2.1.0)
117 | rubocop-performance (1.20.2)
118 | rubocop (>= 1.48.1, < 2.0)
119 | rubocop-ast (>= 1.30.0, < 2.0)
120 | ruby-progressbar (1.13.0)
121 | ruby2_keywords (0.0.5)
122 | standard (1.33.0)
123 | language_server-protocol (~> 3.17.0.2)
124 | lint_roller (~> 1.0)
125 | rubocop (~> 1.59.0)
126 | standard-custom (~> 1.0.0)
127 | standard-performance (~> 1.3)
128 | standard-custom (1.0.2)
129 | lint_roller (~> 1.0)
130 | rubocop (~> 1.50)
131 | standard-performance (1.3.1)
132 | lint_roller (~> 1.1)
133 | rubocop-performance (~> 1.20.2)
134 | stringio (3.1.1)
135 | thor (1.3.0)
136 | tzinfo (2.0.6)
137 | concurrent-ruby (~> 1.0)
138 | unicode-display_width (2.5.0)
139 | webrick (1.8.1)
140 | zeitwerk (2.6.13)
141 |
142 | PLATFORMS
143 | ruby
144 |
145 | DEPENDENCIES
146 | bundler
147 | cypress-rails!
148 | minitest (~> 5.0)
149 | rake (~> 13.0)
150 | standard (>= 0.2.0)
151 |
152 | BUNDLED WITH
153 | 2.3.13
154 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2019-2020 Test Double, LLC
4 |
5 | Portions of the CypressRails::Server component:
6 | Copyright (c) 2009-2018 Thomas Walpole, Jonas Nicklas
7 |
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to deal
11 | in the Software without restriction, including without limitation the rights
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in
17 | all copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cypress-rails
2 |
3 | This is a simple gem to make it easier to start writing browser tests with
4 | [Cypress](http://cypress.io) for your [Rails](https://rubyonrails.org) apps,
5 | regardless of whether your app is server-side rendered HTML, completely
6 | client-side JavaScript, or something in-between.
7 |
8 | ## Installation
9 |
10 | **tl;dr**:
11 |
12 | 1. Install the npm package `cypress`
13 | 2. Install this gem `cypress-rails`
14 | 3. Run `rake cypress:init`
15 |
16 | ### Installing Cypress itself
17 |
18 | The first step is making sure Cypress is installed (that's up to you, this
19 | library doesn't install Cypress, it just provides a little Rails-specific glue).
20 |
21 | If you're on newer versions of Rails and using
22 | [webpacker](https://www.github.com/rails/webpacker) for your front-end assets,
23 | then you're likely already using yarn to manage your JavaScript dependencies. If
24 | that's the case, you can add Cypress with:
25 |
26 | ```sh
27 | $ yarn add --dev cypress
28 | ```
29 |
30 | If you're not using yarn in conjunction with your Rails app, check out the
31 | Cypress docs on getting it installed. At the end of the day, this gem just needs
32 | the `cypress` binary to exist either in `./node_modules/.bin/cypress` or on your
33 | `PATH`.
34 |
35 | ### Installing the cypress-rails gem
36 |
37 | Now, to install the cypress-rails gem, you'll want to add it to your development
38 | & test gem groups of your Gemfile, so that you have easy access to its rake
39 | tasks:
40 |
41 | ```ruby
42 | group :development, :test do
43 | gem "cypress-rails"
44 | end
45 | ```
46 |
47 | Once installed, you'll want to run:
48 |
49 | ```sh
50 | $ rake cypress:init
51 | ```
52 |
53 | This will override a few configurations in your `cypress.config.js` configuration
54 | file.
55 |
56 | ## Usage
57 |
58 | ### Develop tests interactively with `cypress open`
59 |
60 | When writing tests with Cypress, you'll find the most pleasant experience (by
61 | way of a faster feedback loop and an interactive, easy-to-inspect test runner)
62 | using the `cypress open` command.
63 |
64 | When using Rails, however, you'll also want your Rails test server to be running
65 | so that there's something for Cypress to interact with. `cypress-rails` provides
66 | a wrapper for running `cypress open` with a dedicated Rails test server.
67 |
68 | So, by running:
69 |
70 | ```sh
71 | $ rake cypress:open
72 | ```
73 |
74 | Any JavaScript files added to `cypress/integration` will be identified by
75 | Cypress as tests. Simply click a test file in the Cypress application window to
76 | launch the test in a browser. Each time you save the test file, it will re-run
77 | itself.
78 |
79 | ### Run tests headlessly with `cypress run`
80 |
81 | To run your tests headlessly (e.g. when you're in CI), you'll want the `run`
82 | command:
83 |
84 | ```sh
85 | $ rake cypress:run
86 | ```
87 |
88 | ## Managing your test data
89 |
90 | The tricky thing about browser tests is that they usually depend on some test
91 | data being available with which to exercise the app efficiently. Because cypress
92 | is a JavaScript-based tool and can't easily manipulate your Rails app directly,
93 | cypress-rails provides a number of hooks that you can use to manage your test
94 | data.
95 |
96 | Here's what a `config/initializers/cypress_rails.rb` initializer might look
97 | like:
98 |
99 | ```ruby
100 | return unless Rails.env.test?
101 |
102 | CypressRails.hooks.before_server_start do
103 | # Called once, before either the transaction or the server is started
104 | end
105 |
106 | CypressRails.hooks.after_transaction_start do
107 | # Called after the transaction is started (at launch and after each reset)
108 | end
109 |
110 | CypressRails.hooks.after_state_reset do
111 | # Triggered after `/cypress_rails_reset_state` is called
112 | end
113 |
114 | CypressRails.hooks.before_server_stop do
115 | # Called once, at_exit
116 | end
117 | ```
118 |
119 | (You can find [an
120 | example
121 | initializer](/example/config/initializers/cypress_rails_initializer.rb)
122 | in this repo.)
123 |
124 | The gem also provides a special route on the test server:
125 | `/cypress_rails_reset_state`. Each time it's called, cypress-rails will do
126 | two things at the beginning of the next request received by the Rails app:
127 |
128 | * If `CYPRESS_RAILS_TRANSACTIONAL_SERVER` is enabled, roll back the transaction,
129 | effectively resetting the application state to whatever it was at the start of
130 | the test run
131 |
132 | * Trigger any `after_state_reset` hooks you've configured (regardless of the
133 | transactional server setting)
134 |
135 | This way, you can easily instruct the server to reset its test state from your
136 | Cypress tests like so:
137 |
138 | ```js
139 | beforeEach(() => {
140 | cy.request('/cypress_rails_reset_state')
141 | })
142 | ```
143 |
144 | (Remember, in Cypress, `before` is a before-all hook and `beforeEach` is run
145 | between each test case!)
146 |
147 | ## Configuration
148 |
149 | ### Environment variables
150 |
151 | The cypress-rails gem is configured entirely via environment variables. If you
152 | find yourself repeating a number of verbose environment variables as you run
153 | your tests, consider invoking the gem from a custom script or setting your
154 | preferred environment variables project-wide using a tool like
155 | [dotenv](https://github.com/bkeepers/dotenv).
156 |
157 |
158 | * **CYPRESS_RAILS_DIR** (default: `Dir.pwd`) the directory of your Rails project
159 | * **CYPRESS_RAILS_CYPRESS_DIR** (default: _same value as `rails_dir`_) the directory of your Cypress project
160 | * **CYPRESS_RAILS_HOST** (default: `"127.0.0.1"`) the hostname to bind to
161 | * **CYPRESS_RAILS_PORT** (default: _a random available port_) the port to run
162 | the Rails test server on
163 | * **CYPRESS_RAILS_BASE_PATH** (default: `"/"`) the base path for all Cypress's
164 | requests to the app (e.g. via `cy.visit()`). If you've customized your
165 | `baseUrl` setting (e.g. in `cypress.config.js`), you'll need to duplicate it with
166 | this environment variable
167 | * **CYPRESS_RAILS_TRANSACTIONAL_SERVER** (default: `true`) when true, will start
168 | a transaction on all database connections before launching the server. In
169 | general this means anything done during `cypress open` or `cypress run` will
170 | be rolled back on exit (similar to running a Rails System test)
171 | * **CYPRESS_RAILS_CYPRESS_OPTS** (default: _none_) any options you want to
172 | forward to the Cypress CLI when running its `open` or `run` commands.
173 |
174 | #### Example: Running a single spec from the command line
175 |
176 | It's a little verbose, but an example of using the above options to run a single
177 | Cypress test would look like this:
178 |
179 | ```
180 | $ CYPRESS_RAILS_CYPRESS_OPTS="--spec cypress/integration/a_test.js" bin/rake cypress:run
181 | ```
182 |
183 | #### Example: Running your tests in Chromium
184 |
185 | By default, Cypress will run its tests in its packaged Electron app, unless you've configured it globally. To choose which browser it will run from the command line, try this:
186 |
187 | ```
188 | $ CYPRESS_RAILS_CYPRESS_OPTS="--browser chromium" bin/rake cypress:run
189 | ```
190 |
191 | ### Initializer hooks
192 |
193 | ### before_server_start
194 |
195 | Pass a block to `CypressRails.hooks.before_server_start` to register a hook that
196 | will execute before the server or any transaction has been started. If you use
197 | Rails fixtures, it may make sense to load them here, so they don't need to be
198 | re-inserted for each request
199 |
200 | ### after_server_start
201 |
202 | Pass a block to `CypressRails.hooks.after_server_start` to register a hook that
203 | will execute after the server has booted.
204 |
205 | ### after_transaction_start
206 |
207 | If there's any custom behavior or state management you want to do inside the
208 | transaction (so that it's also rolled back each time a reset is triggered),
209 | pass a block to `CypressRails.hooks.after_transaction_start`.
210 |
211 | ### after_state_reset
212 |
213 | Every time the test server receives an HTTP request at
214 | `/cypress_rails_reset_state`, the transaction will be rolled back (if
215 | `CYPRESS_RAILS_TRANSACTIONAL_SERVER` is enabled) and the `after_state_reset`
216 | hook will be triggered. To set up the hook, pass a block to
217 | `CypressRails.hooks.after_state_reset`.
218 |
219 | ### before_server_stop
220 |
221 | In case you've made any permanent changes to your test database that could
222 | pollute other test suites or scripts, you can use the `before_server_stop` to
223 | (assuming everything exits gracefully) clean things up and restore the state
224 | of your test database. To set up the hook, pass a block to
225 | `CypressRails.hooks.before_server_stop`.
226 |
227 | ## Configuring Rails
228 |
229 | Beyond the configuration options above, you'll probably also want to disable caching
230 | in your Rails app's [config/environments/test.rb](/example/config/environments/test.rb#L9)
231 | file, so that changes to your Ruby code are reflected in your tests while you
232 | work on them with `rake cypress:open`. (If either option is set to
233 | `true`, any changes to your Ruby code will require a server restart to be reflected as you work
234 | on your tests.)
235 |
236 | To illustrate, here's what that might look like in `config/environments/test.rb`:
237 |
238 | ```ruby
239 | config.cache_classes = false
240 | config.action_view.cache_template_loading = false
241 | ```
242 |
243 | ## Setting up continuous integration
244 |
245 | #### Circle CI
246 |
247 | Nowadays, Cypress and Circle get along pretty well without much customization.
248 | The only tricky bit is that Cypress will install its large-ish binary to
249 | `~/.cache/Cypress`, so if you cache your dependencies, you'll want to include
250 | that path:
251 |
252 | ```yml
253 | version: 2
254 | jobs:
255 | build:
256 | docker:
257 | - image: circleci/ruby:2.6-node-browsers
258 | - image: circleci/postgres:9.4.12-alpine
259 | environment:
260 | POSTGRES_USER: circleci
261 | steps:
262 | - checkout
263 |
264 | # Bundle install dependencies
265 | - type: cache-restore
266 | key: v1-gems-{{ checksum "Gemfile.lock" }}
267 |
268 | - run: bundle install --path vendor/bundle
269 |
270 | - type: cache-save
271 | key: v1-gems-{{ checksum "Gemfile.lock" }}
272 | paths:
273 | - vendor/bundle
274 |
275 | # Yarn dependencies
276 | - restore_cache:
277 | keys:
278 | - v1-yarn-{{ checksum "yarn.lock" }}
279 | # fallback to using the latest cache if no exact match is found
280 | - v1-yarn-
281 |
282 | - run: yarn install
283 |
284 | - save_cache:
285 | paths:
286 | - node_modules
287 | - ~/.cache
288 | key: v1-yarn-{{ checksum "yarn.lock" }}
289 |
290 | # Run your cypress tests
291 | - run: bin/rake cypress:run
292 | ```
293 |
294 | ## Why use this?
295 |
296 | Rails ships with a perfectly competent browser-testing facility called [system
297 | tests](https://guides.rubyonrails.org/testing.html#system-testing) which depend
298 | on [capybara](https://github.com/teamcapybara/capybara) to drive your tests,
299 | most often with [Selenium](https://www.seleniumhq.org). All of these tools work,
300 | are used by lots of people, and are a perfectly reasonable choice when writing
301 | full-stack tests of your Rails application.
302 |
303 | So why would you go off the Rails to use Cypress and this gem, adding two
304 | additional layers to the Jenga tower of testing facilities that Rails ships
305 | with? Really, it comes down to the potential for an improved development
306 | experience. In particular:
307 |
308 | * Cypress's [IDE-like `open`
309 | command](https://docs.cypress.io/guides/getting-started/writing-your-first-test.html#Add-a-test-file)
310 | provides a highly visual, interactive, inspectable test runner. Not only can
311 | you watch each test run and read the commands as they're executed, Cypress
312 | takes a DOM snapshot before and after each command, which makes rewinding and
313 | inspecting the state of the DOM trivially easy, something that I regularly
314 | find myself losing 20 minutes attempting to do with Capybara
315 | * `cypress open` enables an almost REPL-like feedback loop that is much faster
316 | and more information dense than using Capybara and Selenium. Rather than
317 | running a test from the command line, seeing it fail, then adding a debug
318 | breakpoint to a test to try to manipulate the browser or tweaking a call to a
319 | Capybara API method, failures tend to be rather obvious when using Cypress and
320 | fixing it is usually as easy as tweaking a command, hitting save, and watching
321 | it re-run
322 | * With very few exceptions, a Cypress test that works in a browser window will
323 | also pass when run headlessly in CI
324 | * Cypress selectors are [just jQuery
325 | selectors](https://api.jquery.com/category/selectors/), which makes them both
326 | more familiar and more powerful than the CSS and XPath selectors offered by
327 | Capybara. Additionally, Cypress makes it very easy to drop into a plain
328 | synchronous JavaScript function for [making more complex
329 | assertions](https://docs.cypress.io/guides/references/assertions.html#Should-callback)
330 | or composing repetitive tasks into [custom
331 | commands](https://docs.cypress.io/api/cypress-api/custom-commands.html#Syntax#article)
332 | * Cypress commands are, generally, much faster than analogous tasks in Selenium.
333 | Where certain clicks and form inputs will hang for 300-500ms for seemingly no
334 | reason when running against Selenium WebDriver, Cypress commands tend to run
335 | as fast as jQuery can select and fill an element (which is, of course, pretty
336 | fast)
337 | * By default, Cypress [takes a
338 | video](https://docs.cypress.io/guides/guides/screenshots-and-videos.html#Screenshots#article)
339 | of every headless test run, taking a lot of the mystery (and subsequent
340 | analysis & debugging) out of test failures in CI
341 |
342 | Nevertheless, there are trade-offs to attempting this (most notably around
343 | Cypress's [limited browser
344 | support](https://docs.cypress.io/guides/guides/launching-browsers.html#Browsers)
345 | and the complications to test data management), and I wouldn't recommend
346 | adopting Cypress and writing a bunch of browser tests for every application.
347 | But, if the above points sound like solutions to problems you experience, you
348 | might consider trying it out.
349 |
350 | ## Code of Conduct
351 |
352 | This project follows Test Double's [code of
353 | conduct](https://testdouble.com/code-of-conduct) for all community interactions,
354 | including (but not limited to) one-on-one communications, public posts/comments,
355 | code reviews, pull requests, and GitHub issues. If violations occur, Test Double
356 | will take any action they deem appropriate for the infraction, up to and
357 | including blocking a user from the organization's repositories.
358 |
359 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rake/testtask"
3 | require "standard/rake"
4 |
5 | Rake::TestTask.new(:test) do |t|
6 | t.libs << "test"
7 | t.libs << "lib"
8 | t.test_files = FileList["test/**/*_test.rb"]
9 | end
10 |
11 | task default: [:test, "standard:fix"]
12 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/setup"
4 | require "cypress-rails"
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 |
13 | require "irb"
14 | IRB.start(__FILE__)
15 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/cypress-rails.gemspec:
--------------------------------------------------------------------------------
1 | lib = File.expand_path("../lib", __FILE__)
2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3 | require "cypress-rails/version"
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = "cypress-rails"
7 | spec.version = CypressRails::VERSION
8 | spec.authors = ["Justin Searls"]
9 | spec.email = ["searls@gmail.com"]
10 |
11 | spec.summary = "Helps you write Cypress tests of your Rails app"
12 | spec.homepage = "https://github.com/testdouble/cypress-rails"
13 | spec.license = "MIT"
14 | spec.metadata["rubygems_mfa_required"] = "true"
15 |
16 | # Specify which files should be added to the gem when it is released.
17 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18 | spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
19 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|example)/}) }
20 | end
21 | spec.bindir = "exe"
22 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23 | spec.require_paths = ["lib"]
24 |
25 | spec.add_dependency "railties", ">= 5.2.0"
26 | spec.add_dependency "puma", ">= 3.8.0"
27 |
28 | spec.add_development_dependency "bundler"
29 | spec.add_development_dependency "rake", "~> 13.0"
30 | spec.add_development_dependency "minitest", "~> 5.0"
31 | spec.add_development_dependency "standard", ">= 0.2.0"
32 | end
33 |
--------------------------------------------------------------------------------
/example/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 | /db/*.sqlite3-shm
14 | /db/*.sqlite3-wal
15 |
16 | # Ignore all logfiles and tempfiles.
17 | /log/*
18 | /tmp/*
19 |
20 |
21 | /public/assets
22 | .byebug_history
23 |
24 | # Ignore master key for decrypting credentials and more.
25 | /config/master.key
26 |
27 | /public/packs
28 | /public/packs-test
29 | /node_modules
30 | /yarn-error.log
31 | yarn-debug.log*
32 | .yarn-integrity
33 |
--------------------------------------------------------------------------------
/example/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "rails"
4 | gem "sqlite3"
5 | gem "puma"
6 | gem "webpacker"
7 | gem "bootsnap", require: false
8 |
9 | group :development, :test do
10 | gem "cypress-rails", path: ".."
11 | end
12 |
--------------------------------------------------------------------------------
/example/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | cypress-rails (0.7.1)
5 | puma (>= 3.8.0)
6 | railties (>= 5.2.0)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (7.1.3)
12 | actionpack (= 7.1.3)
13 | activesupport (= 7.1.3)
14 | nio4r (~> 2.0)
15 | websocket-driver (>= 0.6.1)
16 | zeitwerk (~> 2.6)
17 | actionmailbox (7.1.3)
18 | actionpack (= 7.1.3)
19 | activejob (= 7.1.3)
20 | activerecord (= 7.1.3)
21 | activestorage (= 7.1.3)
22 | activesupport (= 7.1.3)
23 | mail (>= 2.7.1)
24 | net-imap
25 | net-pop
26 | net-smtp
27 | actionmailer (7.1.3)
28 | actionpack (= 7.1.3)
29 | actionview (= 7.1.3)
30 | activejob (= 7.1.3)
31 | activesupport (= 7.1.3)
32 | mail (~> 2.5, >= 2.5.4)
33 | net-imap
34 | net-pop
35 | net-smtp
36 | rails-dom-testing (~> 2.2)
37 | actionpack (7.1.3)
38 | actionview (= 7.1.3)
39 | activesupport (= 7.1.3)
40 | nokogiri (>= 1.8.5)
41 | racc
42 | rack (>= 2.2.4)
43 | rack-session (>= 1.0.1)
44 | rack-test (>= 0.6.3)
45 | rails-dom-testing (~> 2.2)
46 | rails-html-sanitizer (~> 1.6)
47 | actiontext (7.1.3)
48 | actionpack (= 7.1.3)
49 | activerecord (= 7.1.3)
50 | activestorage (= 7.1.3)
51 | activesupport (= 7.1.3)
52 | globalid (>= 0.6.0)
53 | nokogiri (>= 1.8.5)
54 | actionview (7.1.3)
55 | activesupport (= 7.1.3)
56 | builder (~> 3.1)
57 | erubi (~> 1.11)
58 | rails-dom-testing (~> 2.2)
59 | rails-html-sanitizer (~> 1.6)
60 | activejob (7.1.3)
61 | activesupport (= 7.1.3)
62 | globalid (>= 0.3.6)
63 | activemodel (7.1.3)
64 | activesupport (= 7.1.3)
65 | activerecord (7.1.3)
66 | activemodel (= 7.1.3)
67 | activesupport (= 7.1.3)
68 | timeout (>= 0.4.0)
69 | activestorage (7.1.3)
70 | actionpack (= 7.1.3)
71 | activejob (= 7.1.3)
72 | activerecord (= 7.1.3)
73 | activesupport (= 7.1.3)
74 | marcel (~> 1.0)
75 | activesupport (7.1.3)
76 | base64
77 | bigdecimal
78 | concurrent-ruby (~> 1.0, >= 1.0.2)
79 | connection_pool (>= 2.2.5)
80 | drb
81 | i18n (>= 1.6, < 2)
82 | minitest (>= 5.1)
83 | mutex_m
84 | tzinfo (~> 2.0)
85 | base64 (0.2.0)
86 | bigdecimal (3.1.6)
87 | bootsnap (1.18.3)
88 | msgpack (~> 1.2)
89 | builder (3.2.4)
90 | concurrent-ruby (1.2.3)
91 | connection_pool (2.4.1)
92 | crass (1.0.6)
93 | date (3.3.4)
94 | drb (2.2.0)
95 | ruby2_keywords
96 | erubi (1.12.0)
97 | globalid (1.2.1)
98 | activesupport (>= 6.1)
99 | i18n (1.14.1)
100 | concurrent-ruby (~> 1.0)
101 | io-console (0.7.2)
102 | irb (1.11.2)
103 | rdoc
104 | reline (>= 0.4.2)
105 | loofah (2.22.0)
106 | crass (~> 1.0.2)
107 | nokogiri (>= 1.12.0)
108 | mail (2.8.1)
109 | mini_mime (>= 0.1.1)
110 | net-imap
111 | net-pop
112 | net-smtp
113 | marcel (1.0.2)
114 | mini_mime (1.1.5)
115 | mini_portile2 (2.8.5)
116 | minitest (5.22.2)
117 | msgpack (1.7.2)
118 | mutex_m (0.2.0)
119 | net-imap (0.4.10)
120 | date
121 | net-protocol
122 | net-pop (0.1.2)
123 | net-protocol
124 | net-protocol (0.2.2)
125 | timeout
126 | net-smtp (0.4.0.1)
127 | net-protocol
128 | nio4r (2.7.0)
129 | nokogiri (1.16.2)
130 | mini_portile2 (~> 2.8.2)
131 | racc (~> 1.4)
132 | psych (5.1.2)
133 | stringio
134 | puma (6.4.2)
135 | nio4r (~> 2.0)
136 | racc (1.7.3)
137 | rack (3.1.7)
138 | rack-proxy (0.7.7)
139 | rack
140 | rack-session (2.0.0)
141 | rack (>= 3.0.0)
142 | rack-test (2.1.0)
143 | rack (>= 1.3)
144 | rackup (2.1.0)
145 | rack (>= 3)
146 | webrick (~> 1.8)
147 | rails (7.1.3)
148 | actioncable (= 7.1.3)
149 | actionmailbox (= 7.1.3)
150 | actionmailer (= 7.1.3)
151 | actionpack (= 7.1.3)
152 | actiontext (= 7.1.3)
153 | actionview (= 7.1.3)
154 | activejob (= 7.1.3)
155 | activemodel (= 7.1.3)
156 | activerecord (= 7.1.3)
157 | activestorage (= 7.1.3)
158 | activesupport (= 7.1.3)
159 | bundler (>= 1.15.0)
160 | railties (= 7.1.3)
161 | rails-dom-testing (2.2.0)
162 | activesupport (>= 5.0.0)
163 | minitest
164 | nokogiri (>= 1.6)
165 | rails-html-sanitizer (1.6.0)
166 | loofah (~> 2.21)
167 | nokogiri (~> 1.14)
168 | railties (7.1.3)
169 | actionpack (= 7.1.3)
170 | activesupport (= 7.1.3)
171 | irb
172 | rackup (>= 1.0.0)
173 | rake (>= 12.2)
174 | thor (~> 1.0, >= 1.2.2)
175 | zeitwerk (~> 2.6)
176 | rake (13.1.0)
177 | rdoc (6.6.2)
178 | psych (>= 4.0.0)
179 | reline (0.4.2)
180 | io-console (~> 0.5)
181 | ruby2_keywords (0.0.5)
182 | semantic_range (3.0.0)
183 | sqlite3 (1.7.2)
184 | mini_portile2 (~> 2.8.0)
185 | stringio (3.1.0)
186 | thor (1.3.0)
187 | timeout (0.4.1)
188 | tzinfo (2.0.6)
189 | concurrent-ruby (~> 1.0)
190 | webpacker (5.4.4)
191 | activesupport (>= 5.2)
192 | rack-proxy (>= 0.6.1)
193 | railties (>= 5.2)
194 | semantic_range (>= 2.3.0)
195 | webrick (1.8.1)
196 | websocket-driver (0.7.6)
197 | websocket-extensions (>= 0.1.0)
198 | websocket-extensions (0.1.5)
199 | zeitwerk (2.6.13)
200 |
201 | PLATFORMS
202 | ruby
203 |
204 | DEPENDENCIES
205 | bootsnap
206 | cypress-rails!
207 | puma
208 | rails
209 | sqlite3
210 | webpacker
211 |
212 | BUNDLED WITH
213 | 2.3.13
214 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | This README would normally document whatever steps are necessary to get the
4 | application up and running.
5 |
6 | Things you may want to cover:
7 |
8 | * Ruby version
9 |
10 | * System dependencies
11 |
12 | * Configuration
13 |
14 | * Database creation
15 |
16 | * Database initialization
17 |
18 | * How to run the test suite
19 |
20 | * Services (job queues, cache servers, search engines, etc.)
21 |
22 | * Deployment instructions
23 |
24 | * ...
25 |
--------------------------------------------------------------------------------
/example/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative "config/application"
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/example/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../stylesheets .css
3 |
--------------------------------------------------------------------------------
/example/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6 | * vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/example/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/example/app/controllers/compliments_controller.rb:
--------------------------------------------------------------------------------
1 | class ComplimentsController < ApplicationController
2 | def index
3 | @compliments = Compliment.all
4 | end
5 |
6 | def update
7 | Compliment.find(params[:id]).update!(params[:compliment].permit(:text))
8 | flash[:saved] = true
9 | redirect_to compliments_path
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/example/app/controllers/forms_controller.rb:
--------------------------------------------------------------------------------
1 | class FormsController < ApplicationController
2 | def static
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/example/app/controllers/static_pages_controller.rb:
--------------------------------------------------------------------------------
1 | class StaticPagesController < ApplicationController
2 | def external
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/example/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/example/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | // This file is automatically compiled by Webpack, along with any other files
2 | // present in this directory. You're encouraged to place your actual application logic in
3 | // a relevant structure within app/javascript and only use these pack files to reference
4 | // that code so it'll be compiled.
5 |
6 | require("@rails/ujs").start()
7 |
8 |
9 | // Uncomment to copy all static images under ../images to the output folder and reference
10 | // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
11 | // or the `imagePath` JavaScript helper below.
12 | //
13 | // const images = require.context('../images', true)
14 | // const imagePath = (name) => images(name, true)
15 |
--------------------------------------------------------------------------------
/example/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | # Automatically retry jobs that encountered a deadlock
3 | # retry_on ActiveRecord::Deadlocked
4 |
5 | # Most jobs are safe to ignore if the underlying records are no longer available
6 | # discard_on ActiveJob::DeserializationError
7 | end
8 |
--------------------------------------------------------------------------------
/example/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/example/app/models/compliment.rb:
--------------------------------------------------------------------------------
1 | class Compliment < ApplicationRecord
2 | end
3 |
--------------------------------------------------------------------------------
/example/app/views/compliments/index.html.erb:
--------------------------------------------------------------------------------
1 |
Compliments Time!
2 | <% if flash[:saved] %>
3 | Yay, you saved a compliment!
4 | <% end %>
5 | <% @compliments.each.with_index do |compliment, i| %>
6 | <%= form_with model: compliment, local: true do |f| %>
7 | <%= f.text_field :text %>
8 | <%= f.submit "Save #{i + 1}" %>
9 | <% end %>
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/example/app/views/forms/static.html.erb:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/example/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AnApp
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= javascript_pack_tag 'application' %>
9 |
10 |
11 |
12 | <%= yield %>
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/app/views/static_pages/external.html.erb:
--------------------------------------------------------------------------------
1 | Getting external compliments!
2 |
3 |
22 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | var validEnv = ['development', 'test', 'production']
3 | var currentEnv = api.env()
4 | var isDevelopmentEnv = api.env('development')
5 | var isProductionEnv = api.env('production')
6 | var isTestEnv = api.env('test')
7 |
8 | if (!validEnv.includes(currentEnv)) {
9 | throw new Error(
10 | 'Please specify a valid `NODE_ENV` or ' +
11 | '`BABEL_ENV` environment variables. Valid values are "development", ' +
12 | '"test", and "production". Instead, received: ' +
13 | JSON.stringify(currentEnv) +
14 | '.'
15 | )
16 | }
17 |
18 | return {
19 | presets: [
20 | isTestEnv && [
21 | require('@babel/preset-env').default,
22 | {
23 | targets: {
24 | node: 'current'
25 | }
26 | }
27 | ],
28 | (isProductionEnv || isDevelopmentEnv) && [
29 | require('@babel/preset-env').default,
30 | {
31 | forceAllTransforms: true,
32 | useBuiltIns: 'entry',
33 | corejs: 3,
34 | modules: false,
35 | exclude: ['transform-typeof-symbol']
36 | }
37 | ]
38 | ].filter(Boolean),
39 | plugins: [
40 | require('babel-plugin-macros'),
41 | require('@babel/plugin-syntax-dynamic-import').default,
42 | isTestEnv && require('babel-plugin-dynamic-import-node'),
43 | require('@babel/plugin-transform-destructuring').default,
44 | [
45 | require('@babel/plugin-proposal-class-properties').default,
46 | {
47 | loose: true
48 | }
49 | ],
50 | [
51 | require('@babel/plugin-proposal-object-rest-spread').default,
52 | {
53 | useBuiltIns: true
54 | }
55 | ],
56 | [
57 | require('@babel/plugin-transform-runtime').default,
58 | {
59 | helpers: false,
60 | regenerator: true,
61 | corejs: false
62 | }
63 | ],
64 | [
65 | require('@babel/plugin-transform-regenerator').default,
66 | {
67 | async: false
68 | }
69 | ]
70 | ].filter(Boolean)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/example/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bundle' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "rubygems"
12 |
13 | m = Module.new {
14 | module_function
15 |
16 | def invoked_as_script?
17 | File.expand_path($0) == File.expand_path(__FILE__)
18 | end
19 |
20 | def env_var_version
21 | ENV["BUNDLER_VERSION"]
22 | end
23 |
24 | def cli_arg_version
25 | return unless invoked_as_script? # don't want to hijack other binstubs
26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27 | bundler_version = nil
28 | update_index = nil
29 | ARGV.each_with_index do |a, i|
30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31 | bundler_version = a
32 | end
33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/o
34 | bundler_version = $1 || ">= 0.a"
35 | update_index = i
36 | end
37 | bundler_version
38 | end
39 |
40 | def gemfile
41 | gemfile = ENV["BUNDLE_GEMFILE"]
42 | return gemfile if gemfile && !gemfile.empty?
43 |
44 | File.expand_path("../../Gemfile", __FILE__)
45 | end
46 |
47 | def lockfile
48 | lockfile =
49 | case File.basename(gemfile)
50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51 | else "#{gemfile}.lock"
52 | end
53 | File.expand_path(lockfile)
54 | end
55 |
56 | def lockfile_version
57 | return unless File.file?(lockfile)
58 | lockfile_contents = File.read(lockfile)
59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/o
60 | Regexp.last_match(1)
61 | end
62 |
63 | def bundler_version
64 | @bundler_version ||= env_var_version || cli_arg_version ||
65 | lockfile_version || "#{Gem::Requirement.default}.a"
66 | end
67 |
68 | def load_bundler!
69 | ENV["BUNDLE_GEMFILE"] ||= gemfile
70 |
71 | # must dup string for RG < 1.8 compatibility
72 | activate_bundler(bundler_version.dup)
73 | end
74 |
75 | def activate_bundler(bundler_version)
76 | if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
77 | bundler_version = "< 2"
78 | end
79 | gem_error = activation_error_handling {
80 | gem "bundler", bundler_version
81 | }
82 | return if gem_error.nil?
83 | require_error = activation_error_handling {
84 | require "bundler/version"
85 | }
86 | return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
87 | warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
88 | exit 42
89 | end
90 |
91 | def activation_error_handling
92 | yield
93 | nil
94 | rescue StandardError, LoadError => e
95 | e
96 | end
97 | }
98 |
99 | m.load_bundler!
100 |
101 | if m.invoked_as_script?
102 | load Gem.bin_path("bundler", "bundle")
103 | end
104 |
--------------------------------------------------------------------------------
/example/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path("../config/application", __dir__)
3 | require_relative "../config/boot"
4 | require "rails/commands"
5 |
--------------------------------------------------------------------------------
/example/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative "../config/boot"
3 | require "rake"
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/example/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "fileutils"
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path("..", __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to setup or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts "== Installing dependencies =="
17 | system! "gem install bundler --conservative"
18 | system("bundle check") || system!("bundle install")
19 |
20 | # Install JavaScript dependencies
21 | # system('bin/yarn')
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! "bin/rails db:prepare"
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! "bin/rails log:clear tmp:clear"
33 |
34 | puts "\n== Restarting application server =="
35 | system! "bin/rails restart"
36 | end
37 |
--------------------------------------------------------------------------------
/example/bin/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "rubygems"
11 | require "bundler/setup"
12 |
13 | require "webpacker"
14 | require "webpacker/webpack_runner"
15 |
16 | APP_ROOT = File.expand_path("..", __dir__)
17 | Dir.chdir(APP_ROOT) do
18 | Webpacker::WebpackRunner.run(ARGV)
19 | end
20 |
--------------------------------------------------------------------------------
/example/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "rubygems"
11 | require "bundler/setup"
12 |
13 | require "webpacker"
14 | require "webpacker/dev_server_runner"
15 |
16 | APP_ROOT = File.expand_path("..", __dir__)
17 | Dir.chdir(APP_ROOT) do
18 | Webpacker::DevServerRunner.run(ARGV)
19 | end
20 |
--------------------------------------------------------------------------------
/example/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path("..", __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | exec "yarnpkg", *ARGV
5 | rescue Errno::ENOENT
6 | warn "Yarn executable was not detected in the system."
7 | warn "Download Yarn at https://yarnpkg.com/en/docs/install"
8 | exit 1
9 | end
10 |
--------------------------------------------------------------------------------
/example/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/example/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails"
4 | # Pick the frameworks you want:
5 | require "active_model/railtie"
6 | require "active_job/railtie"
7 | require "active_record/railtie"
8 | # require "active_storage/engine"
9 | require "action_controller/railtie"
10 | # require "action_mailer/railtie"
11 | # require "action_mailbox/engine"
12 | # require "action_text/engine"
13 | require "action_view/railtie"
14 | # require "action_cable/engine"
15 | # require "sprockets/railtie"
16 | require "rails/test_unit/railtie"
17 |
18 | # Require the gems listed in Gemfile, including any gems
19 | # you've limited to :test, :development, or :production.
20 | Bundler.require(*Rails.groups)
21 |
22 | module AnApp
23 | class Application < Rails::Application
24 | # Initialize configuration defaults for originally generated Rails version.
25 | config.load_defaults 6.0
26 |
27 | # Settings in config/environments/* take precedence over those specified here.
28 | # Application configuration can go into files in config/initializers
29 | # -- all .rb files in that directory are automatically loaded after loading
30 | # the framework and any gems in your application.
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/example/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
2 |
3 | require "bundler/setup" # Set up gems listed in the Gemfile.
4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/example/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | DJUkGRhh6Rx8q45jPHT1XsYpoNMz4ChMViVWSQZeto7oDh7zxPH0BnBy2jEq19FEw4pZQEOuoXFLNAxKj18u6cCFP+VhqhLXGBXvUaDTsc8mqGaUVWcHhQEhN8lyATemUguh+ZTd3/v07qkQRl5MST1qYfXVfRKrXOwFo084Z6uGtE5Gg/AoxC6s4i9Um29XlM17b7kol0tixUAhlOgL3NZYBqAWYivJIG2atRGbro2CSTQvNg+Fub7ibLx8M2v10/t/dP1R3/2+XYBOpr1ESCLh1HAZakK2/PJ9yQmHnMvRUAizFq0z1l6u3T4mYJY4lkdUTZkV36JHhlcn+mQ5eFRVv2w1TjtfsfoM9k/xSm2l6gmwtX6zi6SRHJdU9ziYIh/FIHIwdeILbkgyREs2bGwd4iURmWV/42Wn--U0kLZtq9WUWGspje--xvmf+T8cQhknKYgByMWVQw==
--------------------------------------------------------------------------------
/example/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite. Versions 3.8.0 and up are supported.
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/example/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/example/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join("tmp", "caching-dev.txt").exist?
18 | config.action_controller.perform_caching = true
19 | config.action_controller.enable_fragment_cache_logging = true
20 |
21 | config.cache_store = :memory_store
22 | config.public_file_server.headers = {
23 | "Cache-Control" => "public, max-age=#{2.days.to_i}"
24 | }
25 | else
26 | config.action_controller.perform_caching = false
27 |
28 | config.cache_store = :null_store
29 | end
30 |
31 | # Print deprecation notices to the Rails logger.
32 | config.active_support.deprecation = :log
33 |
34 | # Raise an error on page load if there are pending migrations.
35 | config.active_record.migration_error = :page_load
36 |
37 | # Highlight code that triggered database queries in logs.
38 | config.active_record.verbose_query_logs = true
39 |
40 | # Raises error for missing translations.
41 | # config.action_view.raise_on_missing_translations = true
42 |
43 | # Use an evented file watcher to asynchronously detect changes in source code,
44 | # routes, locales, etc. This feature depends on the listen gem.
45 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
46 | end
47 |
--------------------------------------------------------------------------------
/example/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
24 |
25 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
26 | # config.action_controller.asset_host = 'http://assets.example.com'
27 |
28 | # Specifies the header that your server uses for sending files.
29 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
30 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
31 |
32 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
33 | # config.force_ssl = true
34 |
35 | # Use the lowest log level to ensure availability of diagnostic information
36 | # when problems arise.
37 | config.log_level = :debug
38 |
39 | # Prepend all log lines with the following tags.
40 | config.log_tags = [:request_id]
41 |
42 | # Use a different cache store in production.
43 | # config.cache_store = :mem_cache_store
44 |
45 | # Use a real queuing backend for Active Job (and separate queues per environment).
46 | # config.active_job.queue_adapter = :resque
47 | # config.active_job.queue_name_prefix = "an_app_production"
48 |
49 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
50 | # the I18n.default_locale when a translation cannot be found).
51 | config.i18n.fallbacks = true
52 |
53 | # Send deprecation notices to registered listeners.
54 | config.active_support.deprecation = :notify
55 |
56 | # Use default logging formatter so that PID and timestamp are not suppressed.
57 | config.log_formatter = ::Logger::Formatter.new
58 |
59 | # Use a different logger for distributed setups.
60 | # require 'syslog/logger'
61 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
62 |
63 | if ENV["RAILS_LOG_TO_STDOUT"].present?
64 | logger = ActiveSupport::Logger.new($stdout)
65 | logger.formatter = config.log_formatter
66 | config.logger = ActiveSupport::TaggedLogging.new(logger)
67 | end
68 |
69 | # Do not dump schema after migrations.
70 | config.active_record.dump_schema_after_migration = false
71 |
72 | # Inserts middleware to perform automatic connection switching.
73 | # The `database_selector` hash is used to pass options to the DatabaseSelector
74 | # middleware. The `delay` is used to determine how long to wait after a write
75 | # to send a subsequent read to the primary.
76 | #
77 | # The `database_resolver` class is used by the middleware to determine which
78 | # database is appropriate to use based on the time delay.
79 | #
80 | # The `database_resolver_context` class is used by the middleware to set
81 | # timestamps for the last write to the primary. The resolver uses the context
82 | # class timestamps to determine how long to wait before reading from the
83 | # replica.
84 | #
85 | # By default Rails will store a last write timestamp in the session. The
86 | # DatabaseSelector middleware is designed as such you can define your own
87 | # strategy for connection switching and pass that into the middleware through
88 | # these configuration options.
89 | # config.active_record.database_selector = { delay: 2.seconds }
90 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
91 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
92 | end
93 |
--------------------------------------------------------------------------------
/example/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # The test environment is used exclusively to run your application's
2 | # test suite. You never need to work with it otherwise. Remember that
3 | # your test database is "scratch space" for the test suite and is wiped
4 | # and recreated between test runs. Don't rely on the data there!
5 |
6 | Rails.application.configure do
7 | # Settings specified here will take precedence over those in config/application.rb.
8 |
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot. This avoids loading your whole application
12 | # just for the purpose of running a single test. If you are using a tool that
13 | # preloads Rails for running tests, you may have to set it to true.
14 | config.eager_load = false
15 |
16 | # Configure public file server for tests with Cache-Control for performance.
17 | config.public_file_server.enabled = true
18 | config.public_file_server.headers = {
19 | "Cache-Control" => "public, max-age=3600"
20 | }
21 |
22 | # Show full error reports and disable caching.
23 | config.consider_all_requests_local = true
24 | config.action_controller.perform_caching = false
25 | config.cache_store = :null_store
26 |
27 | # Raise exceptions instead of rendering exception templates.
28 | config.action_dispatch.show_exceptions = false
29 |
30 | # Disable request forgery protection in test environment.
31 | config.action_controller.allow_forgery_protection = false
32 |
33 | # Print deprecation notices to the stderr.
34 | config.active_support.deprecation = :stderr
35 |
36 | # Raises error for missing translations.
37 | # config.action_view.raise_on_missing_translations = true
38 | end
39 |
--------------------------------------------------------------------------------
/example/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/example/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/example/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 | # # If you are using webpack-dev-server then specify webpack-dev-server host
15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
16 |
17 | # # Specify URI for violation reports
18 | # # policy.report_uri "/csp-violation-report-endpoint"
19 | # end
20 |
21 | # If you are using UJS then enable automatic nonce generation
22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
23 |
24 | # Set the nonce only to specific directives
25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
26 |
27 | # Report CSP violations to a specified URI
28 | # For further information see the following documentation:
29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
30 | # Rails.application.config.content_security_policy_report_only = true
31 |
--------------------------------------------------------------------------------
/example/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/example/config/initializers/cypress_rails_initializer.rb:
--------------------------------------------------------------------------------
1 | return unless Rails.env.test?
2 | require "./lib/external_service"
3 |
4 | Rails.application.load_tasks unless defined?(Rake::Task)
5 |
6 | CypressRails.hooks.before_server_start do
7 | # Add our fixtures before the resettable transaction is started
8 | Rake::Task["db:fixtures:load"].invoke
9 | end
10 |
11 | CypressRails.hooks.after_server_start do
12 | # After the server has booted we add the compliment to the existing fixture list!
13 | Compliment.create(text: "This shall be the first.")
14 | if Compliment.count == 4
15 | raise "I cannot run tests without compliments!"
16 | end
17 |
18 | # Start up external service
19 | ExternalService.start_service
20 | end
21 |
22 | CypressRails.hooks.after_transaction_start do
23 | # After each transaction, add this compliment (will be rolled back on reset)
24 | Compliment.create(text: "You are courageous")
25 | end
26 |
27 | CypressRails.hooks.after_state_reset do
28 | if Compliment.count != 4
29 | raise "Wait I was expecting exactly 4 compliments!"
30 | end
31 | end
32 |
33 | CypressRails.hooks.before_server_stop do
34 | # Purge and reload the test database so we don't leave our fixtures in there
35 | Rake::Task["db:test:prepare"].invoke
36 | end
37 |
--------------------------------------------------------------------------------
/example/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/example/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/example/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/example/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/example/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/example/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS", 5)
8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
9 | threads min_threads_count, max_threads_count
10 |
11 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
12 | #
13 | port ENV.fetch("PORT", 3000)
14 |
15 | # Specifies the `environment` that Puma will run in.
16 | #
17 | environment ENV.fetch("RAILS_ENV") { "development" }
18 |
19 | # Specifies the `pidfile` that Puma will use.
20 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
21 |
22 | # Specifies the number of `workers` to boot in clustered mode.
23 | # Workers are forked web server processes. If using threads and workers together
24 | # the concurrency of the application would be max `threads` * `workers`.
25 | # Workers do not work on JRuby or Windows (both of which do not support
26 | # processes).
27 | #
28 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
29 |
30 | # Use the `preload_app!` method when specifying a `workers` number.
31 | # This directive tells Puma to first boot the application and load code
32 | # before forking the application. This takes advantage of Copy On Write
33 | # process behavior so workers use less memory.
34 | #
35 | # preload_app!
36 |
37 | # Allow puma to be restarted by `rails restart` command.
38 | plugin :tmp_restart
39 |
--------------------------------------------------------------------------------
/example/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | get "/an_static_form", to: "forms#static"
3 | get "/external_request", to: "static_pages#external"
4 |
5 | resources :compliments
6 |
7 | root "compliments#index"
8 | end
9 |
--------------------------------------------------------------------------------
/example/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/example/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 |
3 | module.exports = environment
4 |
--------------------------------------------------------------------------------
/example/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/example/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/example/config/webpacker.yml:
--------------------------------------------------------------------------------
1 | # Note: You must restart bin/webpack-dev-server for changes to take effect
2 |
3 | default: &default
4 | source_path: app/javascript
5 | source_entry_path: packs
6 | public_root_path: public
7 | public_output_path: packs
8 | cache_path: tmp/cache/webpacker
9 | check_yarn_integrity: false
10 | webpack_compile_output: false
11 |
12 | # Additional paths webpack should lookup modules
13 | # ['app/assets', 'engine/foo/app/assets']
14 | resolved_paths: []
15 |
16 | # Reload manifest.json on all requests so we reload latest compiled packs
17 | cache_manifest: false
18 |
19 | # Extract and emit a css file
20 | extract_css: false
21 |
22 | static_assets_extensions:
23 | - .jpg
24 | - .jpeg
25 | - .png
26 | - .gif
27 | - .tiff
28 | - .ico
29 | - .svg
30 | - .eot
31 | - .otf
32 | - .ttf
33 | - .woff
34 | - .woff2
35 |
36 | extensions:
37 | - .mjs
38 | - .js
39 | - .sass
40 | - .scss
41 | - .css
42 | - .module.sass
43 | - .module.scss
44 | - .module.css
45 | - .png
46 | - .svg
47 | - .gif
48 | - .jpeg
49 | - .jpg
50 |
51 | development:
52 | <<: *default
53 | compile: true
54 |
55 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
56 | check_yarn_integrity: true
57 |
58 | # Reference: https://webpack.js.org/configuration/dev-server/
59 | dev_server:
60 | https: false
61 | host: localhost
62 | port: 3035
63 | public: localhost:3035
64 | hmr: false
65 | # Inline should be set to true if using HMR
66 | inline: true
67 | overlay: true
68 | compress: true
69 | disable_host_check: true
70 | use_local_ip: false
71 | quiet: false
72 | headers:
73 | 'Access-Control-Allow-Origin': '*'
74 | watch_options:
75 | ignored: '**/node_modules/**'
76 |
77 |
78 | test:
79 | <<: *default
80 | compile: true
81 |
82 | # Compile test packs to a separate directory
83 | public_output_path: packs-test
84 |
85 | production:
86 | <<: *default
87 |
88 | # Production depends on precompilation of packs prior to booting for performance.
89 | compile: false
90 |
91 | # Extract and emit a css file
92 | extract_css: true
93 |
94 | # Cache manifest.json for performance
95 | cache_manifest: true
96 |
--------------------------------------------------------------------------------
/example/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | // setupNodeEvents can be defined in either
5 | // the e2e or component configuration
6 | e2e: {
7 | setupNodeEvents(on, config) {
8 | on('before:browser:launch', (browser = {}, launchOptions) => {
9 | /* ... */
10 | })
11 | },
12 | },
13 | screenshotsFolder: "tmp/cypress_screenshots",
14 | videosFolder: "tmp/cypress_videos",
15 | trashAssetsBeforeRuns: false
16 | })
17 |
--------------------------------------------------------------------------------
/example/cypress/e2e/compliments_test.cy.js:
--------------------------------------------------------------------------------
1 | describe('Compliments', () => {
2 | beforeEach(() => {
3 | cy.request('/cypress_rails_reset_state')
4 | cy.visit('/compliments')
5 | })
6 |
7 | it('renders 3 fixtures + 1 custom compliment', () => {
8 | cy.get('input[value="You make cool things"]')
9 | cy.get('input[value="You are very kind"]')
10 | cy.get('input[value="You motivate others"]')
11 | cy.get('input[value="You are courageous"]')
12 | })
13 |
14 | it('lets me edit the compliments', () => {
15 | cy.get('input[value="You are very kind"]').clear().type('You are SUPER kind{enter}')
16 |
17 | // Shows a confirmation
18 | cy.contains('Yay, you saved a compliment!')
19 |
20 | // Shows the input with the new value
21 | cy.get('input[value="You are very kind"]').should('not.exist')
22 | cy.get('input[value="You are SUPER kind"]')
23 | })
24 |
25 | it('resets the compliments between each test case', () => {
26 | // Make sure the original compliment is still here
27 | cy.get('input[value="You are SUPER kind"]').should('not.exist')
28 | cy.get('input[value="You are very kind"]')
29 |
30 | // Still shows all 4 compliments
31 | cy.get('input[type=text]').should('have.length', 4)
32 | })
33 |
34 | })
35 |
--------------------------------------------------------------------------------
/example/cypress/e2e/external-service.cy.js:
--------------------------------------------------------------------------------
1 | describe('External Service', () => {
2 | it('Fetches data from an external, running server', () => {
3 | cy.visit('/external_request');
4 | cy.get('#external_compliment').should(
5 | 'have.text',
6 | 'Wow, look at you fetch data!'
7 | );
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/example/cypress/e2e/static-html.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | context('Static HTML with no database stuff', () => {
3 | it('Fills a form', () => {
4 | cy.visit('/an_static_form')
5 | cy.get('#name').type('Human Person')
6 | cy.get('#age').select('30-40')
7 | cy.get('input[value=cypress]').check()
8 |
9 | cy.get('#name').should('have.value', 'Human Person')
10 | cy.get('#age').should('have.value', '30')
11 | cy.get('input[value=cypress]').should('be.checked')
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/example/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************
3 | // This example commands.ts shows you how to
4 | // create various custom commands and overwrite
5 | // existing commands.
6 | //
7 | // For more comprehensive examples of custom
8 | // commands please read more here:
9 | // https://on.cypress.io/custom-commands
10 | // ***********************************************
11 | //
12 | //
13 | // -- This is a parent command --
14 | // Cypress.Commands.add('login', (email, password) => { ... })
15 | //
16 | //
17 | // -- This is a child command --
18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
19 | //
20 | //
21 | // -- This is a dual command --
22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
23 | //
24 | //
25 | // -- This will overwrite an existing command --
26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
27 | //
28 | // declare global {
29 | // namespace Cypress {
30 | // interface Chainable {
31 | // login(email: string, password: string): Chainable
32 | // drag(subject: string, options?: Partial): Chainable
33 | // dismiss(subject: string, options?: Partial): Chainable
34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable
35 | // }
36 | // }
37 | // }
38 |
--------------------------------------------------------------------------------
/example/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/e2e.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/example/db/migrate/20200304023531_create_compliments.rb:
--------------------------------------------------------------------------------
1 | class CreateCompliments < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :compliments do |t|
4 | t.string :text
5 | t.timestamps
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/example/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `rails
6 | # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2020_03_04_023531) do
14 | create_table "compliments", force: :cascade do |t|
15 | t.string "text"
16 | t.datetime "created_at", precision: 6, null: false
17 | t.datetime "updated_at", precision: 6, null: false
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/example/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7 | # Character.create(name: 'Luke', movie: movies.first)
8 |
--------------------------------------------------------------------------------
/example/external_service/compliment.json:
--------------------------------------------------------------------------------
1 | {
2 | "complimentText": "Wow, look at you fetch data!"
3 | }
4 |
--------------------------------------------------------------------------------
/example/lib/external_service.rb:
--------------------------------------------------------------------------------
1 | class ExternalService
2 | class << self
3 | def start_service
4 | @job_pid = fork {
5 | exec "yarn start"
6 | }
7 | Process.detach(@job_pid)
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "an_app",
3 | "private": true,
4 | "scripts": {
5 | "start": "http-server external_service --cors"
6 | },
7 | "dependencies": {
8 | "@rails/ujs": "^6.0.0-alpha",
9 | "@rails/webpacker": "5.4.0",
10 | "http-server": "^0.12.3"
11 | },
12 | "version": "0.1.0",
13 | "devDependencies": {
14 | "cypress": "^12.13.0",
15 | "webpack-dev-server": "^3.8.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('postcss-flexbugs-fixes'),
5 | require('postcss-preset-env')({
6 | autoprefixer: {
7 | flexbox: 'no-2009'
8 | },
9 | stage: 3
10 | })
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/example/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/example/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/example/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/example/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testdouble/cypress-rails/86c7cbd440d14de9d45799acf2a83ccc05260c64/example/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/example/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testdouble/cypress-rails/86c7cbd440d14de9d45799acf2a83ccc05260c64/example/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testdouble/cypress-rails/86c7cbd440d14de9d45799acf2a83ccc05260c64/example/public/favicon.ico
--------------------------------------------------------------------------------
/example/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/example/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5 | end
6 |
--------------------------------------------------------------------------------
/example/test/fixtures/compliments.yml:
--------------------------------------------------------------------------------
1 | kind:
2 | text: "You are very kind"
3 |
4 | creative:
5 | text: "You make cool things"
6 |
7 | encouraging:
8 | text: "You motivate others"
9 |
--------------------------------------------------------------------------------
/example/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV["RAILS_ENV"] ||= "test"
2 | require_relative "../config/environment"
3 | require "rails/test_help"
4 |
5 | class ActiveSupport::TestCase
6 | # Run tests in parallel with specified workers
7 | parallelize(workers: :number_of_processors)
8 |
9 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
10 | fixtures :all
11 |
12 | # Add more helper methods to be used by all tests here...
13 | end
14 |
--------------------------------------------------------------------------------
/exe/cypress-rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= "test"
4 | require "pathname"
5 | require "cypress-rails"
6 | require Pathname.new(CypressRails::Config.new.rails_dir).join("config/environment")
7 |
8 | command = ARGV[0]
9 | case command
10 | when "init"
11 | CypressRails::Init.new.call
12 | when "open"
13 | CypressRails::Open.new.call
14 | when "run"
15 | passed = CypressRails::Run.new.call
16 | exit 1 unless passed
17 | end
18 |
--------------------------------------------------------------------------------
/lib/cypress-rails.rb:
--------------------------------------------------------------------------------
1 | require "cypress-rails/version"
2 |
3 | module CypressRails
4 | end
5 |
6 | require "cypress-rails/init"
7 | require "cypress-rails/open"
8 | require "cypress-rails/run"
9 | require "cypress-rails/resets_state"
10 | require "cypress-rails/initializer_hooks"
11 | require "cypress-rails/railtie" if defined?(Rails)
12 |
--------------------------------------------------------------------------------
/lib/cypress-rails/config.rb:
--------------------------------------------------------------------------------
1 | require_relative "env"
2 |
3 | module CypressRails
4 | class Config
5 | attr_accessor :rails_dir, :cypress_dir, :host, :port, :base_path, :transactional_server, :cypress_cli_opts
6 |
7 | def initialize(
8 | rails_dir: Env.fetch("CYPRESS_RAILS_DIR", default: Dir.pwd),
9 | cypress_dir: Env.fetch("CYPRESS_RAILS_CYPRESS_DIR", default: rails_dir),
10 | host: Env.fetch("CYPRESS_RAILS_HOST", default: "127.0.0.1"),
11 | port: Env.fetch("CYPRESS_RAILS_PORT"),
12 | base_path: Env.fetch("CYPRESS_RAILS_BASE_PATH", default: "/"),
13 | transactional_server: Env.fetch("CYPRESS_RAILS_TRANSACTIONAL_SERVER", type: :boolean, default: true),
14 | cypress_cli_opts: Env.fetch("CYPRESS_RAILS_CYPRESS_OPTS", default: "")
15 | )
16 | @rails_dir = rails_dir
17 | @cypress_dir = cypress_dir
18 | @host = host
19 | @port = port
20 | @base_path = base_path
21 | @transactional_server = transactional_server
22 | @cypress_cli_opts = cypress_cli_opts
23 | end
24 |
25 | def to_s
26 | <<~DESC
27 |
28 | cypress-rails configuration:
29 | ============================
30 | CYPRESS_RAILS_DIR.....................#{rails_dir.inspect}
31 | CYPRESS_RAILS_CYPRESS_DIR.............#{cypress_dir.inspect}
32 | CYPRESS_RAILS_HOST....................#{host.inspect}
33 | CYPRESS_RAILS_PORT....................#{port.inspect}
34 | CYPRESS_RAILS_BASE_PATH...............#{base_path.inspect}
35 | CYPRESS_RAILS_TRANSACTIONAL_SERVER....#{transactional_server.inspect}
36 | CYPRESS_RAILS_CYPRESS_OPTS............#{cypress_cli_opts.inspect}
37 |
38 | DESC
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/cypress-rails/env.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | module Env
3 | def self.fetch(name, type: :string, default: nil)
4 | return default unless ENV.key?(name)
5 |
6 | if type == :boolean
7 | no_like_flag = ["", "0", "n", "no", "false"].include?(ENV.fetch(name))
8 | !no_like_flag
9 | else
10 | ENV.fetch(name)
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/cypress-rails/finds_bin.rb:
--------------------------------------------------------------------------------
1 | require "pathname"
2 |
3 | module CypressRails
4 | class FindsBin
5 | LOCAL_PATH = "node_modules/.bin/cypress"
6 |
7 | def call(cypress_dir = Dir.pwd)
8 | local_path = Pathname.new(cypress_dir).join(LOCAL_PATH)
9 | if File.exist?(local_path)
10 | local_path
11 | else
12 | "cypress"
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/cypress-rails/init.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | class Init
3 | DEFAULT_CONFIG = <<~JS
4 | const { defineConfig } = require('cypress')
5 |
6 | module.exports = defineConfig({
7 | // setupNodeEvents can be defined in either
8 | // the e2e or component configuration
9 | e2e: {
10 | setupNodeEvents(on, config) {
11 | on('before:browser:launch', (browser = {}, launchOptions) => {
12 | /* ... */
13 | })
14 | },
15 | },
16 | screenshotsFolder: "tmp/cypress_screenshots",
17 | videosFolder: "tmp/cypress_videos",
18 | trashAssetsBeforeRuns: false
19 | })
20 | JS
21 |
22 | def call(cypress_dir = Config.new.cypress_dir)
23 | config_path = File.join(cypress_dir, "cypress.config.js")
24 | if !File.exist?(config_path)
25 | File.write(config_path, DEFAULT_CONFIG)
26 | puts "Cypress config initialized in `#{config_path}'"
27 | else
28 | warn "Cypress config already exists in `#{config_path}'. Skipping."
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/cypress-rails/initializer_hooks.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | def self.hooks
3 | InitializerHooks.instance
4 | end
5 |
6 | class InitializerHooks
7 | def self.instance
8 | @instance ||= new
9 | end
10 |
11 | def before_server_start(&blk)
12 | register(:before_server_start, blk)
13 | end
14 |
15 | def after_server_start(&blk)
16 | register(:after_server_start, blk)
17 | end
18 |
19 | def after_transaction_start(&blk)
20 | register(:after_transaction_start, blk)
21 | end
22 |
23 | def after_state_reset(&blk)
24 | register(:after_state_reset, blk)
25 | end
26 |
27 | def before_server_stop(&blk)
28 | register(:before_server_stop, blk)
29 | end
30 |
31 | def reset!
32 | @hooks = {}
33 | end
34 |
35 | def run(name)
36 | return unless @hooks[name]
37 | @hooks[name].each do |blk|
38 | blk.call
39 | end
40 | end
41 |
42 | private
43 |
44 | def register(name, blk)
45 | @hooks[name] ||= []
46 | @hooks[name] << blk
47 | end
48 |
49 | def initialize
50 | reset!
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/cypress-rails/launches_cypress.rb:
--------------------------------------------------------------------------------
1 | require_relative "finds_bin"
2 | require_relative "config"
3 | require_relative "initializer_hooks"
4 | require_relative "manages_transactions"
5 | require_relative "starts_rails_server"
6 |
7 | module CypressRails
8 | class LaunchesCypress
9 | def initialize
10 | @initializer_hooks = InitializerHooks.instance
11 | @manages_transactions = ManagesTransactions.instance
12 | @starts_rails_server = StartsRailsServer.new
13 | @finds_bin = FindsBin.new
14 | end
15 |
16 | def call(command, config)
17 | puts config
18 | @initializer_hooks.run(:before_server_start)
19 | if config.transactional_server
20 | @manages_transactions.begin_transaction
21 | end
22 | server = @starts_rails_server.call(
23 | host: config.host,
24 | port: config.port,
25 | transactional_server: config.transactional_server
26 | )
27 | bin = @finds_bin.call(config.cypress_dir)
28 |
29 | set_exit_hooks!(config)
30 |
31 | command = <<~EXEC
32 | CYPRESS_BASE_URL="http://#{server.host}:#{server.port}#{config.base_path}" "#{bin}" #{command} --project "#{config.cypress_dir}" #{config.cypress_cli_opts}
33 | EXEC
34 |
35 | puts "\nLaunching Cypress…\n$ #{command}\n"
36 | system command
37 | end
38 |
39 | private
40 |
41 | def set_exit_hooks!(config)
42 | at_exit do
43 | run_exit_hooks_if_necessary!(config)
44 | end
45 | Signal.trap("INT") do
46 | puts "Exiting cypress-rails…"
47 | exit
48 | end
49 | end
50 |
51 | def run_exit_hooks_if_necessary!(config)
52 | @at_exit_hooks_have_fired ||= false # avoid warning
53 | return if @at_exit_hooks_have_fired
54 |
55 | if config.transactional_server
56 | @manages_transactions.rollback_transaction
57 | end
58 | @initializer_hooks.run(:before_server_stop)
59 |
60 | @at_exit_hooks_have_fired = true
61 | end
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/lib/cypress-rails/manages_transactions.rb:
--------------------------------------------------------------------------------
1 | require_relative "initializer_hooks"
2 |
3 | module CypressRails
4 | class ManagesTransactions
5 | def self.instance
6 | @instance ||= new
7 | end
8 |
9 | def begin_transaction
10 | @connections = gather_connections
11 | @connections.each do |connection|
12 | connection.begin_transaction joinable: false, _lazy: false
13 | connection.pool.lock_thread = true
14 | end
15 |
16 | # When connections are established in the future, begin a transaction too
17 | @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") { |_, _, _, _, payload|
18 | if payload.key?(:spec_name) && (spec_name = payload[:spec_name])
19 | setup_shared_connection_pool
20 |
21 | begin
22 | connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
23 | rescue ActiveRecord::ConnectionNotEstablished
24 | connection = nil
25 | end
26 |
27 | if connection && !@connections.include?(connection)
28 | connection.begin_transaction joinable: false, _lazy: false
29 | connection.pool.lock_thread = true
30 | @connections << connection
31 | end
32 | end
33 | }
34 |
35 | @initializer_hooks.run(:after_transaction_start)
36 | end
37 |
38 | def rollback_transaction
39 | return unless @connections.present?
40 |
41 | ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
42 |
43 | @connections.each do |connection|
44 | connection.rollback_transaction if connection.transaction_open?
45 | connection.pool.lock_thread = false
46 | end
47 | @connections.clear
48 |
49 | ActiveRecord::Base.connection_handler.clear_active_connections!
50 | end
51 |
52 | private
53 |
54 | def initialize
55 | @initializer_hooks = InitializerHooks.instance
56 | end
57 |
58 | def gather_connections
59 | setup_shared_connection_pool
60 |
61 | ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
62 | end
63 |
64 | # Shares the writing connection pool with connections on
65 | # other handlers.
66 | #
67 | # In an application with a primary and replica the test fixtures
68 | # need to share a connection pool so that the reading connection
69 | # can see data in the open transaction on the writing connection.
70 | def setup_shared_connection_pool
71 | return unless ActiveRecord::TestFixtures.respond_to?(:setup_shared_connection_pool)
72 | @legacy_saved_pool_configs ||= Hash.new { |hash, key| hash[key] = {} }
73 | @saved_pool_configs ||= Hash.new { |hash, key| hash[key] = {} }
74 |
75 | ActiveRecord::TestFixtures.instance_method(:setup_shared_connection_pool).bind(self).call
76 | end
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/lib/cypress-rails/open.rb:
--------------------------------------------------------------------------------
1 | require_relative "launches_cypress"
2 | require_relative "config"
3 |
4 | module CypressRails
5 | class Open
6 | def initialize
7 | @launches_cypress = LaunchesCypress.new
8 | end
9 |
10 | def call(config = Config.new)
11 | @launches_cypress.call("open", config)
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/cypress-rails/railtie.rb:
--------------------------------------------------------------------------------
1 | require "rails/railtie"
2 | require "pathname"
3 |
4 | module CypressRails
5 | class Railtie < Rails::Railtie
6 | railtie_name :"cypress-rails"
7 |
8 | rake_tasks do
9 | load Pathname.new(__dir__).join("rake.rb")
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/cypress-rails/rake.rb:
--------------------------------------------------------------------------------
1 | require "pathname"
2 | CLI = Pathname.new(File.dirname(__FILE__)).join("../../exe/cypress-rails")
3 |
4 | desc "Initialize cypress.config.js"
5 | task :"cypress:init" do
6 | system "#{CLI} init"
7 | end
8 |
9 | desc "Open interactive Cypress app for developing tests"
10 | task :"cypress:open" do
11 | trap("SIGINT") {} # avoid traceback
12 | system "#{CLI} open"
13 | end
14 |
15 | desc "Run Cypress tests headlessly"
16 | task :"cypress:run" do
17 | abort unless system "#{CLI} run"
18 | end
19 |
--------------------------------------------------------------------------------
/lib/cypress-rails/resets_state.rb:
--------------------------------------------------------------------------------
1 | require_relative "config"
2 | require_relative "manages_transactions"
3 | require_relative "initializer_hooks"
4 |
5 | module CypressRails
6 | class ResetsState
7 | def initialize
8 | @manages_transactions = ManagesTransactions.instance
9 | @initializer_hooks = InitializerHooks.instance
10 | end
11 |
12 | def call(transactional_server:)
13 | if transactional_server
14 | @manages_transactions.rollback_transaction
15 | @manages_transactions.begin_transaction
16 | end
17 | @initializer_hooks.run(:after_state_reset)
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/cypress-rails/run.rb:
--------------------------------------------------------------------------------
1 | require_relative "launches_cypress"
2 | require_relative "config"
3 |
4 | module CypressRails
5 | class Run
6 | def initialize
7 | @launches_cypress = LaunchesCypress.new
8 | end
9 |
10 | def call(config = Config.new)
11 | @launches_cypress.call("run", config)
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/cypress-rails/server.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "uri"
4 | require "net/http"
5 | require "rack"
6 | require_relative "initializer_hooks"
7 | require_relative "server/middleware"
8 | require_relative "server/checker"
9 | require_relative "server/timer"
10 | require_relative "server/puma"
11 |
12 | module CypressRails
13 | class Server
14 | class << self
15 | def ports
16 | @ports ||= {}
17 | end
18 | end
19 |
20 | attr_reader :app, :host, :port
21 |
22 | def initialize(app,
23 | host:,
24 | port:,
25 | reportable_errors: [Exception],
26 | extra_middleware: [])
27 | @app = app
28 | @extra_middleware = extra_middleware
29 | @server_thread = nil # suppress warnings
30 | @host = host
31 | @reportable_errors = reportable_errors
32 | @port = port
33 | @port ||= Server.ports[port_key]
34 | @port ||= find_available_port(host)
35 | @checker = Checker.new(@host, @port)
36 | @initializer_hooks = InitializerHooks.instance
37 | end
38 |
39 | def reset_error!
40 | middleware.clear_error
41 | end
42 |
43 | def error
44 | middleware.error
45 | end
46 |
47 | def using_ssl?
48 | @checker.ssl?
49 | end
50 |
51 | def responsive?
52 | return false if @server_thread&.join(0)
53 |
54 | res = @checker.request { |http| http.get("/__identify__") }
55 |
56 | res.body == app.object_id.to_s if res.is_a?(Net::HTTPSuccess) || res.is_a?(Net::HTTPRedirection)
57 | rescue SystemCallError, Net::ReadTimeout, OpenSSL::SSL::SSLError
58 | false
59 | end
60 |
61 | def wait_for_pending_requests
62 | timer = Timer.new(60)
63 | while pending_requests?
64 | raise "Requests did not finish in 60 seconds: #{middleware.pending_requests}" if timer.expired?
65 |
66 | sleep 0.01
67 | end
68 | end
69 |
70 | def boot
71 | unless responsive?
72 | Server.ports[port_key] = port
73 |
74 | @server_thread = Thread.new {
75 | Puma.create(middleware, port, host)
76 | }
77 |
78 | timer = Timer.new(60)
79 | until responsive?
80 | raise "Rack application timed out during boot" if timer.expired?
81 |
82 | @server_thread.join(0.1)
83 | @initializer_hooks.run(:after_server_start)
84 | end
85 | end
86 |
87 | self
88 | end
89 |
90 | private
91 |
92 | def middleware
93 | @middleware ||= Middleware.new(app, @reportable_errors, @extra_middleware)
94 | end
95 |
96 | def port_key
97 | app.object_id # as opposed to middleware.object_id if multiple instances
98 | end
99 |
100 | def pending_requests?
101 | middleware.pending_requests?
102 | end
103 |
104 | def find_available_port(host)
105 | server = TCPServer.new(host, 0)
106 | port = server.addr[1]
107 | server.close
108 |
109 | # Workaround issue where some platforms (mac, ???) when passed a host
110 | # of '0.0.0.0' will return a port that is only available on one of the
111 | # ip addresses that resolves to, but the next binding to that port requires
112 | # that port to be available on all ips
113 | server = TCPServer.new(host, port)
114 | port
115 | rescue Errno::EADDRINUSE
116 | retry
117 | ensure
118 | server&.close
119 | end
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/lib/cypress-rails/server/checker.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | class Server
3 | class Checker
4 | TRY_HTTPS_ERRORS = [EOFError, Net::ReadTimeout, Errno::ECONNRESET].freeze
5 |
6 | def initialize(host, port)
7 | @host, @port = host, port
8 | @ssl = false
9 | end
10 |
11 | def request(&block)
12 | ssl? ? https_request(&block) : http_request(&block)
13 | rescue *TRY_HTTPS_ERRORS
14 | res = https_request(&block)
15 | @ssl = true
16 | res
17 | end
18 |
19 | def ssl?
20 | @ssl
21 | end
22 |
23 | private
24 |
25 | def http_request(&block)
26 | make_request(read_timeout: 2, &block)
27 | end
28 |
29 | def https_request(&block)
30 | make_request(**ssl_options, &block)
31 | end
32 |
33 | def make_request(**options, &block)
34 | Net::HTTP.start(@host, @port, options.merge(max_retries: 0), &block)
35 | end
36 |
37 | def ssl_options
38 | {use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE}
39 | end
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/lib/cypress-rails/server/middleware.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | class Server
3 | class Middleware
4 | class Counter
5 | def initialize
6 | @value = []
7 | @mutex = Mutex.new
8 | end
9 |
10 | def increment(uri)
11 | @mutex.synchronize { @value.push(uri) }
12 | end
13 |
14 | def decrement(uri)
15 | @mutex.synchronize { @value.delete_at(@value.index(uri) || @value.length) }
16 | end
17 |
18 | def positive?
19 | @mutex.synchronize { @value.length.positive? }
20 | end
21 |
22 | def value
23 | @mutex.synchronize { @value.dup }
24 | end
25 | end
26 |
27 | attr_reader :error
28 |
29 | def initialize(app, server_errors, extra_middleware = [])
30 | @app = app
31 | @extended_app = extra_middleware.inject(@app) { |ex_app, klass|
32 | klass.new(ex_app)
33 | }
34 | @counter = Counter.new
35 | @server_errors = server_errors
36 | end
37 |
38 | def pending_requests
39 | @counter.value
40 | end
41 |
42 | def pending_requests?
43 | @counter.positive?
44 | end
45 |
46 | def clear_error
47 | @error = nil
48 | end
49 |
50 | def call(env)
51 | if env["PATH_INFO"] == "/__identify__"
52 | [200, {}, [@app.object_id.to_s]]
53 | else
54 | @counter.increment(env["REQUEST_URI"])
55 | begin
56 | @extended_app.call(env)
57 | rescue *@server_errors => e
58 | @error ||= e
59 | raise e
60 | ensure
61 | @counter.decrement(env["REQUEST_URI"])
62 | end
63 | end
64 | end
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/lib/cypress-rails/server/puma.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | class Server
3 | module Puma
4 | def self.create(app, port, host)
5 | require "rack/handler/puma"
6 |
7 | # If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
8 | # Therefore construct and run the Server instance ourselves.
9 | # Rack::Handler::Puma.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
10 | default_options = {Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false}
11 | options = default_options # .merge(options)
12 |
13 | puma_rack_handler = defined?(Rackup::Handler::Puma) ? Rackup::Handler::Puma : Rack::Handler::Puma
14 | conf = puma_rack_handler.config(app, options)
15 | conf.clamp
16 | logger = (defined?(::Puma::LogWriter) ? ::Puma::LogWriter : ::Puma::Events).stdio
17 |
18 | puma_ver = Gem::Version.new(::Puma::Const::PUMA_VERSION)
19 | require_relative "patches/puma_ssl" if (Gem::Version.new("4.0.0")...Gem::Version.new("4.1.0")).cover? puma_ver
20 |
21 | logger.log "Starting Puma..."
22 | logger.log "* Version #{::Puma::Const::PUMA_VERSION} , codename: #{::Puma::Const::CODE_NAME}"
23 | logger.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}"
24 |
25 | ::Puma::Server.new(conf.app, defined?(::Puma::LogWriter) ? nil : logger, conf.options).tap do |s|
26 | s.binder.parse conf.options[:binds], s.respond_to?(:log_writer) ? s.log_writer : s.events
27 | s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads] if s.respond_to?(:min_threads=)
28 | end.run.join
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/cypress-rails/server/timer.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | class Server
3 | class Timer
4 | def initialize(expire_in)
5 | @start = current
6 | @expire_in = expire_in
7 | end
8 |
9 | def expired?
10 | current - @start >= @expire_in
11 | end
12 |
13 | def stalled?
14 | @start == current
15 | end
16 |
17 | private
18 |
19 | def current
20 | Process.clock_gettime(Process::CLOCK_MONOTONIC)
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/cypress-rails/starts_rails_server.rb:
--------------------------------------------------------------------------------
1 | require_relative "tracks_resets"
2 | require_relative "server"
3 |
4 | module CypressRails
5 | class StartsRailsServer
6 | def call(host:, port:, transactional_server:)
7 | configure_rails_to_run_our_state_reset_on_every_request!(transactional_server)
8 | app = create_rack_app
9 | Server.new(app, host: host, port: port).tap do |server|
10 | server.boot
11 | end
12 | end
13 |
14 | def configure_rails_to_run_our_state_reset_on_every_request!(transactional_server)
15 | Rails.application.executor.to_run do
16 | TracksResets.instance.reset_state_if_needed(transactional_server)
17 | end
18 | end
19 |
20 | def create_rack_app
21 | Rack::Builder.new do
22 | map "/cypress_rails_reset_state" do
23 | run lambda { |env|
24 | TracksResets.instance.reset_needed!
25 | [202, {"Content-Type" => "text/plain"}, ["Accepted"]]
26 | }
27 | end
28 | map "/" do
29 | run Rails.application
30 | end
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/cypress-rails/tracks_resets.rb:
--------------------------------------------------------------------------------
1 | require_relative "resets_state"
2 |
3 | module CypressRails
4 | class TracksResets
5 | def self.instance
6 | @instance ||= new
7 | end
8 |
9 | def reset_needed!
10 | @reset_needed = true
11 | end
12 |
13 | def reset_state_if_needed(transactional_server)
14 | if @reset_needed
15 | ResetsState.new.call(transactional_server: transactional_server)
16 | @reset_needed = false
17 | end
18 | end
19 |
20 | private
21 |
22 | def initialize
23 | @reset_needed = false
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/cypress-rails/version.rb:
--------------------------------------------------------------------------------
1 | module CypressRails
2 | VERSION = "0.7.1"
3 | end
4 |
--------------------------------------------------------------------------------
/script/test:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | PS4='[script/test:${LINENO}] $ '
4 | set -euo pipefail
5 | set -x
6 |
7 | echo "---> Installing dependencies"
8 | bundle
9 | cd example
10 | bundle
11 | yarn
12 | cd ..
13 |
14 | echo "---> Running tests"
15 | bundle exec rake
16 | ./script/test_example_app
17 |
18 | bundle exec rake test
19 |
20 | echo "---> Job's done!"
21 |
22 |
--------------------------------------------------------------------------------
/script/test_example_app:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | directory=$1
6 |
7 | cd example
8 | bundle
9 | yarn install
10 |
11 | # test a normal test run
12 | bundle exec rake db:test:prepare
13 | NODE_OPTIONS=--openssl-legacy-provider RAILS_ENV=test bundle exec rake assets:precompile
14 | bundle exec rake cypress:run
15 |
16 | # test that passing options works (by printing help)
17 | if ! bundle exec rake cypress:run CYPRESS_RAILS_CYPRESS_OPTS="-h" | grep -q "Usage: cypress run \[options\]"; then
18 | echo "Failed to pass options to cypress run"
19 | exit 1
20 | fi
21 |
22 |
--------------------------------------------------------------------------------
/test/config_test.rb:
--------------------------------------------------------------------------------
1 | require_relative "test_helper"
2 |
3 | class ConfigTest < Minitest::Test
4 | def test_that_rails_dir_and_cypress_dir_use_default_directory
5 | config = CypressRails::Config.new
6 | expected_directory_path = Dir.pwd
7 |
8 | assert_equal(expected_directory_path, config.rails_dir)
9 | assert_equal(expected_directory_path, config.cypress_dir)
10 | end
11 |
12 | def test_that_rails_dir_and_cypress_dir_can_be_independently_set
13 | mock_env(
14 | "CYPRESS_RAILS_DIR" => "path/to/cypress-rails",
15 | "CYPRESS_RAILS_CYPRESS_DIR" => "path/to/another/cypress/directory"
16 | ) do
17 | config = CypressRails::Config.new
18 |
19 | assert_equal("path/to/cypress-rails", config.rails_dir)
20 | assert_equal("path/to/another/cypress/directory", config.cypress_dir)
21 | end
22 | end
23 |
24 | def test_that_cypress_dir_uses_same_directory_as_rails_dir_when_not_set
25 | mock_env("CYPRESS_RAILS_DIR" => "path/to/cypress-rails") do
26 | config = CypressRails::Config.new
27 |
28 | assert_nil(ENV["CYPRESS_RAILS_CYPRESS_DIR"])
29 | assert_equal("path/to/cypress-rails", config.cypress_dir)
30 | end
31 | end
32 |
33 | private
34 |
35 | def mock_env(partial_env_hash)
36 | old = ENV.to_hash
37 | ENV.update partial_env_hash
38 | begin
39 | yield
40 | ensure
41 | ENV.replace old
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/test/cypress_rails_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class CypressRailsTest < Minitest::Test
4 | def test_that_it_has_a_version_number
5 | refute_nil ::CypressRails::VERSION
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2 | require "cypress-rails"
3 |
4 | require "minitest/autorun"
5 |
--------------------------------------------------------------------------------