cypress-on-rails 1.18.0 → 1.19.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.
@@ -0,0 +1,197 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+ require 'fileutils'
4
+ require 'cypress_on_rails/configuration'
5
+
6
+ module CypressOnRails
7
+ class Server
8
+ attr_reader :host, :port, :framework, :install_folder
9
+
10
+ def initialize(options = {})
11
+ config = CypressOnRails.configuration
12
+
13
+ @framework = options[:framework] || :cypress
14
+ @host = options[:host] || config.server_host
15
+ @port = options[:port] || config.server_port || find_available_port
16
+ @port = @port.to_i if @port
17
+ @install_folder = options[:install_folder] || config.install_folder || detect_install_folder
18
+ @transactional = options.fetch(:transactional, config.transactional_server)
19
+ end
20
+
21
+ def open
22
+ start_server do
23
+ run_command(open_command, "Opening #{framework} test runner")
24
+ end
25
+ end
26
+
27
+ def run
28
+ start_server do
29
+ result = run_command(run_command_args, "Running #{framework} tests")
30
+ exit(result ? 0 : 1)
31
+ end
32
+ end
33
+
34
+ def init
35
+ ensure_install_folder_exists
36
+ puts "#{framework.to_s.capitalize} configuration initialized at #{install_folder}"
37
+ end
38
+
39
+ private
40
+
41
+ def detect_install_folder
42
+ # Check common locations for cypress/playwright installation
43
+ possible_folders = ['e2e', 'spec/e2e', 'spec/cypress', 'spec/playwright', 'cypress', 'playwright']
44
+ folder = possible_folders.find { |f| File.exist?(File.expand_path(f)) }
45
+ folder || 'e2e'
46
+ end
47
+
48
+ def ensure_install_folder_exists
49
+ unless File.exist?(install_folder)
50
+ puts "Creating #{install_folder} directory..."
51
+ FileUtils.mkdir_p(install_folder)
52
+ end
53
+ end
54
+
55
+ def find_available_port
56
+ server = TCPServer.new('127.0.0.1', 0)
57
+ port = server.addr[1]
58
+ server.close
59
+ port
60
+ end
61
+
62
+ def start_server(&block)
63
+ config = CypressOnRails.configuration
64
+
65
+ run_hook(config.before_server_start)
66
+
67
+ ENV['CYPRESS'] = '1'
68
+ ENV['RAILS_ENV'] = 'test'
69
+
70
+ server_pid = spawn_server
71
+
72
+ begin
73
+ wait_for_server
74
+ run_hook(config.after_server_start)
75
+
76
+ puts "Rails server started on #{base_url}"
77
+
78
+ if @transactional && defined?(ActiveRecord::Base)
79
+ ActiveRecord::Base.connection.begin_transaction(joinable: false)
80
+ run_hook(config.after_transaction_start)
81
+ end
82
+
83
+ yield
84
+
85
+ ensure
86
+ run_hook(config.before_server_stop)
87
+
88
+ if @transactional && defined?(ActiveRecord::Base)
89
+ ActiveRecord::Base.connection.rollback_transaction if ActiveRecord::Base.connection.transaction_open?
90
+ end
91
+
92
+ stop_server(server_pid)
93
+ ENV.delete('CYPRESS')
94
+ end
95
+ end
96
+
97
+ def spawn_server
98
+ rails_args = if File.exist?('bin/rails')
99
+ ['bin/rails']
100
+ else
101
+ ['bundle', 'exec', 'rails']
102
+ end
103
+
104
+ server_args = rails_args + ['server', '-p', port.to_s, '-b', host]
105
+
106
+ puts "Starting Rails server: #{server_args.join(' ')}"
107
+
108
+ spawn(*server_args, out: $stdout, err: $stderr)
109
+ end
110
+
111
+ def wait_for_server(timeout = 30)
112
+ Timeout.timeout(timeout) do
113
+ loop do
114
+ begin
115
+ TCPSocket.new(host, port).close
116
+ break
117
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
118
+ sleep 0.1
119
+ end
120
+ end
121
+ end
122
+ rescue Timeout::Error
123
+ raise "Rails server failed to start on #{host}:#{port} after #{timeout} seconds"
124
+ end
125
+
126
+ def stop_server(pid)
127
+ if pid
128
+ puts "Stopping Rails server (PID: #{pid})"
129
+ Process.kill('TERM', pid)
130
+ Process.wait(pid)
131
+ end
132
+ rescue Errno::ESRCH
133
+ # Process already terminated
134
+ end
135
+
136
+ def base_url
137
+ "http://#{host}:#{port}"
138
+ end
139
+
140
+ def open_command
141
+ case framework
142
+ when :cypress
143
+ if command_exists?('yarn')
144
+ ['yarn', 'cypress', 'open', '--project', install_folder, '--config', "baseUrl=#{base_url}"]
145
+ elsif command_exists?('npx')
146
+ ['npx', 'cypress', 'open', '--project', install_folder, '--config', "baseUrl=#{base_url}"]
147
+ else
148
+ ['cypress', 'open', '--project', install_folder, '--config', "baseUrl=#{base_url}"]
149
+ end
150
+ when :playwright
151
+ if command_exists?('yarn')
152
+ ['yarn', 'playwright', 'test', '--ui']
153
+ elsif command_exists?('npx')
154
+ ['npx', 'playwright', 'test', '--ui']
155
+ else
156
+ ['playwright', 'test', '--ui']
157
+ end
158
+ end
159
+ end
160
+
161
+ def run_command_args
162
+ case framework
163
+ when :cypress
164
+ if command_exists?('yarn')
165
+ ['yarn', 'cypress', 'run', '--project', install_folder, '--config', "baseUrl=#{base_url}"]
166
+ elsif command_exists?('npx')
167
+ ['npx', 'cypress', 'run', '--project', install_folder, '--config', "baseUrl=#{base_url}"]
168
+ else
169
+ ['cypress', 'run', '--project', install_folder, '--config', "baseUrl=#{base_url}"]
170
+ end
171
+ when :playwright
172
+ if command_exists?('yarn')
173
+ ['yarn', 'playwright', 'test']
174
+ elsif command_exists?('npx')
175
+ ['npx', 'playwright', 'test']
176
+ else
177
+ ['playwright', 'test']
178
+ end
179
+ end
180
+ end
181
+
182
+ def run_command(command_args, description)
183
+ puts "#{description}: #{command_args.join(' ')}"
184
+ system(*command_args)
185
+ end
186
+
187
+ def command_exists?(command)
188
+ system("which #{command} > /dev/null 2>&1")
189
+ end
190
+
191
+ def run_hook(hook)
192
+ if hook && hook.respond_to?(:call)
193
+ hook.call
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,58 @@
1
+ module CypressOnRails
2
+ class StateResetMiddleware
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ if env['PATH_INFO'] == '/__cypress__/reset_state' || env['PATH_INFO'] == '/cypress_rails_reset_state'
9
+ reset_application_state
10
+ [200, { 'Content-Type' => 'text/plain' }, ['State reset completed']]
11
+ else
12
+ @app.call(env)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def reset_application_state
19
+ config = CypressOnRails.configuration
20
+
21
+ # Default state reset actions
22
+ if defined?(DatabaseCleaner)
23
+ DatabaseCleaner.clean_with(:truncation)
24
+ elsif defined?(ActiveRecord::Base)
25
+ connection = ActiveRecord::Base.connection
26
+
27
+ # Use disable_referential_integrity if available for safer table clearing
28
+ if connection.respond_to?(:disable_referential_integrity)
29
+ connection.disable_referential_integrity do
30
+ connection.tables.each do |table|
31
+ next if table == 'schema_migrations' || table == 'ar_internal_metadata'
32
+ connection.execute("DELETE FROM #{connection.quote_table_name(table)}")
33
+ end
34
+ end
35
+ else
36
+ # Fallback to regular deletion with proper table name quoting
37
+ connection.tables.each do |table|
38
+ next if table == 'schema_migrations' || table == 'ar_internal_metadata'
39
+ connection.execute("DELETE FROM #{connection.quote_table_name(table)}")
40
+ end
41
+ end
42
+ end
43
+
44
+ # Clear Rails cache
45
+ Rails.cache.clear if defined?(Rails) && Rails.cache
46
+
47
+ # Reset any class-level state
48
+ ActiveSupport::Dependencies.clear if defined?(ActiveSupport::Dependencies)
49
+
50
+ # Run after_state_reset hook after cleanup is complete
51
+ run_hook(config.after_state_reset)
52
+ end
53
+
54
+ def run_hook(hook)
55
+ hook.call if hook && hook.respond_to?(:call)
56
+ end
57
+ end
58
+ end
@@ -1,3 +1,3 @@
1
1
  module CypressOnRails
