Skip to content

Commit 7d19845

Browse files
justin808claude
andcommitted
Add Playwright E2E testing via cypress-on-rails gem
- Add cypress-on-rails gem (v1.19) with Playwright support - Generate Playwright configuration and test structure using gem generator - Create React on Rails specific E2E tests for components and SSR - Add comprehensive documentation in CLAUDE.md for Playwright usage - Include detailed README in e2e directory with examples - Configure Rails integration for database control and factory_bot This implementation leverages the cypress-on-rails gem to provide: - Seamless Rails integration with database cleanup between tests - Factory Bot support for easy test data creation - Ability to run arbitrary Ruby code from Playwright tests - Predefined scenarios for complex application states - Better developer experience than standalone Playwright Tests demonstrate React on Rails features: server-side rendering, client-side hydration, Redux integration, and component interactivity. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 9612f37 commit 7d19845

21 files changed

+831
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,7 @@ ssr-generated
6666

6767
# Claude Code local settings
6868
.claude/settings.local.json
69+
70+
# Playwright test artifacts (from cypress-on-rails gem)
71+
/spec/dummy/e2e/playwright-report/
72+
/spec/dummy/test-results/

CLAUDE.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Git hooks will automatically run linting on **all changed files (staged + unstag
2727
- **Run tests**:
2828
- Ruby tests: `rake run_rspec`
2929
- JavaScript tests: `yarn run test` or `rake js_tests`
30+
- Playwright E2E tests: See Playwright section below
3031
- All tests: `rake` (default task runs lint and all tests except examples)
3132
- **Linting** (MANDATORY BEFORE EVERY COMMIT):
3233
- **REQUIRED**: `bundle exec rubocop` - Must pass with zero offenses
@@ -126,10 +127,140 @@ This project maintains both a Ruby gem and an NPM package:
126127
- Generated examples are in `gen-examples/` (ignored by git)
127128
- Only use `yarn` as the JS package manager, never `npm`
128129

130+
## Playwright E2E Testing
131+
132+
### Overview
133+
Playwright E2E testing is integrated via the `cypress-on-rails` gem (v1.19+), which provides seamless integration between Playwright and Rails. This allows you to control Rails application state during tests, use factory_bot, and more.
134+
135+
### Setup
136+
The gem and Playwright are already configured. To install Playwright browsers:
137+
138+
```bash
139+
cd spec/dummy
140+
yarn playwright install --with-deps
141+
```
142+
143+
### Running Playwright Tests
144+
145+
```bash
146+
cd spec/dummy
147+
148+
# Run all tests
149+
yarn playwright test
150+
151+
# Run tests in UI mode (interactive debugging)
152+
yarn playwright test --ui
153+
154+
# Run tests with visible browser
155+
yarn playwright test --headed
156+
157+
# Debug a specific test
158+
yarn playwright test --debug
159+
160+
# Run specific test file
161+
yarn playwright test e2e/playwright/e2e/react_on_rails/basic_components.spec.js
162+
```
163+
164+
### Writing Tests
165+
166+
Tests are located in `spec/dummy/e2e/playwright/e2e/`. The gem provides helpful commands for Rails integration:
167+
168+
```javascript
169+
import { test, expect } from "@playwright/test";
170+
import { app, appEval, appFactories } from '../../support/on-rails';
171+
172+
test.describe("My React Component", () => {
173+
test.beforeEach(async ({ page }) => {
174+
// Clean database before each test
175+
await app('clean');
176+
});
177+
178+
test("should interact with component", async ({ page }) => {
179+
// Create test data using factory_bot
180+
await appFactories([['create', 'user', { name: 'Test User' }]]);
181+
182+
// Or run arbitrary Ruby code
183+
await appEval('User.create!(email: "[email protected]")');
184+
185+
// Navigate and test
186+
await page.goto("/");
187+
const component = page.locator('#MyComponent-react-component-0');
188+
await expect(component).toBeVisible();
189+
});
190+
});
191+
```
192+
193+
### Available Rails Helpers
194+
195+
The `cypress-on-rails` gem provides these helpers (imported from `support/on-rails.js`):
196+
197+
- `app('clean')` - Clean database
198+
- `appEval(code)` - Run arbitrary Ruby code
199+
- `appFactories(options)` - Create records via factory_bot
200+
- `appScenario(name)` - Load predefined scenario
201+
- See `e2e/playwright/app_commands/` for available commands
202+
203+
### Creating App Commands
204+
205+
Add custom commands in `e2e/playwright/app_commands/`:
206+
207+
```ruby
208+
# e2e/playwright/app_commands/my_command.rb
209+
CypressOnRails::SmartFactoryWrapper.configure(
210+
always_reload: !Rails.configuration.cache_classes,
211+
factory: :factory_bot,
212+
dir: "{#{FactoryBot.definition_file_paths.join(',')}}"
213+
)
214+
215+
command 'my_command' do |options|
216+
# Your custom Rails code
217+
{ success: true, data: options }
218+
end
219+
```
220+
221+
### Test Organization
222+
223+
```
224+
spec/dummy/e2e/
225+
├── playwright.config.js # Playwright configuration
226+
├── playwright/
227+
│ ├── support/
228+
│ │ ├── index.js # Test setup
229+
│ │ └── on-rails.js # Rails helper functions
230+
│ ├── e2e/
231+
│ │ ├── react_on_rails/ # React on Rails specific tests
232+
│ │ │ └── basic_components.spec.js
233+
│ │ └── rails_examples/ # Example tests
234+
│ │ └── using_scenarios.spec.js
235+
│ └── app_commands/ # Rails helper commands
236+
│ ├── clean.rb
237+
│ ├── factory_bot.rb
238+
│ ├── eval.rb
239+
│ └── scenarios/
240+
│ └── basic.rb
241+
```
242+
243+
### Best Practices
244+
245+
- Use `app('clean')` in `beforeEach` to ensure clean state
246+
- Leverage Rails helpers (`appFactories`, `appEval`) instead of UI setup
247+
- Test React on Rails specific features: SSR, hydration, component registry
248+
- Use component IDs like `#ComponentName-react-component-0` for selectors
249+
- Monitor console errors during tests
250+
- Test across different browsers with `--project` flag
251+
252+
### Debugging
253+
254+
- Run in UI mode: `yarn playwright test --ui`
255+
- Use `page.pause()` to pause execution
256+
- Check `playwright-report/` for detailed results after test failures
257+
- Enable debug logging in `playwright.config.js`
258+
129259
## IDE Configuration
130260

131261
Exclude these directories to prevent IDE slowdowns:
132262

133263
- `/coverage`, `/tmp`, `/gen-examples`, `/packages/react-on-rails/lib`
134264
- `/node_modules`, `/spec/dummy/node_modules`, `/spec/dummy/tmp`
135265
- `/spec/dummy/app/assets/webpack`, `/spec/dummy/log`
266+
- `/spec/dummy/e2e/playwright-report`, `/spec/dummy/test-results`

Gemfile.development_dependencies

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ group :test do
4949
gem "capybara"
5050
gem "capybara-screenshot"
5151
gem "coveralls", require: false
52+
gem "cypress-on-rails", "~> 1.19"
5253
gem "equivalent-xml"
5354
gem "generator_spec"
5455
gem "launchy"

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ GEM
120120
thor (>= 0.19.4, < 2.0)
121121
tins (~> 1.6)
122122
crass (1.0.6)
123+
cypress-on-rails (1.19.0)
124+
rack
123125
date (3.3.4)
124126
debug (1.9.2)
125127
irb (~> 1.10)
@@ -410,6 +412,7 @@ DEPENDENCIES
410412
capybara
411413
capybara-screenshot
412414
coveralls
415+
cypress-on-rails (~> 1.19)
413416
debug
414417
equivalent-xml
415418
gem-release

spec/dummy/Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ GEM
122122
thor (>= 0.19.4, < 2.0)
123123
tins (~> 1.6)
124124
crass (1.0.6)
125+
cypress-on-rails (1.19.0)
126+
rack
125127
date (3.4.1)
126128
debug (1.9.2)
127129
irb (~> 1.10)
@@ -412,6 +414,7 @@ DEPENDENCIES
412414
capybara
413415
capybara-screenshot
414416
coveralls
417+
cypress-on-rails (~> 1.19)
415418
debug
416419
equivalent-xml
417420
generator_spec
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# frozen_string_literal: true
2+
3+
if defined?(CypressOnRails)
4+
CypressOnRails.configure do |c|
5+
c.api_prefix = ""
6+
c.install_folder = File.expand_path("#{__dir__}/../../e2e/playwright")
7+
# WARNING!! CypressOnRails can execute arbitrary ruby code
8+
# please use with extra caution if enabling on hosted servers or starting your local server on 0.0.0.0
9+
c.use_middleware = !Rails.env.production?
10+
# c.use_vcr_middleware = !Rails.env.production?
11+
# # Use this if you want to use use_cassette wrapper instead of manual insert/eject
12+
# # c.use_vcr_use_cassette_middleware = !Rails.env.production?
13+
# # Pass custom VCR options
14+
# c.vcr_options = {
15+
# hook_into: :webmock,
16+
# default_cassette_options: { record: :once },
17+
# cassette_library_dir: File.expand_path("#{__dir__}/../../e2e/playwright/fixtures/vcr_cassettes")
18+
# }
19+
c.logger = Rails.logger
20+
21+
# Server configuration for rake tasks (cypress:open, cypress:run, playwright:open, playwright:run)
22+
# c.server_host = 'localhost' # or use ENV['CYPRESS_RAILS_HOST']
23+
# c.server_port = 3001 # or use ENV['CYPRESS_RAILS_PORT']
24+
# c.transactional_server = true # Enable automatic transaction rollback between tests
25+
26+
# Server lifecycle hooks for rake tasks
27+
# c.before_server_start = -> { DatabaseCleaner.clean_with(:truncation) }
28+
# c.after_server_start = -> { puts "Test server started on port #{CypressOnRails.configuration.server_port}" }
29+
# c.after_transaction_start = -> { Rails.application.load_seed }
30+
# c.after_state_reset = -> { Rails.cache.clear }
31+
# c.before_server_stop = -> { puts "Stopping test server..." }
32+
33+
# If you want to enable a before_request logic, such as authentication, logging, sending metrics, etc.
34+
# Refer to https://www.rubydoc.info/gems/rack/Rack/Request for the `request` argument.
35+
# Return nil to continue through the Cypress command. Return a response [status, header, body] to halt.
36+
# c.before_request = lambda { |request|
37+
# unless request.env['warden'].authenticate(:secret_key)
38+
# return [403, {}, ["forbidden"]]
39+
# end
40+
# }
41+
end
42+
43+
# # if you compile your asssets on CI
44+
# if ENV['CYPRESS'].present? && ENV['CI'].present?
45+
# Rails.application.configure do
46+
# config.assets.compile = false
47+
# config.assets.unknown_asset_fallback = false
48+
# end
49+
# end
50+
end

0 commit comments

Comments
 (0)