Skip to content

Commit ab422f7

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 <noreply@anthropic.com>
1 parent 0b00514 commit ab422f7

File tree

21 files changed

+831
-0
lines changed

21 files changed

+831
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,7 @@ ssr-generated
7676
# Claude Code local settings
7777
.claude/settings.local.json
7878
.claude/.fuse_hidden*
79+
80+
# Playwright test artifacts (from cypress-on-rails gem)
81+
/spec/dummy/e2e/playwright-report/
82+
/spec/dummy/test-results/

CLAUDE.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Pre-commit hooks automatically run:
3333
- **Run tests**:
3434
- Ruby tests: `rake run_rspec`
3535
- JavaScript tests: `yarn run test` or `rake js_tests`
36+
- Playwright E2E tests: See Playwright section below
3637
- All tests: `rake` (default task runs lint and all tests except examples)
3738
- **Linting** (MANDATORY BEFORE EVERY COMMIT):
3839
- **REQUIRED**: `bundle exec rubocop` - Must pass with zero offenses
@@ -233,10 +234,140 @@ rm debug-*.js
233234
- Generated examples are in `gen-examples/` (ignored by git)
234235
- Only use `yarn` as the JS package manager, never `npm`
235236

237+
## Playwright E2E Testing
238+
239+
### Overview
240+
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.
241+
242+
### Setup
243+
The gem and Playwright are already configured. To install Playwright browsers:
244+
245+
```bash
246+
cd spec/dummy
247+
yarn playwright install --with-deps
248+
```
249+
250+
### Running Playwright Tests
251+
252+
```bash
253+
cd spec/dummy
254+
255+
# Run all tests
256+
yarn playwright test
257+
258+
# Run tests in UI mode (interactive debugging)
259+
yarn playwright test --ui
260+
261+
# Run tests with visible browser
262+
yarn playwright test --headed
263+
264+
# Debug a specific test
265+
yarn playwright test --debug
266+
267+
# Run specific test file
268+
yarn playwright test e2e/playwright/e2e/react_on_rails/basic_components.spec.js
269+
```
270+
271+
### Writing Tests
272+
273+
Tests are located in `spec/dummy/e2e/playwright/e2e/`. The gem provides helpful commands for Rails integration:
274+
275+
```javascript
276+
import { test, expect } from "@playwright/test";
277+
import { app, appEval, appFactories } from '../../support/on-rails';
278+
279+
test.describe("My React Component", () => {
280+
test.beforeEach(async ({ page }) => {
281+
// Clean database before each test
282+
await app('clean');
283+
});
284+
285+
test("should interact with component", async ({ page }) => {
286+
// Create test data using factory_bot
287+
await appFactories([['create', 'user', { name: 'Test User' }]]);
288+
289+
// Or run arbitrary Ruby code
290+
await appEval('User.create!(email: "test@example.com")');
291+
292+
// Navigate and test
293+
await page.goto("/");
294+
const component = page.locator('#MyComponent-react-component-0');
295+
await expect(component).toBeVisible();
296+
});
297+
});
298+
```
299+
300+
### Available Rails Helpers
301+
302+
The `cypress-on-rails` gem provides these helpers (imported from `support/on-rails.js`):
303+
304+
- `app('clean')` - Clean database
305+
- `appEval(code)` - Run arbitrary Ruby code
306+
- `appFactories(options)` - Create records via factory_bot
307+
- `appScenario(name)` - Load predefined scenario
308+
- See `e2e/playwright/app_commands/` for available commands
309+
310+
### Creating App Commands
311+
312+
Add custom commands in `e2e/playwright/app_commands/`:
313+
314+
```ruby
315+
# e2e/playwright/app_commands/my_command.rb
316+
CypressOnRails::SmartFactoryWrapper.configure(
317+
always_reload: !Rails.configuration.cache_classes,
318+
factory: :factory_bot,
319+
dir: "{#{FactoryBot.definition_file_paths.join(',')}}"
320+
)
321+
322+
command 'my_command' do |options|
323+
# Your custom Rails code
324+
{ success: true, data: options }
325+
end
326+
```
327+
328+
### Test Organization
329+
330+
```
331+
spec/dummy/e2e/
332+
├── playwright.config.js # Playwright configuration
333+
├── playwright/
334+
│ ├── support/
335+
│ │ ├── index.js # Test setup
336+
│ │ └── on-rails.js # Rails helper functions
337+
│ ├── e2e/
338+
│ │ ├── react_on_rails/ # React on Rails specific tests
339+
│ │ │ └── basic_components.spec.js
340+
│ │ └── rails_examples/ # Example tests
341+
│ │ └── using_scenarios.spec.js
342+
│ └── app_commands/ # Rails helper commands
343+
│ ├── clean.rb
344+
│ ├── factory_bot.rb
345+
│ ├── eval.rb
346+
│ └── scenarios/
347+
│ └── basic.rb
348+
```
349+
350+
### Best Practices
351+
352+
- Use `app('clean')` in `beforeEach` to ensure clean state
353+
- Leverage Rails helpers (`appFactories`, `appEval`) instead of UI setup
354+
- Test React on Rails specific features: SSR, hydration, component registry
355+
- Use component IDs like `#ComponentName-react-component-0` for selectors
356+
- Monitor console errors during tests
357+
- Test across different browsers with `--project` flag
358+
359+
### Debugging
360+
361+
- Run in UI mode: `yarn playwright test --ui`
362+
- Use `page.pause()` to pause execution
363+
- Check `playwright-report/` for detailed results after test failures
364+
- Enable debug logging in `playwright.config.js`
365+
236366
## IDE Configuration
237367

238368
Exclude these directories to prevent IDE slowdowns:
239369

240370
- `/coverage`, `/tmp`, `/gen-examples`, `/packages/react-on-rails/lib`
241371
- `/node_modules`, `/spec/dummy/node_modules`, `/spec/dummy/tmp`
242372
- `/spec/dummy/app/assets/webpack`, `/spec/dummy/log`
373+
- `/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)