2
- VERSION = '1.18.0'.freeze
2
+ VERSION = '1.19.0'.freeze
3
3
  end
@@ -15,6 +15,18 @@ if defined?(CypressOnRails)
15
15
  <% unless options.experimental %># <% end %> cassette_library_dir: File.expand_path("#{__dir__}/../../<%= options.install_folder %>/<%= options.framework %>/fixtures/vcr_cassettes")
16
16
  <% unless options.experimental %># <% end %> }
17
17
  c.logger = Rails.logger
18
+
19
+ # Server configuration for rake tasks (cypress:open, cypress:run, playwright:open, playwright:run)
20
+ # c.server_host = 'localhost' # or use ENV['CYPRESS_RAILS_HOST']
21
+ # c.server_port = 3001 # or use ENV['CYPRESS_RAILS_PORT']
22
+ # c.transactional_server = true # Enable automatic transaction rollback between tests
23
+
24
+ # Server lifecycle hooks for rake tasks
25
+ # c.before_server_start = -> { DatabaseCleaner.clean_with(:truncation) }
26
+ # c.after_server_start = -> { puts "Test server started on port #{CypressOnRails.configuration.server_port}" }
27
+ # c.after_transaction_start = -> { Rails.application.load_seed }
28
+ # c.after_state_reset = -> { Rails.cache.clear }
29
+ # c.before_server_stop = -> { puts "Stopping test server..." }
18
30
 
