cypress-rails 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67672b89666277b05b54d43d809daf48bd66c4c3d4b24bc52792994c378ffc6a
4
- data.tar.gz: 20559b13133e2e32c777bb41b88607d7250ecd2281458fc60cc3ea5dee95cebf
3
+ metadata.gz: 681b1824ae1ff6588b7c118c9887e57a16e86ea4d2b09b0ba732074790a9c6c5
4
+ data.tar.gz: d41e3c0ff8e1a3ea9dcbd3532d70b5939520592bc5de5c9efdcb0bfb30901173
5
5
  SHA512:
6
- metadata.gz: 918352bf0a54236e1fafad16ab19d5f4a9a787278bd82e1386361076b1ba6f189ef2ccafd59d8f2545c4e32b0e34aa80ba33065a15ae2958df48e8078e1b685f
7
- data.tar.gz: eccdf2b27a8f41d2e158060596856ec455d2d66f2c59b114db70aa47265a5f8ba3030fd435fafe5c8d78cc4390f5a26ef2cbd05f8916fb6a66ae4a761085f4e7
6
+ metadata.gz: 36a951154b585e565a47d3f90132f10c060b03a7e232e6c7e93a5b71779b902e655577afa1d8826f1522eadc559aee64b8f9afd1c0ccd766295a81c3bbe0c1aa
7
+ data.tar.gz: 750298885263db32dd288eeaa693157e9cd4e1777c7e2f3843020d4d3827dff51d44c33e81451e4af7ace3e5e5cb5283dfd64f5b347e5b9d71bacf2baac4659a
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # CHANGELOG
2
+
3
+ ## 0.1.0
4
+
5
+ * **[Breaking]** Remove `CypressRails::TestCase`. Use `rake cypress:run` instead
6
+ * **[Breaking]** cypress-rails now starts a transaction immediately after
7
+ launching the server, which could result in other processes not being able
8
+ to observe your changes. To revert to the old behavior, set the env var
9
+ `CYPRESS_RAILS_TRANSACTIONAL_SERVER=false`
10
+ * Add configuration variables `CYPRESS_RAILS_DIR`,
11
+ `CYPRESS_RAILS_TRANSACTIONAL_SERVER`. Rename port and Cypress CLI forwarding
12
+ to `CYPRESS_RAILS_PORT` and `CYPRESS_RAILS_CYPRESS_OPTS`
13
+ * Add test data configuration hooks (to be run in an initializer):
14
+ * `CypressRails.hooks.before_server_start`
15
+ * `CypressRails.hooks.after_transaction_start`
16
+ * `CypressRails.hooks.after_state_reset` - after a transaction rollback
17
+ * `CypressRails.hooks.before_server_stop` - called in an `at_exit` hook
18
+
19
+ ## 0.0.4
20
+
21
+ * Started a changelog
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cypress-rails (0.0.4)
4
+ cypress-rails (0.1.0)
5
5
  capybara
6
6
  railties (>= 5.2.0)
7
7
  selenium-webdriver
@@ -9,29 +9,30 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actionpack (5.2.3)
13
- actionview (= 5.2.3)
14
- activesupport (= 5.2.3)
15
- rack (~> 2.0)
12
+ actionpack (6.0.2.1)
13
+ actionview (= 6.0.2.1)
14
+ activesupport (= 6.0.2.1)
15
+ rack (~> 2.0, >= 2.0.8)
16
16
  rack-test (>= 0.6.3)
17
17
  rails-dom-testing (~> 2.0)
18
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
19
- actionview (5.2.3)
20
- activesupport (= 5.2.3)
18
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
19
+ actionview (6.0.2.1)
20
+ activesupport (= 6.0.2.1)
21
21
  builder (~> 3.1)
22
22
  erubi (~> 1.4)
23
23
  rails-dom-testing (~> 2.0)
