cypress-on-rails 1.19.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dcd66d2ef6067166c4e132ca63c09961b26bc0d246063621212fb18fd08c683
4
- data.tar.gz: 79307a5b690fbc8b658d2d7c2eecf65f50ea7c883436ba2fbe8299dde44f9528
3
+ metadata.gz: 9c135e18e23ddeea1d0d2bbd246de9147b9ce42969bd862e51f1e3dec39abbe0
4
+ data.tar.gz: a295806731243324fbfda475ec6be6a6c4bce60651755b278431afd722501cab
5
5
  SHA512:
6
- metadata.gz: 39cf1fd4210c02e3f42b1fa0fb570fd8d5c9bbea19307b71a88d125a772a6f5188a282dd9fcec7cd0c648ff1cad61a8148c6b091f9dd897babee5f63283f06b2
7
- data.tar.gz: 1353e78c41a987613cec39d906b1db1ed7a28b7506236ebd4b6ed30c3dd8e84268fe158b58afd31e7131f9d7465443087d51c7fb281c89f1d82bd8b0d0920d76
6
+ metadata.gz: 501aebab098ec19fbec53e3f7ea0ef8adc966c0d4ec2d60a0c424c45e369ad652899270b5e411376f5e582ef8a29acc3d02814cccee85654fe989bdc0aad9c1f
7
+ data.tar.gz: f833268a8c93e4853fbb28d5b26f653ae741817c4314756975ebd46cac69b6791ca0d436a1d4ab20c9f0be88d4e14b4e399d3d59326cbaf01fb4f2d0b57f24d4
data/CHANGELOG.md CHANGED
@@ -7,6 +7,84 @@ This project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Fixed
11
+ * **BREAKING: Generator folder structure**: Fixed install generator to create `e2e_helper.rb` and `app_commands/` at the install folder root (e.g., `e2e/`) instead of inside the framework subdirectory (e.g., `e2e/cypress/`). This ensures compatibility between Cypress/Playwright config file location and middleware expectations. [#201]
12
+
13
+ ### Migration Guide for Folder Structure Change
14
+
15
+ #### Breaking Change Notice
16
+ If you previously ran the install generator (versions prior to 1.20.0), the file structure created was incorrect. The generator placed helper and command files in the framework subdirectory when they should be at the install folder root.
17
+
18
+ **Old (Incorrect) Structure:**
19
+ ```
20
+ e2e/
21
+ cypress.config.js
22
+ cypress/
23
+ e2e_helper.rb ← Wrong location
24
+ app_commands/ ← Wrong location
25
+ support/
26
+ e2e/
27
+ ```
28
+
29
+ **New (Correct) Structure:**
30
+ ```
31
+ e2e/
32
+ cypress.config.js
33
+ e2e_helper.rb ← Correct location
34
+ app_commands/ ← Correct location
35
+ fixtures/
36
+ vcr_cassettes/ ← VCR cassettes also at root
37
+ cypress/
38
+ support/
39
+ e2e/
40
+ ```
41
+
42
+ #### How to Migrate
43
+
44
+ **Option 1: Fresh Installation (Recommended for new projects)**
45
+ ```bash
46
+ # Remove old structure
47
+ rm -rf e2e/
48
+
49
+ # Re-run generator
50
+ bin/rails g cypress_on_rails:install --force
51
+ ```
52
+
53
+ **Option 2: Manual Migration (For existing projects with custom code)**
54
+ ```bash
55
+ # Move files to correct location
56
+ mv e2e/cypress/e2e_helper.rb e2e/
57
+ mv e2e/cypress/app_commands e2e/
58
+
59
+ # Update VCR cassettes path if using VCR
60
+ mv e2e/cypress/fixtures e2e/
61
+
62
+ # Verify your initializer has the correct path
63
+ # Should be: c.install_folder = File.expand_path("#{__dir__}/../../e2e")
64
+ # Not: c.install_folder = File.expand_path("#{__dir__}/../../e2e/cypress")
65
+ ```
66
+
67
+ **Option 3: Update Initializer Only (Quick fix, not recommended)**
68
+ If you cannot migrate files immediately, you can temporarily update your initializer:
69
+ ```ruby
70
+ # config/initializers/cypress_on_rails.rb
71
+ c.install_folder = File.expand_path("#{__dir__}/../../e2e/cypress")
72
+ ```
73
+ However, this means Cypress may have issues finding config files. We recommend migrating to the correct structure.
74
+
75
+ #### Why This Change?
76
+ The middleware expects to find `e2e_helper.rb` at `#{install_folder}/e2e_helper.rb` and commands at `#{install_folder}/app_commands/`. Meanwhile, Cypress/Playwright expect config files at the install_folder root when using `--project` flag. The previous structure created a conflict where these requirements couldn't both be satisfied.
77
+
78
+ #### Testing Your Migration
79
+ After migrating, verify:
80
+ 1. Run `bin/rails cypress:open` or `bin/rails playwright:open` - should open successfully
81
+ 2. Run a test that uses app commands - should execute without "file not found" errors
82
+ 3. Check that VCR cassettes (if used) are being created/loaded correctly
83
+
84
+ ---
85
+
86
+ ## [1.19.0] - 2025-10-01
87
+
10
88
  ### Added
11
89
  * **Rake tasks for test execution**: Added `cypress:open` and `cypress:run` rake tasks for seamless test execution, similar to cypress-rails functionality. Also added `playwright:open` and `playwright:run` tasks.
12
90
  * **Server lifecycle hooks**: Added configuration hooks for test server management:
@@ -434,8 +512,10 @@ If migrating from the `cypress-rails` gem:
434
512
  [PR 27]: https://github.com/shakacode/cypress-playwright-on-rails/pull/27
435
513
  [PR 31]: https://github.com/shakacode/cypress-playwright-on-rails/pull/31
436
514
  [PR 18]: https://github.com/shakacode/cypress-playwright-on-rails/pull/18
515
+ [#201]: https://github.com/shakacode/cypress-playwright-on-rails/issues/201
437
516
 
438
517
  <!-- Version diff reference list -->
518
+ [1.19.0]: https://github.com/shakacode/cypress-playwright-on-rails/compare/v1.18.0...v1.19.0
439
519
  [1.18.0]: https://github.com/shakacode/cypress-playwright-on-rails/compare/v1.17.0...v1.18.0
440
520
  [1.17.0]: https://github.com/shakacode/cypress-playwright-on-rails/compare/v1.16.0...v1.17.0
441
521
  [1.16.0]: https://github.com/shakacode/cypress-playwright-on-rails/compare/v1.15.1...v1.16.0
data/README.md CHANGED
@@ -134,17 +134,52 @@ bin/rails g cypress_on_rails:install --install_with=skip
134
134
  bin/rails g cypress_on_rails:install --install_with=skip
135
135
  ```
136
136
 
137
- The generator modifies/adds the following files/directory in your application:
138
- * `config/initializers/cypress_on_rails.rb` used to configure Cypress on Rails
139
- * `e2e/cypress/integration/` contains your cypress tests
140
- * `e2e/cypress/support/on-rails.js` contains Cypress on Rails support code
141
- * `e2e/cypress/e2e_helper.rb` contains helper code to require libraries like factory_bot
142
- * `e2e/cypress/app_commands/` contains your scenario definitions
143
- * `e2e/playwright/e2e/` contains your playwright tests
144
- * `e2e/playwright/support/on-rails.js` contains Playwright on Rails support code
145
-
146
- If you are not using `database_cleaner` look at `e2e/cypress/app_commands/clean.rb`.
147
- If you are not using `factory_bot` look at `e2e/cypress/app_commands/factory_bot.rb`.
137
+ The generator creates the following structure in your application:
138
+
139
+ **For Cypress:**
140
+ ```
141
+ e2e/
142
+ cypress.config.js # Cypress configuration
143
+ e2e_helper.rb # Helper code for factory_bot, database_cleaner, etc.
144
+ app_commands/ # Your custom commands and scenarios
145
+ clean.rb
146
+ factory_bot.rb
147
+ scenarios/
148
+ basic.rb
149
+ fixtures/
150
+ vcr_cassettes/ # VCR recordings (if using VCR)
151
+ cypress/
152
+ support/
153
+ index.js
154
+ commands.js
155
+ on-rails.js # Cypress on Rails support code
156
+ e2e/
157
+ rails_examples/ # Example tests
158
+ ```
159
+
160
+ **For Playwright:**
161
+ ```
162
+ e2e/
163
+ playwright.config.js # Playwright configuration
164
+ e2e_helper.rb # Helper code for factory_bot, database_cleaner, etc.
165
+ app_commands/ # Your custom commands and scenarios (shared with Cypress)
166
+ fixtures/
167
+ vcr_cassettes/ # VCR recordings (if using VCR)
168
+ playwright/
169
+ support/
170
+ index.js
171
+ on-rails.js # Playwright on Rails support code
172
+ e2e/
173
+ rails_examples/ # Example tests
174
+ ```
175
+
176
+ **Additional files:**
177
+ * `config/initializers/cypress_on_rails.rb` - Configuration for Cypress on Rails
178
+
179
+ **Important:** Note that `e2e_helper.rb` and `app_commands/` are at the root of the install folder (e.g., `e2e/`), NOT inside the framework subdirectory (e.g., `e2e/cypress/`). This allows both Cypress and Playwright to share the same commands and helpers when using both frameworks.
180
+
181
+ If you are not using `database_cleaner` look at `e2e/app_commands/clean.rb`.
182
+ If you are not using `factory_bot` look at `e2e/app_commands/factory_bot.rb`.
148
183
 
149
184
  Now you can create scenarios and commands that are plain Ruby files that get loaded through middleware, the ruby sky is your limit.
150
185
 
@@ -448,8 +483,7 @@ Add your VCR configuration to your `config/cypress_on_rails.rb`
448
483
  c.vcr_options = {
449
484
  hook_into: :webmock,
450
485
  default_cassette_options: { record: :once },
451
- # It's possible to override cassette_library_dir using install_folder
452
- cassette_library_dir: File.expand_path("#{__dir__}/../../spec/cypress/fixtures/vcr_cassettes")
486
+ cassette_library_dir: File.expand_path("#{__dir__}/../../e2e/fixtures/vcr_cassettes")
453
487
  }
454
488
  ```
455
489
 
@@ -777,15 +811,9 @@ In `config/initializers/cypress_on_rails.rb`, add this line:
777
811
  <img alt="BrowserStack" src="https://user-images.githubusercontent.com/4244251/184881129-e1edf4b7-3ae1-4ea8-9e6d-3595cf01609e.png" height="55px">
778
812
  </picture>
779
813
  </a>
780
- <a href="https://railsautoscale.com">
781
- <img src="https://user-images.githubusercontent.com/4244251/184881144-95c2c25c-9879-4069-864d-4e67d6ed39d2.png" alt="Rails Autoscale" height="55px">
782
- </a>
783
814
  <a href="https://www.honeybadger.io">
784
815
  <img src="https://user-images.githubusercontent.com/4244251/184881133-79ee9c3c-8165-4852-958e-31687b9536f4.png" alt="Honeybadger" height="55px">
785
816
  </a>
786
- <a href="https://reviewable.io">
787
- <img src="https://user-images.githubusercontent.com/20628911/230848305-c94510a4-82d7-468f-bf9f-eeb81d3f2ce0.png" alt="Reviewable" height="55px">
788
- </a>
789
817
 
790
818
  <br />
791
819
  <br />
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
8
8
  s.author = ["miceportal team", 'Grant Petersen-Speelman']
9
9
  s.email = ["info@miceportal.de", 'grantspeelman@gmail.com']
10
10
  s.homepage = "http://github.com/shakacode/cypress-on-rails"
11
- s.summary = "Integrates cypress with rails or rack applications"
12
- s.description = "Integrates cypress with rails or rack applications"
11
+ s.summary = "Integrates Cypress and Playwright with Rails or Rack applications"
12
+ s.description = "Integrates Cypress and Playwright with Rails or Rack applications"
13
13
  s.post_install_message = 'The CypressDev constant is being deprecated and will be completely removed and replaced with CypressOnRails.'
14
14
  s.files = `git ls-files`.split("\n")
15
15
  s.test_files = `git ls-files -- {spec}/*`.split("\n")
@@ -16,11 +16,35 @@ module CypressOnRails
16
16
  def self.load_e2e_helper
17
17
  e2e_helper_file = "#{configuration.install_folder}/e2e_helper.rb"
18
18
  cypress_helper_file = "#{configuration.install_folder}/cypress_helper.rb"
19
+
20
+ # Check for old structure (files in framework subdirectory)
21
+ old_cypress_location = "#{configuration.install_folder}/cypress/e2e_helper.rb"
22
+ old_playwright_location = "#{configuration.install_folder}/playwright/e2e_helper.rb"
23
+
24
+ # Try to load from the correct location first
19
25
  if File.exist?(e2e_helper_file)
20
26
  Kernel.require e2e_helper_file
21
27
  elsif File.exist?(cypress_helper_file)
22
28
  Kernel.require cypress_helper_file
23
29
  warn "cypress_helper.rb is deprecated, please rename the file to e2e_helper.rb"
30
+ # Fallback: load from old location if new location doesn't exist
31
+ elsif File.exist?(old_cypress_location) || File.exist?(old_playwright_location)
32
+ old_location = File.exist?(old_cypress_location) ? old_cypress_location : old_playwright_location
33
+ logger.warn "=" * 80
34
+ logger.warn "DEPRECATION WARNING: Old folder structure detected!"
35
+ logger.warn "Found e2e_helper.rb at: #{old_location}"
36
+ logger.warn "This file should be at: #{e2e_helper_file}"
37
+ logger.warn ""
38
+ logger.warn "Loading from old location for now, but this will stop working in a future version."
39
+ logger.warn "The generator now creates e2e_helper.rb and app_commands/ at the install_folder"
40
+ logger.warn "root, not inside the framework subdirectory."
41
+ logger.warn ""
42
+ logger.warn "To fix this, run: mv #{old_location} #{e2e_helper_file}"
43
+ logger.warn "Also move app_commands: mv #{File.dirname(old_location)}/app_commands #{configuration.install_folder}/"
44
+ logger.warn "See CHANGELOG.md for full migration guide."
45
+ logger.warn "=" * 80
46
+ # Load from old location as fallback
47
+ Kernel.require old_location
24
48
  else
25
49
  logger.warn "could not find #{e2e_helper_file} nor #{cypress_helper_file}"
26
50
  end
@@ -22,6 +22,12 @@ module CypressOnRails
22
22
  attr_accessor :server_host
23
23
  attr_accessor :server_port
24
24
  attr_accessor :transactional_server
25
+ # HTTP path to check for server readiness (default: '/')
26
+ # Can be set via CYPRESS_RAILS_READINESS_PATH environment variable
27
+ attr_accessor :server_readiness_path
28
+ # Timeout in seconds for individual HTTP readiness checks (default: 5)
29
+ # Can be set via CYPRESS_RAILS_READINESS_TIMEOUT environment variable
30
+ attr_accessor :server_readiness_timeout
25
31
 
26
32
  # Attributes for backwards compatibility
27
33
  def cypress_folder
@@ -62,6 +68,8 @@ module CypressOnRails
62
68
  self.server_host = ENV.fetch('CYPRESS_RAILS_HOST', 'localhost')
63
69
  self.server_port = ENV.fetch('CYPRESS_RAILS_PORT', nil)
64
70
  self.transactional_server = true
71
+ self.server_readiness_path = ENV.fetch('CYPRESS_RAILS_READINESS_PATH', '/')
72
+ self.server_readiness_timeout = ENV.fetch('CYPRESS_RAILS_READINESS_TIMEOUT', '5').to_i
65
73
  end
66
74
 
67
75
  def tagged_logged
@@ -1,6 +1,7 @@
1
1
  require 'socket'
2
2
  require 'timeout'
3
3
  require 'fileutils'
4
+ require 'net/http'
4
5
  require 'cypress_on_rails/configuration'
5
6
 
6
7
  module CypressOnRails
@@ -9,13 +10,16 @@ module CypressOnRails
9
10
 
10
11
  def initialize(options = {})
11
12
  config = CypressOnRails.configuration
12
-
13
+
13
14
  @framework = options[:framework] || :cypress
14
15
  @host = options[:host] || config.server_host
15
16
  @port = options[:port] || config.server_port || find_available_port
16
17
  @port = @port.to_i if @port
17
18
  @install_folder = options[:install_folder] || config.install_folder || detect_install_folder
18
19
  @transactional = options.fetch(:transactional, config.transactional_server)
20
+ # Process management: track PID and process group for proper cleanup
21
+ @server_pid = nil
22
+ @server_pgid = nil
19
23
  end
20
24
 
21
25
  def open
@@ -105,34 +109,91 @@ module CypressOnRails
105
109
 
106
110
  puts "Starting Rails server: #{server_args.join(' ')}"
107
111
 
108
- spawn(*server_args, out: $stdout, err: $stderr)
112
+ @server_pid = spawn(*server_args, out: $stdout, err: $stderr, pgroup: true)
113
+ begin
114
+ @server_pgid = Process.getpgid(@server_pid)
115
+ rescue Errno::ESRCH => e
116
+ # Edge case: process terminated before we could get pgid
117
+ # This is OK - send_term_signal will fall back to single-process kill
118
+ CypressOnRails.configuration.logger.warn("Process #{@server_pid} terminated immediately after spawn: #{e.message}")
119
+ @server_pgid = nil
120
+ end
121
+ @server_pid
109
122
  end
110
123
 
111
124
  def wait_for_server(timeout = 30)
112
125
  Timeout.timeout(timeout) do
113
126
  loop do
114
- begin
115
- TCPSocket.new(host, port).close
116
- break
117
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
118
- sleep 0.1
119
- end
127
+ break if server_responding?
128
+ sleep 0.1
120
129
  end
121
130
  end
122
131
  rescue Timeout::Error
123
132
  raise "Rails server failed to start on #{host}:#{port} after #{timeout} seconds"
124
133
  end
125
134
 
135
+ def server_responding?
136
+ config = CypressOnRails.configuration
137
+ readiness_path = config.server_readiness_path || '/'
138
+ timeout = config.server_readiness_timeout || 5
139
+ uri = URI("http://#{host}:#{port}#{readiness_path}")
140
+
141
+ response = Net::HTTP.start(uri.host, uri.port, open_timeout: timeout, read_timeout: timeout) do |http|
142
+ http.get(uri.path)
143
+ end
144
+
145
+ # Accept 200-399 (success and redirects), reject 404 and 5xx
146
+ # 3xx redirects are considered "ready" because the server is responding correctly
147
+ (200..399).cover?(response.code.to_i)
148
+ rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL, Errno::ETIMEDOUT, SocketError,
149
+ Net::OpenTimeout, Net::ReadTimeout, Net::HTTPBadResponse
150
+ false
151
+ end
152
+
126
153
  def stop_server(pid)
127
- if pid
128
- puts "Stopping Rails server (PID: #{pid})"
129
- Process.kill('TERM', pid)
130
- Process.wait(pid)
154
+ return unless pid
155
+
156
+ puts "Stopping Rails server (PID: #{pid})"
157
+ send_term_signal(pid)
158
+
159
+ begin
160
+ Timeout.timeout(10) do
161
+ Process.wait(pid)
162
+ end
163
+ rescue Timeout::Error
164
+ CypressOnRails.configuration.logger.warn("Server did not terminate after TERM signal, sending KILL")
165
+ safe_kill_process('KILL', pid)
166
+ Process.wait(pid) rescue Errno::ESRCH
131
167
  end
132
168
  rescue Errno::ESRCH
133
169
  # Process already terminated
134
170
  end
135
171
 
172
+ def send_term_signal(pid)
173
+ if @server_pgid && process_exists?(pid)
174
+ Process.kill('TERM', -@server_pgid)
175
+ else
176
+ safe_kill_process('TERM', pid)
177
+ end
178
+ rescue Errno::ESRCH, Errno::EPERM => e
179
+ CypressOnRails.configuration.logger.warn("Failed to kill process group #{@server_pgid}: #{e.message}, trying single process")
180
+ safe_kill_process('TERM', pid)
181
+ end
182
+
183
+ def process_exists?(pid)
184
+ return false unless pid
185
+ Process.kill(0, pid)
186
+ true
187
+ rescue Errno::ESRCH, Errno::EPERM
188
+ false
189
+ end
190
+
191
+ def safe_kill_process(signal, pid)
192
+ Process.kill(signal, pid) if pid
193
+ rescue Errno::ESRCH, Errno::EPERM
194
+ # Process already terminated or permission denied
195
+ end
196
+
136
197
  def base_url
137
198
  "http://#{host}:#{port}"
138
199
  end
@@ -1,3 +1,3 @@
1
1
  module CypressOnRails
2
- VERSION = '1.19.0'.freeze
2
+ VERSION = '1.20.0'.freeze
3
3
  end
@@ -42,8 +42,8 @@ module CypressOnRails
42
42
 
43
43
  def add_initial_files
44
44
  template "config/initializers/cypress_on_rails.rb.erb", "config/initializers/cypress_on_rails.rb"
45
- template "spec/e2e/e2e_helper.rb.erb", "#{options.install_folder}/#{options.framework}/e2e_helper.rb"
46
- directory 'spec/e2e/app_commands', "#{options.install_folder}/#{options.framework}/app_commands"
45
+ template "spec/e2e/e2e_helper.rb.erb", "#{options.install_folder}/e2e_helper.rb"
46
+ directory 'spec/e2e/app_commands', "#{options.install_folder}/app_commands"
47
47
  if options.framework == 'cypress'
48
48
  copy_file "spec/cypress/support/on-rails.js", "#{options.install_folder}/cypress/support/on-rails.js"
49
49
  directory 'spec/cypress/e2e/rails_examples', "#{options.install_folder}/cypress/e2e/rails_examples"
@@ -1,7 +1,7 @@
1
1
  if defined?(CypressOnRails)
2
2
  CypressOnRails.configure do |c|
3
3
  c.api_prefix = "<%= options.api_prefix %>"
4
- c.install_folder = File.expand_path("#{__dir__}/../../<%= options.install_folder %>/<%= options.framework %>")
4
+ c.install_folder = File.expand_path("#{__dir__}/../../<%= options.install_folder %>")
5
5
  # WARNING!! CypressOnRails can execute arbitrary ruby code
6
6
  # please use with extra caution if enabling on hosted servers or starting your local server on 0.0.0.0
7
7
  c.use_middleware = !Rails.env.production?
@@ -12,7 +12,7 @@ if defined?(CypressOnRails)
12
12
  <% unless options.experimental %># <% end %> c.vcr_options = {
13
13
  <% unless options.experimental %># <% end %> hook_into: :webmock,
14
14
  <% unless options.experimental %># <% end %> default_cassette_options: { record: :once },
15
- <% unless options.experimental %># <% end %> cassette_library_dir: File.expand_path("#{__dir__}/../../<%= options.install_folder %>/<%= options.framework %>/fixtures/vcr_cassettes")
15
+ <% unless options.experimental %># <% end %> cassette_library_dir: File.expand_path("#{__dir__}/../../<%= options.install_folder %>/fixtures/vcr_cassettes")
16
16
  <% unless options.experimental %># <% end %> }
17
17
  c.logger = Rails.logger
18
18
 
@@ -10,6 +10,8 @@ RSpec.describe CypressOnRails::Configuration do
10
10
  expect(CypressOnRails.configuration.logger).to_not be_nil
11
11
  expect(CypressOnRails.configuration.before_request).to_not be_nil
12
12
  expect(CypressOnRails.configuration.vcr_options).to eq({})
13
+ expect(CypressOnRails.configuration.server_readiness_path).to eq('/')
14
+ expect(CypressOnRails.configuration.server_readiness_timeout).to eq(5)
13
15
  end
14
16
 
15
17
  it 'can be configured' do
@@ -22,6 +24,8 @@ RSpec.describe CypressOnRails::Configuration do
22
24
  config.logger = my_logger
23
25
  config.before_request = before_request_lambda
24
26
  config.vcr_options = { hook_into: :webmock }
27
+ config.server_readiness_path = '/health'
28
+ config.server_readiness_timeout = 10
25
29
  end
26
30
  expect(CypressOnRails.configuration.api_prefix).to eq('/api')
27
31
  expect(CypressOnRails.configuration.install_folder).to eq('my/path')
@@ -29,5 +33,7 @@ RSpec.describe CypressOnRails::Configuration do
29
33
  expect(CypressOnRails.configuration.logger).to eq(my_logger)
30
34
  expect(CypressOnRails.configuration.before_request).to eq(before_request_lambda)
31
35
  expect(CypressOnRails.configuration.vcr_options).to eq(hook_into: :webmock)
36
+ expect(CypressOnRails.configuration.server_readiness_path).to eq('/health')
37
+ expect(CypressOnRails.configuration.server_readiness_timeout).to eq(10)
32
38
  end
33
39
  end
@@ -0,0 +1,222 @@
1
+ require 'spec_helper'
2
+ require 'rails/generators'
3
+ require 'generators/cypress_on_rails/install_generator'
4
+ require 'tmpdir'
5
+ require 'fileutils'
6
+
7
+ RSpec.describe CypressOnRails::InstallGenerator, type: :generator do
8
+ let(:destination_root) { Dir.mktmpdir }
9
+
10
+ before do
11
+ # Set up a minimal Rails app structure
12
+ FileUtils.mkdir_p(File.join(destination_root, 'config', 'initializers'))
13
+ FileUtils.mkdir_p(File.join(destination_root, 'bin'))
14
+
15
+ # Mock the generator's destination_root
16
+ allow(Dir).to receive(:pwd).and_return(destination_root)
17
+
18
+ # Prevent actual npm/yarn installation in tests
19
+ # Mock only package manager commands, let file operations through
20
+ allow_any_instance_of(CypressOnRails::InstallGenerator).to receive(:system) do |_, command|
21
+ # Return true for yarn/npm install commands to skip actual installation
22
+ command.to_s.match?(/yarn|npm/)
23
+ end
24
+ end
25
+
26
+ after do
27
+ FileUtils.rm_rf(destination_root)
28
+ end
29
+
30
+ describe 'with default options (cypress framework, e2e folder)' do
31
+ let(:args) { [] }
32
+ let(:options) { {} }
33
+
34
+ before do
35
+ run_generator(args, options)
36
+ end
37
+
38
+ it 'creates the initializer with correct install_folder path' do
39
+ initializer_path = File.join(destination_root, 'config', 'initializers', 'cypress_on_rails.rb')
40
+ expect(File).to exist(initializer_path)
41
+
42
+ content = File.read(initializer_path)
43
+ # Should point to e2e, not e2e/cypress
44
+ expect(content).to include('c.install_folder = File.expand_path("#{__dir__}/../../e2e")')
45
+ end
46
+
47
+ it 'creates cypress config at install_folder root' do
48
+ config_path = File.join(destination_root, 'e2e', 'cypress.config.js')
49
+ expect(File).to exist(config_path)
50
+ end
51
+
52
+ it 'creates e2e_helper.rb at install_folder root' do
53
+ helper_path = File.join(destination_root, 'e2e', 'e2e_helper.rb')
54
+ expect(File).to exist(helper_path)
55
+ end
56
+
57
+ it 'creates app_commands directory at install_folder root' do
58
+ commands_path = File.join(destination_root, 'e2e', 'app_commands')
59
+ expect(File).to be_directory(commands_path)
60
+ end
61
+
62
+ it 'creates cypress support files in framework subdirectory' do
63
+ support_path = File.join(destination_root, 'e2e', 'cypress', 'support', 'index.js')
64
+ expect(File).to exist(support_path)
65
+ end
66
+
67
+ it 'creates cypress examples in framework subdirectory' do
68
+ examples_path = File.join(destination_root, 'e2e', 'cypress', 'e2e', 'rails_examples')
69
+ expect(File).to be_directory(examples_path)
70
+ end
71
+ end
72
+
73
+ describe 'with playwright framework' do
74
+ let(:args) { [] }
75
+ let(:options) { { framework: 'playwright' } }
76
+
77
+ before do
78
+ run_generator(args, options)
79
+ end
80
+
81
+ it 'creates the initializer with correct install_folder path' do
82
+ initializer_path = File.join(destination_root, 'config', 'initializers', 'cypress_on_rails.rb')
83
+ expect(File).to exist(initializer_path)
84
+
85
+ content = File.read(initializer_path)
86
+ # Should point to e2e, not e2e/playwright
87
+ expect(content).to include('c.install_folder = File.expand_path("#{__dir__}/../../e2e")')
88
+ end
89
+
90
+ it 'creates playwright config at install_folder root' do
91
+ config_path = File.join(destination_root, 'e2e', 'playwright.config.js')
92
+ expect(File).to exist(config_path)
93
+ end
94
+
95
+ it 'creates e2e_helper.rb at install_folder root' do
96
+ helper_path = File.join(destination_root, 'e2e', 'e2e_helper.rb')
97
+ expect(File).to exist(helper_path)
98
+ end
99
+
100
+ it 'creates app_commands directory at install_folder root' do
101
+ commands_path = File.join(destination_root, 'e2e', 'app_commands')
102
+ expect(File).to be_directory(commands_path)
103
+ end
104
+
105
+ it 'creates playwright support files in framework subdirectory' do
106
+ support_path = File.join(destination_root, 'e2e', 'playwright', 'support', 'index.js')
107
+ expect(File).to exist(support_path)
108
+ end
109
+
110
+ it 'creates playwright examples in framework subdirectory' do
111
+ examples_path = File.join(destination_root, 'e2e', 'playwright', 'e2e', 'rails_examples')
112
+ expect(File).to be_directory(examples_path)
113
+ end
114
+ end
115
+
116
+ describe 'with custom install_folder' do
117
+ let(:args) { [] }
118
+ let(:options) { { install_folder: 'spec/system' } }
119
+
120
+ before do
121
+ run_generator(args, options)
122
+ end
123
+
124
+ it 'creates files in the custom folder' do
125
+ helper_path = File.join(destination_root, 'spec', 'system', 'e2e_helper.rb')
126
+ expect(File).to exist(helper_path)
127
+
128
+ commands_path = File.join(destination_root, 'spec', 'system', 'app_commands')
129
+ expect(File).to be_directory(commands_path)
130
+ end
131
+
132
+ it 'sets the correct install_folder in the initializer' do
133
+ initializer_path = File.join(destination_root, 'config', 'initializers', 'cypress_on_rails.rb')
134
+ content = File.read(initializer_path)
135
+ expect(content).to include('c.install_folder = File.expand_path("#{__dir__}/../../spec/system")')
136
+ end
137
+ end
138
+
139
+ describe 'file structure ensures middleware and framework compatibility' do
140
+ let(:args) { [] }
141
+ let(:options) { {} }
142
+
143
+ before do
144
+ run_generator(args, options)
145
+ end
146
+
147
+ it 'does not create e2e_helper.rb in old location (framework subdirectory)' do
148
+ old_cypress_path = File.join(destination_root, 'e2e', 'cypress', 'e2e_helper.rb')
149
+ old_playwright_path = File.join(destination_root, 'e2e', 'playwright', 'e2e_helper.rb')
150
+ expect(File).not_to exist(old_cypress_path)
151
+ expect(File).not_to exist(old_playwright_path)
152
+ end
153
+
154
+ it 'does not create app_commands in old location (framework subdirectory)' do
155
+ old_cypress_path = File.join(destination_root, 'e2e', 'cypress', 'app_commands')
156
+ old_playwright_path = File.join(destination_root, 'e2e', 'playwright', 'app_commands')
157
+ expect(File).not_to exist(old_cypress_path)
158
+ expect(File).not_to exist(old_playwright_path)
159
+ end
160
+
161
+ it 'places e2e_helper.rb where middleware expects it (install_folder/e2e_helper.rb)' do
162
+ # Middleware looks for #{install_folder}/e2e_helper.rb
163
+ helper_path = File.join(destination_root, 'e2e', 'e2e_helper.rb')
164
+ expect(File).to exist(helper_path)
165
+ expect(File.read(helper_path)).to include('CypressOnRails')
166
+ end
167
+
168
+ it 'places app_commands where middleware expects it (install_folder/app_commands)' do
169
+ # Middleware looks for #{install_folder}/app_commands/#{command}.rb
170
+ commands_path = File.join(destination_root, 'e2e', 'app_commands')
171
+ expect(File).to be_directory(commands_path)
172
+
173
+ # Check that command files exist
174
+ clean_cmd = File.join(commands_path, 'clean.rb')
175
+ expect(File).to exist(clean_cmd)
176
+ end
177
+
178
+ it 'places cypress.config.js where cypress --project flag expects it' do
179
+ # Cypress runs with --project install_folder, expects config at that level
180
+ config_path = File.join(destination_root, 'e2e', 'cypress.config.js')
181
+ expect(File).to exist(config_path)
182
+
183
+ # Verify the config references the correct relative path for support files
184
+ content = File.read(config_path)
185
+ expect(content).to include('cypress/support/index.js')
186
+ end
187
+
188
+ it 'creates a valid directory structure' do
189
+ # The expected structure:
190
+ # e2e/
191
+ # cypress.config.js <- Config at root of install_folder
192
+ # e2e_helper.rb <- Helper at root of install_folder
193
+ # app_commands/ <- Commands at root of install_folder
194
+ # cypress/ <- Framework-specific subdirectory
195
+ # support/
196
+ # e2e/
197
+
198
+ expect(File).to exist(File.join(destination_root, 'e2e', 'cypress.config.js'))
199
+ expect(File).to exist(File.join(destination_root, 'e2e', 'e2e_helper.rb'))
200
+ expect(File).to be_directory(File.join(destination_root, 'e2e', 'app_commands'))
201
+ expect(File).to be_directory(File.join(destination_root, 'e2e', 'cypress'))
202
+ expect(File).to be_directory(File.join(destination_root, 'e2e', 'cypress', 'support'))
203
+ expect(File).to be_directory(File.join(destination_root, 'e2e', 'cypress', 'e2e'))
204
+ end
205
+ end
206
+
207
+ def run_generator(args, options)
208
+ generator_options = []
209
+ options.each do |key, value|
210
+ generator_options << "--#{key}=#{value}"
211
+ end
212
+
213
+ CypressOnRails::InstallGenerator.start(
214
+ args + generator_options,
215
+ {
216
+ destination_root: destination_root,
217
+ shell: Thor::Shell::Basic.new,
218
+ behavior: :invoke
219
+ }
220
+ )
221
+ end
222
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cypress-on-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.0
4
+ version: 1.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - miceportal team
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- description: Integrates cypress with rails or rack applications
111
+ description: Integrates Cypress and Playwright with Rails or Rack applications
112
112
  email:
113
113
  - info@miceportal.de
114
114
  - grantspeelman@gmail.com
@@ -197,6 +197,7 @@ files:
197
197
  - spec/cypress_on_rails/smart_factory_wrapper_spec.rb
198
198
  - spec/cypress_on_rails/vcr/insert_eject_middleware_spec.rb
199
199
  - spec/cypress_on_rails/vcr/use_cassette_middleware_spec.rb
200
+ - spec/generators/install_generator_spec.rb
200
201
  - spec/spec_helper.rb
201
202
  - specs_e2e/cypress.config.js
202
203
  - specs_e2e/playwright.config.js
@@ -436,5 +437,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
436
437
  requirements: []
437
438
  rubygems_version: 3.6.7
438
439
  specification_version: 4
439
- summary: Integrates cypress with rails or rack applications
440
+ summary: Integrates Cypress and Playwright with Rails or Rack applications
440
441
  test_files: []