playwright-on-rails
This is a downstream copy of cypress-rails, adapted to support Playwright instead of Cypress. All credit goes to them. Changes here will be kept at a minimum, and suggestions should probably be sent to that project.
This is a simple gem to make it easier to start writing browser tests with Playwright for your Rails apps, regardless of whether your app is server-side rendered HTML, completely client-side JavaScript, or something in-between.
Installation
tl;dr:
- Install the npm package
playwright
- Install this gem
playwright-on-rails
- Run
rake playwright:init
Installing Playwright itself
The first step is making sure Playwright is installed (that's up to you, this library doesn't install Playwright, it just provides a little Rails-specific glue).
If you're on newer versions of Rails and using webpacker for your front-end assets, then you're likely already using yarn to manage your JavaScript dependencies. If that's the case, you can add Playwright with:
$ yarn add --dev playwright
If you're not using yarn in conjunction with your Rails app, check out the
Playwright docs on getting it installed. At the end of the day, this gem just needs
the playwright
binary to exist either in ./node_modules/.bin/playwright
or on your
PATH
.
Installing the playwright-on-rails gem
Now, to install the playwright-on-rails gem, you'll want to add it to your development & test gem groups of your Gemfile, so that you have easy access to its rake tasks:
group :development, :test do
gem "playwright-on-rails"
end
Once installed, you'll want to run:
$ rake playwright:init
This will override a few configurations in your playwright.config.js
configuration
file.
Usage
Develop tests interactively with playwright open
When writing tests with Playwright, you'll find the most pleasant experience (by
way of a faster feedback loop and an interactive, easy-to-inspect test runner)
using the playwright open
command.
When using Rails, however, you'll also want your Rails test server to be running
so that there's something for Playwright to interact with. playwright-on-rails
provides
a wrapper for running playwright open
with a dedicated Rails test server.
So, by running:
$ rake playwright:open
Any JavaScript files added to playwright/tests
will be identified by
Playwright as tests. Simply click a test file in the Playwright application window to
launch the test in a browser. Each time you save the test file, it will re-run
itself.
Run tests headlessly with playwright run
To run your tests headlessly (e.g. when you're in CI), you'll want the run
command:
$ rake playwright:run
Managing your test data
The tricky thing about browser tests is that they usually depend on some test data being available with which to exercise the app efficiently. Because playwright is a JavaScript-based tool and can't easily manipulate your Rails app directly, playwright-on-rails provides a number of hooks that you can use to manage your test data.
Here's what a config/initializers/playwright-on-rails.rb
initializer might look
like:
return unless Rails.env.test?
PlaywrightOnRails.hooks.before_server_start do
# Called once, before either the transaction or the server is started
end
PlaywrightOnRails.hooks.after_transaction_start do
# Called after the transaction is started (at launch and after each reset)
end
PlaywrightOnRails.hooks.after_state_reset do
# Triggered after `/playwright_on_rails_reset_state` is called
end
PlaywrightOnRails.hooks.before_server_stop do
# Called once, at_exit
end
(You can find an example initializer in this repo.)
The gem also provides a special route on the test server:
/playwright_on_rails_reset_state
. Each time it's called, playwright-on-rails will do
two things at the beginning of the next request received by the Rails app:
If
PLAYWRIGHT_RAILS_TRANSACTIONAL_SERVER
is enabled, roll back the transaction, effectively resetting the application state to whatever it was at the start of the test runTrigger any
after_state_reset
hooks you've configured (regardless of the transactional server setting)
This way, you can easily instruct the server to reset its test state from your Playwright tests like so:
beforeEach(() => {
cy.request('/playwright_on_rails_reset_state')
})
(Remember, in Playwright, before
is a before-all hook and beforeEach
is run
between each test case!)
Configuration
Environment variables
The playwright-on-rails gem is configured entirely via environment variables. If you find yourself repeating a number of verbose environment variables as you run your tests, consider invoking the gem from a custom script or setting your preferred environment variables project-wide using a tool like dotenv.
- PLAYWRIGHT_RAILS_DIR (default:
Dir.pwd
) the directory of your Rails project - PLAYWRIGHT_RAILS_PLAYWRIGHT_DIR (default: same value as
rails_dir
) the directory of your Playwright project - PLAYWRIGHT_RAILS_HOST (default:
"127.0.0.1"
) the hostname to bind to - PLAYWRIGHT_RAILS_PORT (default: a random available port) the port to run the Rails test server on
- PLAYWRIGHT_RAILS_BASE_PATH (default:
"/"
) the base path for all Playwright's requests to the app (e.g. viacy.visit()
). If you've customized yourbaseUrl
setting (e.g. inplaywright.config.js
), you'll need to duplicate it with this environment variable - PLAYWRIGHT_RAILS_TRANSACTIONAL_SERVER (default:
true
) when true, will start a transaction on all database connections before launching the server. In general this means anything done duringplaywright open
orplaywright run
will be rolled back on exit (similar to running a Rails System test) - PLAYWRIGHT_RAILS_PLAYWRIGHT_OPTS (default: none) any options you want to
forward to the Playwright CLI when running its
open
orrun
commands.
Example: Running a single spec from the command line
It's a little verbose, but an example of using the above options to run a single Playwright test would look like this:
$ PLAYWRIGHT_RAILS_PLAYWRIGHT_OPTS="playwright/integration/a_test.js" bin/rake playwright:run
Initializer hooks
before_server_start
Pass a block to PlaywrightOnRails.hooks.before_server_start
to register a hook that
will execute before the server or any transaction has been started. If you use
Rails fixtures, it may make sense to load them here, so they don't need to be
re-inserted for each request
after_server_start
Pass a block to PlaywrightOnRails.hooks.after_server_start
to register a hook that
will execute after the server has booted.
after_transaction_start
If there's any custom behavior or state management you want to do inside the
transaction (so that it's also rolled back each time a reset is triggered),
pass a block to PlaywrightOnRails.hooks.after_transaction_start
.
after_state_reset
Every time the test server receives an HTTP request at
/playwright_on_rails_reset_state
, the transaction will be rolled back (if
PLAYWRIGHT_RAILS_TRANSACTIONAL_SERVER
is enabled) and the after_state_reset
hook will be triggered. To set up the hook, pass a block to
PlaywrightOnRails.hooks.after_state_reset
.
before_server_stop
In case you've made any permanent changes to your test database that could
pollute other test suites or scripts, you can use the before_server_stop
to
(assuming everything exits gracefully) clean things up and restore the state
of your test database. To set up the hook, pass a block to
PlaywrightOnRails.hooks.before_server_stop
.
Configuring Rails
Beyond the configuration options above, you'll probably also want to disable caching
in your Rails app's config/environments/test.rb
file, so that changes to your Ruby code are reflected in your tests while you
work on them with rake playwright:open
. (If either option is set to
true
, any changes to your Ruby code will require a server restart to be reflected as you work
on your tests.)
To illustrate, here's what that might look like in config/environments/test.rb
:
config.cache_classes = false
config.action_view.cache_template_loading = false
Setting up continuous integration
See original.
Why use this?
See original.