24
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
25
- activesupport (5.2.3)
24
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
25
+ activesupport (6.0.2.1)
26
26
  concurrent-ruby (~> 1.0, >= 1.0.2)
27
27
  i18n (>= 0.7, < 2)
28
28
  minitest (~> 5.1)
29
29
  tzinfo (~> 1.1)
30
+ zeitwerk (~> 2.2)
30
31
  addressable (2.7.0)
31
32
  public_suffix (>= 2.0.2, < 5.0)
32
33
  ast (2.4.0)
33
- builder (3.2.3)
34
- capybara (3.29.0)
34
+ builder (3.2.4)
35
+ capybara (3.31.0)
35
36
  addressable
36
37
  mini_mime (>= 0.1.3)
37
38
  nokogiri (~> 1.8)
@@ -40,66 +41,67 @@ GEM
40
41
  regexp_parser (~> 1.5)
41
42
  xpath (~> 3.2)
42
43
  childprocess (3.0.0)
43
- concurrent-ruby (1.1.5)
44
- crass (1.0.5)
45
- erubi (1.8.0)
46
- i18n (1.6.0)
44
+ concurrent-ruby (1.1.6)
45
+ crass (1.0.6)
46
+ erubi (1.9.0)
47
+ i18n (1.8.2)
47
48
  concurrent-ruby (~> 1.0)
48
- jaro_winkler (1.5.3)
49
- loofah (2.3.1)
49
+ jaro_winkler (1.5.4)
50
+ loofah (2.4.0)
50
51
  crass (~> 1.0.2)
51
52
  nokogiri (>= 1.5.9)
52
53
  method_source (0.9.2)
53
54
  mini_mime (1.0.2)
54
55
  mini_portile2 (2.4.0)
55
- minitest (5.11.3)
56
- nokogiri (1.10.8)
56
+ minitest (5.14.0)
57
+ nokogiri (1.10.9)
57
58
  mini_portile2 (~> 2.4.0)
58
- parallel (1.17.0)
59
- parser (2.6.4.1)
59
+ parallel (1.19.1)
60
+ parser (2.7.0.4)
60
61
  ast (~> 2.4.0)
61
- public_suffix (4.0.1)
62
- rack (2.0.8)
62
+ public_suffix (4.0.3)
63
+ rack (2.2.2)
63
64
  rack-test (1.1.0)
64
65
  rack (>= 1.0, < 3)
65
66
  rails-dom-testing (2.0.3)
66
67
  activesupport (>= 4.2.0)
67
68
  nokogiri (>= 1.6)
68
- rails-html-sanitizer (1.2.0)
69
- loofah (~> 2.2, >= 2.2.2)
70
- railties (5.2.3)
71
- actionpack (= 5.2.3)
72
- activesupport (= 5.2.3)
69
+ rails-html-sanitizer (1.3.0)
70
+ loofah (~> 2.3)
71
+ railties (6.0.2.1)
72
+ actionpack (= 6.0.2.1)
73
+ activesupport (= 6.0.2.1)
73
74
  method_source
74
75
  rake (>= 0.8.7)
75
- thor (>= 0.19.0, < 2.0)
76
+ thor (>= 0.20.3, < 2.0)
76
77
  rainbow (3.0.0)
77
78
  rake (13.0.1)
78
- regexp_parser (1.6.0)
79
- rubocop (0.72.0)
79
+ regexp_parser (1.7.0)
80
+ rubocop (0.79.0)
80
81
  jaro_winkler (~> 1.5.1)
81
82
  parallel (~> 1.10)
82
- parser (>= 2.6)
83
+ parser (>= 2.7.0.1)
83
84
  rainbow (>= 2.2.2, < 4.0)
84
85
  ruby-progressbar (~> 1.7)
85
86
  unicode-display_width (>= 1.4.0, < 1.7)
86
- rubocop-performance (1.4.1)
87
+ rubocop-performance (1.5.2)
87
88
  rubocop (>= 0.71.0)
88
89
  ruby-progressbar (1.10.1)