19
31
  # If you want to enable a before_request logic, such as authentication, logging, sending metrics, etc.
20
32
  # Refer to https://www.rubydoc.info/gems/rack/Rack/Request for the `request` argument.
@@ -9,7 +9,7 @@ describe('Rails using factory bot examples', function() {
9
9
  ])
10
10
  cy.visit('/')
11
11
  cy.get('table').find('tbody').should(($tbody) => {
12
- // clean should of removed these from other tests
12
+ // clean should have removed these from other tests
13
13
  expect($tbody).not.to.contain('Hello World')
14
14
 
15
15
  expect($tbody).to.contain('Good bye Mars')
@@ -23,7 +23,7 @@ describe('Rails using factory bot examples', function() {
23
23
  ])
24
24
  cy.visit('/')
25
25
  cy.get('table').find('tbody').should(($tbody) => {
26
- // clean should of removed these from other tests
26
+ // clean should have removed these from other tests
27
27
  expect($tbody).to.contain('Hello World')
28
28
  expect($tbody).not.to.contain('Good bye Mars')
29
29
  })
@@ -7,7 +7,7 @@ describe('Rails using scenarios examples', function() {
7
7
  cy.appScenario('basic')
8
8
  cy.visit('/')
9
9
  cy.get('table').find('tbody').should(($tbody) => {
10
- // clean should of removed these from other tests
10
+ // clean should have removed these from other tests
11
11
  expect($tbody).not.to.contain('Good bye Mars')
12
12
  expect($tbody).not.to.contain('Hello World')
13
13
 
@@ -1,4 +1,4 @@
1
- // CypressOnRails: dont remove these command
1
+ // CypressOnRails: don't remove these commands
2
2
  Cypress.Commands.add('appCommands', function (body) {
3
3
  Object.keys(body).forEach(key => body[key] === undefined ? delete body[key] : {});
4
4
  const log = Cypress.log({ name: "APP", message: body, autoEnd: false })
@@ -0,0 +1,33 @@
1
+ namespace :cypress do
2
+ desc "Open Cypress test runner UI"
3
+ task :open => :environment do
4
+ require 'cypress_on_rails/server'
5
+ CypressOnRails::Server.new.open
6
+ end
7
+
8
+ desc "Run Cypress tests in headless mode"
9
+ task :run => :environment do
10
+ require 'cypress_on_rails/server'
11
+ CypressOnRails::Server.new.run
12
+ end
13
+
14
+ desc "Initialize Cypress configuration"
15
+ task :init => :environment do
16
+ require 'cypress_on_rails/server'
17
+ CypressOnRails::Server.new.init
18
+ end
19
+ end
20
+
21
+ namespace :playwright do
22
+ desc "Open Playwright test runner UI"
23
+ task :open => :environment do
24
+ require 'cypress_on_rails/server'
25
+ CypressOnRails::Server.new(framework: :playwright).open
26
+ end
27
+
28
+ desc "Run Playwright tests in headless mode"
29
+ task :run => :environment do
30
+ require 'cypress_on_rails/server'
31
+ CypressOnRails::Server.new(framework: :playwright).run
32
+ end
33
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler"
4
+ require_relative "task_helpers"
5
+
6
+ class RaisingMessageHandler
7
+ def add_error(error)
8
+ raise error
9
+ end
10
+ end
11
+
12
+ # rubocop:disable Metrics/BlockLength
13
+
14
+ desc("Releases the gem using the given version.
15
+
16
+ IMPORTANT: the gem version must be in valid rubygem format (no dashes).
17
+
18
+ This task depends on the gem-release (ruby gem) which is installed via `bundle install`
19
+
20
+ 1st argument: The new version in rubygem format (no dashes). Pass no argument to
21
+ automatically perform a patch version bump.
22
+ 2nd argument: Perform a dry run by passing 'true' as a second argument.
23
+
24
+ Note, accept defaults for rubygems options. Script will pause to get 2FA tokens.
25
+
26
+ Example: `rake release[2.1.0,false]`")
27
+ task :release, %i[gem_version dry_run] do |_t, args|
28
+ include CypressOnRails::TaskHelpers
29
+
30
+ # Check if there are uncommitted changes
31
+ unless `git status --porcelain`.strip.empty?
32
+ raise "You have uncommitted changes. Please commit or stash them before releasing."
33
+ end
34
+
35
+ args_hash = args.to_hash
36
+
37
+ is_dry_run = args_hash[:dry_run] == 'true'
38
+
39
+ gem_version = args_hash.fetch(:gem_version, "")
40
+
41
+ # See https://github.com/svenfuchs/gem-release
42
+ sh_in_dir(gem_root, "git pull --rebase")
43
+ sh_in_dir(gem_root, "gem bump --no-commit --file lib/cypress_on_rails/version.rb #{gem_version.strip.empty? ? '' : %(--version #{gem_version})}")
44
+
45
+ # Read the actual version from the file after bump
46
+ load File.expand_path("../lib/cypress_on_rails/version.rb", __dir__)
47
+ actual_version = CypressOnRails::VERSION
48
+
49
+ # Update Gemfile.lock files
50
+ sh_in_dir(gem_root, "bundle install")
51
+
52
+ unless is_dry_run
53
+ # Commit the version bump
54
+ sh_in_dir(gem_root, "git add lib/cypress_on_rails/version.rb")
55
+ sh_in_dir(gem_root, "git commit -m \"Release v#{actual_version}\"")
56
+
57
+ # Tag the release
58
+ sh_in_dir(gem_root, "git tag v#{actual_version}")
59
+
60
+ # Push the commit and tag
61
+ sh_in_dir(gem_root, "git push && git push --tags")
62
+
63
+ # Release the new gem version
64
+ puts "Carefully add your OTP for Rubygems. If you get an error, run 'gem release' again."
65
+ sh_in_dir(gem_root, "gem release")
66
+ else
67
+ puts "DRY RUN: Would have committed, tagged v#{actual_version}, pushed, and released gem"
68
+ end
69
+
70
+ msg = <<~MSG
71
+ Once you have successfully published, run these commands to update CHANGELOG.md:
72
+
73
+ bundle exec rake update_changelog
74
+ git commit -a -m 'Update CHANGELOG.md'
75
+ git push
76
+ MSG
77
+ puts msg
78
+ end
79
+
80
+ # rubocop:enable Metrics/BlockLength
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CypressOnRails
4
+ module TaskHelpers
5
+ # Returns the root folder of the cypress-on-rails gem
6
+ def gem_root
7
+ File.expand_path("..", __dir__)
8
+ end
9
+
10
+ # Executes a string or an array of strings in a shell in the given directory
11
+ def sh_in_dir(dir, *shell_commands)
12
+ Dir.chdir(dir) do
13
+ # Without `with_unbundled_env`, running bundle in the child directories won't correctly
14
+ # update the Gemfile.lock
15
+ Bundler.with_unbundled_env do
16
+ shell_commands.flatten.each do |shell_command|
17
+ sh(shell_command.strip)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "English"
4
+
5
+ desc "Updates CHANGELOG.md inserting headers for the new version.
6
+
7
+ Argument: Git tag. Defaults to the latest tag."
8
+
9
+ task :update_changelog, %i[tag] do |_, args|
10
+ tag = args[:tag] || `git describe --tags --abbrev=0`.strip
11
+
12
+ # Remove 'v' prefix if present (e.g., v1.18.0 -> 1.18.0)
13
+ version = tag.start_with?('v') ? tag[1..-1] : tag
14
+ anchor = "[#{version}]"
15
+
16
+ changelog = File.read("CHANGELOG.md")
17
+
18
+ if changelog.include?(anchor)
19
+ puts "Tag #{version} is already documented in CHANGELOG.md, update manually if needed"
20
+ next
21
+ end
22
+
23
+ tag_date_output = `git show -s --format=%cs #{tag} 2>&1`
24
+ if $CHILD_STATUS.success?
25
+ tag_date = tag_date_output.split("\n").last.strip
26
+ else
27
+ abort("Failed to find tag #{tag}")
28
+ end
29
+
30
+ # After "## [Unreleased]", insert new version header
31
+ unreleased_section = "## [Unreleased]"
32
+ new_version_header = "\n\n## #{anchor} - #{tag_date}"
33
+
34
+ if changelog.include?(unreleased_section)
35
+ changelog.sub!(unreleased_section, "#{unreleased_section}#{new_version_header}")
36
+ else
37
+ abort("Could not find '## [Unreleased]' section in CHANGELOG.md")
38
+ end
39
+
40
+ # Find and update version comparison links at the bottom
41
+ # Pattern: [1.18.0]: https://github.com/shakacode/cypress-playwright-on-rails/compare/v1.17.0...v1.18.0
42
+ compare_link_prefix = "https://github.com/shakacode/cypress-playwright-on-rails/compare"
43
+
44
+ # Find the last version link to determine the previous version
45
+ last_version_match = changelog.match(/\[(\d+\.\d+\.\d+(?:\.\w+)?)\]:.*?compare\/v(\d+\.\d+\.\d+(?:\.\w+)?)\.\.\.v(\d+\.\d+\.\d+(?:\.\w+)?)/)
46
+
47
+ if last_version_match
48
+ last_version = last_version_match[1]
49
+ # Add new version link at the top of the version list
50
+ new_link = "#{anchor}: #{compare_link_prefix}/v#{last_version}...v#{version}"
51
+ # Insert after the "<!-- Version diff reference list -->" comment
52
+ changelog.sub!("<!-- Version diff reference list -->", "<!-- Version diff reference list -->\n#{new_link}")
53
+ else
54
+ puts "Warning: Could not find version comparison links. You may need to add the link manually."
55
+ end
56
+
57
+ File.write("CHANGELOG.md", changelog)
58
+ puts "Updated CHANGELOG.md with an entry for #{version}"
59
+ puts "\nNext steps:"
60
+ puts "1. Edit CHANGELOG.md to add release notes under the [#{version}] section"
61
+ puts "2. Move content from [Unreleased] to [#{version}] if applicable"
62
+ puts "3. Review and commit the changes"
63
+ 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.18.0
4
+ version: 1.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - miceportal team
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: gem-release
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  description: Integrates cypress with rails or rack applications
98
112
  email:
99
113
  - info@miceportal.de
@@ -103,6 +117,8 @@ extensions: []
103
117
  extra_rdoc_files: []
104
118
  files:
105
119
  - ".github/FUNDING.yml"
120
+ - ".github/workflows/claude-code-review.yml"
121
+ - ".github/workflows/claude.yml"
106
122
  - ".github/workflows/ruby.yml"
107
123
  - ".gitignore"
108
124
  - ".rspec"
@@ -110,8 +126,15 @@ files:
110
126
  - Gemfile
111
127
  - LICENSE
112
128
  - README.md
129
+ - RELEASING.md
113
130
  - Rakefile
114
131
  - cypress-on-rails.gemspec
132
+ - docs/BEST_PRACTICES.md
133
+ - docs/DX_IMPROVEMENTS.md
134
+ - docs/PLAYWRIGHT_GUIDE.md
135
+ - docs/RELEASE.md
136
+ - docs/TROUBLESHOOTING.md
137
+ - docs/VCR_GUIDE.md
115
138
  - docs/authentication.md
116
139
  - docs/factory_bot_associations.md
117
140
  - lib/cypress-on-rails.rb
@@ -124,8 +147,10 @@ files:
124
147
  - lib/cypress_on_rails/middleware.rb
125
148
  - lib/cypress_on_rails/middleware_config.rb
126
149
  - lib/cypress_on_rails/railtie.rb
150
+ - lib/cypress_on_rails/server.rb
127
151
  - lib/cypress_on_rails/simple_rails_factory.rb
128
152
  - lib/cypress_on_rails/smart_factory_wrapper.rb
153
+ - lib/cypress_on_rails/state_reset_middleware.rb
129
154
  - lib/cypress_on_rails/vcr/insert_eject_middleware.rb
130
155
  - lib/cypress_on_rails/vcr/middleware_helpers.rb
131
156
  - lib/cypress_on_rails/vcr/use_cassette_middleware.rb
@@ -153,10 +178,14 @@ files:
153
178
  - lib/generators/cypress_on_rails/templates/spec/playwright/e2e/rails_examples/using_scenarios.spec.js
154
179
  - lib/generators/cypress_on_rails/templates/spec/playwright/support/index.js.erb
155
180
  - lib/generators/cypress_on_rails/templates/spec/playwright/support/on-rails.js
181
+ - lib/tasks/cypress.rake
156
182
  - plugin/.gitignore
157
183
  - plugin/cypress/plugins/index.js
158
184
  - plugin/package.json
159
185
  - plugin/support/index.js
186
+ - rakelib/release.rake
187
+ - rakelib/task_helpers.rb
188
+ - rakelib/update_changelog.rake
160
189
  - spec/cypress_on_rails/command_executor/e2e_helper.rb
161
190
  - spec/cypress_on_rails/command_executor/test_command.rb
162
191
  - spec/cypress_on_rails/command_executor/test_command_with_options.rb
@@ -405,7 +434,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
405
434
  - !ruby/object:Gem::Version
406
435
  version: '0'
407
436
  requirements: []
408
- rubygems_version: 3.6.9
437
+ rubygems_version: 3.6.7
409
438
  specification_version: 4
410
439
  summary: Integrates cypress with rails or rack applications
411
440
  test_files: []