89
90
  rubyzip (2.2.0)
90
91
  selenium-webdriver (3.142.7)
91
92
  childprocess (>= 0.5, < 4.0)
92
93
  rubyzip (>= 1.2.2)
93
- standard (0.1.3)
94
- rubocop (~> 0.72.0)
95
- rubocop-performance (~> 1.4.0)
94
+ standard (0.2.0)
95
+ rubocop (~> 0.79.0)
96
+ rubocop-performance (~> 1.5.1)
96
97
  thor (1.0.1)
97
98
  thread_safe (0.3.6)
98
- tzinfo (1.2.5)
99
+ tzinfo (1.2.6)
99
100
  thread_safe (~> 0.1)
100
- unicode-display_width (1.6.0)
101
+ unicode-display_width (1.6.1)
101
102
  xpath (3.2.0)
102
103
  nokogiri (~> 1.8)
104
+ zeitwerk (2.3.0)
103
105
 
104
106
  PLATFORMS
105
107
  ruby
@@ -111,7 +113,7 @@ DEPENDENCIES
111
113
  cypress-rails!
112
114
  minitest (~> 5.0)
113
115
  rake (~> 13.0)
114
- standard (~> 0.1.3)
116
+ standard (>= 0.2.0)
115
117
 
116
118
  BUNDLED WITH
117
119
  1.17.3
data/README.md CHANGED
@@ -129,16 +129,16 @@ So, by running either:
129
129
  $ cypress-rails open
130
130
  ```
131
131
 
132
- Or, if you don't mind the extra cost of loading rake just so it can call
133
- `system`:
132
+ Or:
134
133
 
135
134
  ```
136
135
  $ rake cypress:open
137
136
  ```
138
137
 
139
- Add tests to `cypress/integration`. Simply click a test file in the Cypress
140
- application window to launch the test in a browser. Each time you save the test
141
- file, it will re-run itself.
138
+ Any JavaScript files added to `cypress/integration` will be identified by
139
+ Cypress as tests. Simply click a test file in the Cypress application window to
140
+ launch the test in a browser. Each time you save the test file, it will re-run
141
+ itself.
142
142
 
143
143
  ### Run tests headlessly with `cypress run`
144
144
 
@@ -155,50 +155,116 @@ Or, with rake:
155
155
  $ rake cypress:run
156
156
  ```
157
157
 
158
- ### Pass options to cypress run or open
158
+ ## Managing your test data
159
159
 
160
- Options can be provided to `cypress run` and `cypress open` using the
161
- `CYPRESS_OPTS` environment variable
160
+ The tricky thing about browser tests is that they usually depend on some test
161
+ data being available with which to exercise the app efficiently. Because cypress
162
+ is a JavaScript-based tool and can't easily manipulate your Rails app directly,
163
+ cypress-rails provides a number of hooks that you can use to manage your test
164
+ data.
162
165
 
163
- ```
164
- rake cypress:run CYPRESS_OPTS="--record --key=abc123 --parallel"
165
- ```
166
+ Here's what a `config/initializers/cypress_rails.rb` initializer might look
167
+ like:
166
168
 
167
- ### Write Ruby tests that wrap and invoke your cypress tests
169
+ ```ruby
170
+ return unless Rails.env.test?
168
171
 
169
- You can also extend a provided `CypressRails::TestCase`, which itself inherits
170
- from Rails' built-in `ActionDispatch::SystemTestCase`.
172
+ CypressRails.hooks.before_server_start do
173
+ # Called once, before ether the transaction or the server is started
174
+ end
171
175
 
172
- That means you can add a test named `test/system/cypress_test.rb`:
176
+ CypressRails.hooks.after_transaction_start do
177
+ # Called after the transaction is started (at launch and after each reset)
178
+ end
173
179
 
174
- ```ruby
175
- require "test_helper"
180
+ CypressRails.hooks.after_state_reset do
181
+ # Triggered after `/cypress_rails_reset_state` is called
182
+ end
176
183
 
177
- class CypressTest < CypressRails::TestCase
178
- test_locator "cypress/integration/**/*.js"
184
+ CypressRails.hooks.before_server_stop do
185
+ # Called once, at_exit
179
186
  end
180
187
  ```
181
188
 
182
- And it will run alongside all the rest of your system tests. Because this would
183
- be invoked by your existing test scripts, you can benefit from whatever custom
184
- test helpers (e.g. database setup, test coverage, etc.) your other full-stack
185
- tests need, as well as specifying custom `setup` and `teardown` directives.
189
+ (You can find [an
190
+ example
191
+ initializer](/example/an_app/config/initializers/cypress_rails_initializer.rb)
192
+ in this repo.)
186
193
 
187
- Each Cypress file matched by the `test_locator` is translated to a single test
188
- case, which—while slightly inefficient, as it spools Cypress up and down
189
- multiple times—also makes it easy to handle each Cypress file as you would any
190
- other Ruby test. It also allows for CLI usage like this (where the test name is
191
- an expansion of the file location with the path separators replaced with `_`):
194
+ The gem also provides a special route on the test server:
195
+ `/cypress_rails_reset_state`. Each time it's called, cypress-rails will do
196
+ two things at the beginning of the next request received by the Rails app:
192
197
 
198
+ * If `CYPRESS_RAILS_TRANSACTIONAL_SERVER` is enabled, roll back the transaction,
199
+ effectively resetting the application state to whatever it was at the start of
200
+ the test run
201
+
202
+ * Trigger any `after_state_reset` hooks you've configured (regardless of the
203
+ transactional server setting)
204
+
205
+ This way, you can easily instruct the server to reset its test state from your
206
+ Cypress tests like so:
207
+
208
+ ```js
209
+ beforeEach(() =>
210
+ cy.request('/cypress_rails_reset_state')
211
+ )
193
212
  ```
194
- $ rails test test/system --name test_cypress_integration_send_invoice_js
195
- ```
196
213
 
197
- **WARNING**: keep in mind that any custom Ruby code you add before or after each
198
- Cypress test is run in the context of a `CypressRails::TestCase` will _not_ be
199
- run when developing with `cypress open`! That means this is probably not the
200
- most rock-solid strategy for consistent test behavior when it comes to things
201
- like test data management.
214
+ (Remember, in Cypress, `before` is a before-all hook and `beforeEach` is run
215
+ between each test case!)
216
+
217
+ ## Configuration
218
+
219
+ ### Environment variables
220
+
221
+ The cypress-rails gem is configured entirely via environment variables. If you
222
+ find yourself repeating a number of verbose environment variables as you run
223
+ your tests, consider invoking the gem from a custom script or setting your
224
+ preferred environment variables project-wide using a tool like
225
+ [dotenv](https://github.com/bkeepers/dotenv).
226
+
227
+
228
+ * **CYPRESS_RAILS_DIR** (default: `Dir.pwd`) the directory of your project
229
+ * **CYPRESS_RAILS_PORT** (default: _a random available port_) the port to run
230
+ the Rails test server on
231
+ * **CYPRESS_RAILS_TRANSACTIONAL_SERVER** (default: `true`) when true, will start
232
+ a transaction on all database connections before launching the server. In
233
+ general this means anything done during `cypress open` or `cypress run` will
234
+ be rolled back on exit (similar to running a Rails System test)
235
+ * **CYPRESS_RAILS_CYPRESS_OPTS** (default: _none_) any options you want to
236
+ forward to the Cypress CLI when running its `open` or `run` commands
237
+
238
+ ### Initializer hooks
239
+
240
+ ### before_server_start
241
+
242
+ Pass a block to `CypressRails.hooks.before_server_start` to register a hook that
243
+ will execute before the server or any transaction has been started. If you use
244
+ Rails fixtures, it may make sense to load them here, so they don't need to be
245
+ re-inserted for each request
246
+
247
+ ### after_transaction_start
248
+
249
+ If there's any custom behavior or state management you want to do inside the
250
+ transaction (so that it's also rolled back each time a reset is triggered),
251
+ pass a block to `CypressRails.hooks.after_transaction_start`.
252
+
253
+ ### after_state_reset
254
+
255
+ Every time the test server receives an HTTP request at
256
+ `/cypress_rails_reset_state`, the transaction will be rolled back (if
257
+ `CYPRESS_RAILS_TRANSACTIONAL_SERVER` is enabled) and the `after_state_reset`
258
+ hook will be triggered. To set up the hook, pass a block to
259
+ `CypressRails.hooks.after_state_reset`.
260
+
261
+ ### before_server_stop
262
+
263
+ In case you've made any permanent changes to your test database that could
264
+ pollute other test suites or scripts, you can use the `before_server_stop` to
265
+ (assuming everything exits gracefully) clean things up and restore the state
266
+ of your test database. To set up the hook, pass a block to
267
+ `CypressRails.hooks.before_server_stop`.
202
268
 
203
269
  ### Setting up continuous integration
204
270
 
@@ -251,9 +317,3 @@ jobs:
251
317
  - run: bin/rake cypress:run
252
318
  ```
253
319
 
254
- ## Configuration
255
-
256
- You can change the behavior of this gem by setting these environment variables:
257
-
258
- * **RAILS_CYPRESS_PORT**: the port to run the Rails test server on (defaults to
259
- a random available port
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "bundler", "~> 1.17"
29
29
  spec.add_development_dependency "rake", "~> 13.0"
30
30
  spec.add_development_dependency "minitest", "~> 5.0"
31
- spec.add_development_dependency "standard", "~> 0.1.3"
31
+ spec.add_development_dependency "standard", ">= 0.2.0"
32
32
  spec.add_development_dependency "actionpack"
33
33
  spec.add_development_dependency "capybara"
34
34
  end
data/exe/cypress-rails CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  ENV["RAILS_ENV"] = "test"
4
4
  require "pathname"
5
- require Pathname.new(Dir.pwd).join("config/environment")
6
5
  require "cypress-rails"
6
+ require Pathname.new(CypressRails::Config.new.dir).join("config/environment")
7
7
 
8
8
  command = ARGV[0]
9
9
  case command
@@ -0,0 +1,27 @@
1
+ require_relative "env"
2
+
3
+ module CypressRails
4
+ class Config < Struct.new(:dir, :port, :transactional_server, :cypress_cli_opts, keyword_init: true)
5
+ def initialize(
6
+ dir: Env.fetch("CYPRESS_RAILS_DIR", default: Dir.pwd),
7
+ port: Env.fetch("CYPRESS_RAILS_PORT"),
8
+ transactional_server: Env.fetch("CYPRESS_RAILS_TRANSACTIONAL_SERVER", type: :boolean, default: true),
9
+ cypress_cli_opts: Env.fetch("CYPRESS_RAILS_CYPRESS_OPTS", default: "")
10
+ )
11
+ super
12
+ end
13
+
14
+ def to_s
15
+ <<~DESC
16
+
17
+ cypress-rails configuration:
18
+ ============================
19
+ CYPRESS_RAILS_DIR.....................#{dir.inspect}
20
+ CYPRESS_RAILS_PORT....................#{port.inspect}
21
+ CYPRESS_RAILS_TRANSACTIONAL_SERVER....#{transactional_server.inspect}
22
+ CYPRESS_RAILS_CYPRESS_OPTS............#{cypress_cli_opts.inspect}
23
+
24
+ DESC
25
+ end
26
+ end
27
+ end
@@ -1,6 +1,6 @@
1
1
  module CypressRails
2
2
  class ConfiguresTestServer
3
- def call(port: ENV["RAILS_CYPRESS_PORT"])
3
+ def call(port:)
4
4
  require "capybara"
5
5
  require "selenium-webdriver"
6
6
  Capybara.server_port = port || find_available_port
@@ -0,0 +1,17 @@
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
+ if ["", "0", "n", "no", "false"].include?(ENV.fetch(name))
8
+ false
9
+ else
10
+ true
11
+ end
12
+ else
13
+ ENV.fetch(name)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -3,7 +3,7 @@ module CypressRails
3
3
  DEFAULT_CONFIG = {
4
4
  "screenshotsFolder" => "tmp/cypress_screenshots",
5
5
  "videosFolder" => "tmp/cypress_videos",
6
- "trashAssetsBeforeRuns" => false,
6
+ "trashAssetsBeforeRuns" => false
7
7
  }
8
8
 
9
9
  def call(dir = Dir.pwd)
@@ -0,0 +1,49 @@
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_transaction_start(&blk)
16
+ register(:after_transaction_start, blk)
17
+ end
18
+
19
+ def after_state_reset(&blk)
20
+ register(:after_state_reset, blk)
21
+ end
22
+
23
+ def before_server_stop(&blk)
24
+ register(:before_server_stop, blk)
25
+ end
26
+
27
+ def reset!
28
+ @hooks = {}
29
+ end
30
+
31
+ def run(name)
32
+ return unless @hooks[name]
33
+ @hooks[name].each do |blk|
34
+ blk.call
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def register(name, blk)
41
+ @hooks[name] ||= []
42
+ @hooks[name] << blk
43
+ end
44
+
45
+ def initialize
46
+ reset!
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,36 @@
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.to_s
18
+ @initializer_hooks.run(:before_server_start)
19
+ if config.transactional_server
20
+ @manages_transactions.begin_transaction
21
+ end
22
+ @starts_rails_server.call(
23
+ port: config.port,
24
+ transactional_server: config.transactional_server
25
+ )
26
+ bin = @finds_bin.call(config.dir)
27
+ at_exit do
28
+ @initializer_hooks.run(:before_server_stop)
29
+ end
30
+
31
+ system <<~EXEC
32
+ CYPRESS_BASE_URL=http://#{Capybara.server_host}:#{Capybara.server_port} #{bin} #{command} --project "#{config.dir}" #{config.cypress_cli_opts}
33
+ EXEC
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,85 @@
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 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.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
+ writing_handler = ActiveRecord::Base.connection_handler
72
+
73
+ ActiveRecord::Base.connection_handlers.values.each do |handler|
74
+ if handler != writing_handler
75
+ handler.connection_pool_list.each do |pool|
76
+ name = pool.spec.name
77
+ writing_connection = writing_handler.retrieve_connection_pool(name)
78
+ return nil unless writing_connection
79
+ handler.send(:owner_to_pool)[name] = writing_connection
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,20 +1,14 @@
1
- require_relative "finds_bin"
2
- require_relative "starts_rails_server"
1
+ require_relative "launches_cypress"
2
+ require_relative "config"
3
3
 
4
4
  module CypressRails
5
5
  class Open
6
6
  def initialize
7
- @starts_rails_server = StartsRailsServer.new
8
- @finds_bin = FindsBin.new
7
+ @launches_cypress = LaunchesCypress.new
9
8
  end
10
9
 
11
- def call(dir: Dir.pwd, port: ENV["RAILS_CYPRESS_PORT"], opts: ENV["CYPRESS_OPTS"])
12
- @starts_rails_server.call(dir: dir, port: port)
13
- bin = @finds_bin.call(dir)
14
-
15
- system <<~EXEC
16
- CYPRESS_BASE_URL=http://#{Capybara.server_host}:#{Capybara.server_port} #{bin} open --project "#{dir}" #{opts}
17
- EXEC
10
+ def call(config = Config.new)
11
+ @launches_cypress.call("open", config)
18
12
  end
19
13
  end
20
14
  end
@@ -8,6 +8,10 @@ end
8
8
 
9
9
  desc "Open interactive Cypress app for developing tests"
10
10
  task :"cypress:open" do
11
+ trap("SIGINT") do
12
+ puts "Exiting cypress:open"
13
+ exit
14
+ end
11
15
  system "#{CLI} open"
12
16
  end
13
17
 
@@ -0,0 +1,20 @@
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
@@ -1,20 +1,14 @@
1
- require_relative "finds_bin"
2
- require_relative "starts_rails_server"
1
+ require_relative "launches_cypress"
2
+ require_relative "config"
3
3
 
4
4
  module CypressRails
5
5
  class Run
6
6
  def initialize
7
- @starts_rails_server = StartsRailsServer.new
8
- @finds_bin = FindsBin.new
7
+ @launches_cypress = LaunchesCypress.new
9
8
  end
10
9
 
11
- def call(dir: Dir.pwd, port: ENV["RAILS_CYPRESS_PORT"], opts: ENV["CYPRESS_OPTS"])
12
- @starts_rails_server.call(dir: dir, port: port)
13
- bin = @finds_bin.call(dir)
14
-
15
- system <<~EXEC
16
- CYPRESS_BASE_URL=http://#{Capybara.server_host}:#{Capybara.server_port} #{bin} run --project "#{dir}" #{opts}
17
- EXEC
10
+ def call(config = Config.new)
11
+ @launches_cypress.call("run", config)
18
12
  end
19
13
  end
20
14
  end
@@ -1,4 +1,5 @@
1
1
  require_relative "configures_test_server"
2
+ require_relative "tracks_resets"
2
3
 
3
4
  module CypressRails
4
5
  class StartsRailsServer
@@ -6,27 +7,51 @@ module CypressRails
6
7
  @configures_test_server = ConfiguresTestServer.new
7
8
  end
8
9
 
9
- def call(dir:, port:)
10
+ def call(port:, transactional_server:)
10
11
  @configures_test_server.call(port: port)
12
+ configure_driver!
13
+ configure_rails_to_run_our_state_reset_on_every_request!(transactional_server)
14
+ Capybara.app = create_capybara_rack_app
15
+ start_system_testing_server!
16
+ Capybara.current_session
17
+ end
18
+
19
+ private
11
20
 
21
+ def configure_driver!
22
+ # this is only necessary b/c we're piggybacking on the capybara server
12
23
  require "action_dispatch/system_testing/driver"
13
24
  require "action_dispatch/system_testing/browser"
14
25
  ActionDispatch::SystemTesting::Driver.new(:selenium, {
15
26
  using: :headless_chrome,
16
27
  screen_size: [1400, 1400],
17
- options: {},
28
+ options: {}
18
29
  }).use
30
+ end
19
31
 
20
- Capybara.app = Rack::Builder.new do
32
+ def configure_rails_to_run_our_state_reset_on_every_request!(transactional_server)
33
+ Rails.application.executor.to_run do
34
+ TracksResets.instance.reset_state_if_needed(transactional_server)
35
+ end
36
+ end
37
+
38
+ def create_capybara_rack_app
39
+ Rack::Builder.new do
40
+ map "/cypress_rails_reset_state" do
41
+ run lambda { |env|
42
+ TracksResets.instance.reset_needed!
43
+ [202, {"Content-Type" => "text/plain"}, ["Accepted"]]
44
+ }
45
+ end
21
46
  map "/" do
22
47
  run Rails.application
23
48
  end
24
49
  end
50
+ end
25
51
 
52
+ def start_system_testing_server!
26
53
  require "action_dispatch/system_testing/server"
27
54
  ActionDispatch::SystemTesting::Server.new.run
28
-
29
- Capybara.current_session
30
55
  end
31
56
  end
32
57
  end
@@ -0,0 +1,26 @@
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
@@ -1,3 +1,3 @@
1
1
  module CypressRails
2
- VERSION = "0.0.4"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/cypress-rails.rb CHANGED
@@ -6,5 +6,6 @@ end
6
6
  require "cypress-rails/init"
7
7
  require "cypress-rails/open"
8
8
  require "cypress-rails/run"
9
- require "cypress-rails/test_case"
9
+ require "cypress-rails/resets_state"
10
+ require "cypress-rails/initializer_hooks"
10
11
  require "cypress-rails/railtie" if defined?(Rails)
@@ -12,7 +12,7 @@ yarn install
12
12
  bundle exec rake cypress:run
13
13
 
14
14
  # test that passing options works (by printing help)
15
- if ! bundle exec rake cypress:run CYPRESS_OPTS="-h" | grep -q "Usage: run \[options\]"; then
15
+ if ! bundle exec rake cypress:run CYPRESS_RAILS_CYPRESS_OPTS="-h" | grep -q "Usage: cypress run \[options\]"; then
16
16
  echo "Failed to pass options to cypress run"
17
17
  exit 1
18
18
  fi
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cypress-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-01 00:00:00.000000000 Z
11
+ date: 2020-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: standard
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: 0.1.3
103
+ version: 0.2.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: 0.1.3
110
+ version: 0.2.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: actionpack
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -148,6 +148,7 @@ files:
148
148
  - ".gitignore"
149
149
  - ".standard.yml"
150
150
  - ".travis.yml"
151
+ - CHANGELOG.md
151
152
  - Gemfile
152
153
  - Gemfile.lock
153
154
  - LICENSE.txt
@@ -158,15 +159,21 @@ files:
158
159
  - cypress-rails.gemspec
159
160
  - exe/cypress-rails
160
161
  - lib/cypress-rails.rb
162
+ - lib/cypress-rails/config.rb
161
163
  - lib/cypress-rails/configures_test_server.rb
164
+ - lib/cypress-rails/env.rb
162
165
  - lib/cypress-rails/finds_bin.rb
163
166
  - lib/cypress-rails/init.rb
167
+ - lib/cypress-rails/initializer_hooks.rb
168
+ - lib/cypress-rails/launches_cypress.rb
169
+ - lib/cypress-rails/manages_transactions.rb
164
170
  - lib/cypress-rails/open.rb
165
171
  - lib/cypress-rails/railtie.rb
166
172
  - lib/cypress-rails/rake.rb
173
+ - lib/cypress-rails/resets_state.rb
167
174
  - lib/cypress-rails/run.rb
168
175
  - lib/cypress-rails/starts_rails_server.rb
169
- - lib/cypress-rails/test_case.rb
176
+ - lib/cypress-rails/tracks_resets.rb
170
177
  - lib/cypress-rails/version.rb
171
178
  - script/test_example_app
172
179
  homepage: https://github.com/testdouble/cypress-rails
@@ -1,34 +0,0 @@
1
- require "action_dispatch/system_test_case"
2
-
3
- require_relative "configures_test_server"
4
-
5
- module CypressRails
6
- class TestCase < ActionDispatch::SystemTestCase
7
- driven_by :selenium_headless
8
-
9
- def setup
10
- @@__cypress_rails_configures_test_server ||= ConfiguresTestServer.new.call
11
- ensure
12
- super
13
- end
14
-
15
- def self.test_locator(path_or_globs)
16
- Dir[path_or_globs].map do |path|
17
- test_name = "test_#{path.gsub(/[\/\.:\\]/, "_")}"
18
- define_method test_name do
19
- bin = FindsBin.new.call
20
- command = "CYPRESS_BASE_URL=http://#{Capybara.server_host}:#{Capybara.server_port} #{bin} run --spec \"#{path}\""
21
- unless system(command)
22
- raise <<~ERROR
23
- Cypress test failed. Try again with:
24
- $ rails test test/system --name #{test_name}
25
-
26
- Underlying Cypress command run was:
27
- $ #{command}
28
- ERROR
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end