{"id":45246402,"url":"https://github.com/alec-c4/e2e","last_synced_at":"2026-02-20T22:26:03.478Z","repository":{"id":336640462,"uuid":"1150504981","full_name":"alec-c4/e2e","owner":"alec-c4","description":"Unified, high-performance E2E testing framework for Ruby.","archived":false,"fork":false,"pushed_at":"2026-02-13T13:58:53.000Z","size":149,"stargazers_count":18,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-13T20:50:48.386Z","etag":null,"topics":["playwright","ruby","ruby-on-rails","test-automation","testing"],"latest_commit_sha":null,"homepage":"https://alec-c4.com/projects/e2e","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alec-c4.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"alec-c4"}},"created_at":"2026-02-05T11:04:50.000Z","updated_at":"2026-02-13T13:58:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/alec-c4/e2e","commit_stats":null,"previous_names":["alec-c4/e2e"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/alec-c4/e2e","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Fe2e","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Fe2e/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Fe2e/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Fe2e/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alec-c4","download_url":"https://codeload.github.com/alec-c4/e2e/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Fe2e/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29667088,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T19:49:36.704Z","status":"ssl_error","status_checked_at":"2026-02-20T19:44:05.372Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["playwright","ruby","ruby-on-rails","test-automation","testing"],"created_at":"2026-02-20T22:26:02.836Z","updated_at":"2026-02-20T22:26:03.471Z","avatar_url":"https://github.com/alec-c4.png","language":"Ruby","readme":"# E2E\n\n**Unified, high-performance E2E testing framework for Ruby.**\n\n`e2e` is a modern wrapper around **Playwright**, designed to bring the elegance of Capybara and the speed of raw browser automation together.\n\n[![Gem Version](https://badge.fury.io/rb/e2e.svg)](https://badge.fury.io/rb/e2e)\n[![Build Status](https://github.com/alec-c4/e2e/actions/workflows/main.yml/badge.svg)](https://github.com/alec-c4/e2e/actions)\n\n## Why E2E?\n\n- **⚡️ Blazing Fast:** Uses direct IPC (Pipes) to communicate with the browser, avoiding HTTP overhead.\n- **🧩 Plug \u0026 Play:** Zero configuration for most Rails apps. Includes a generator.\n- **💎 Clean DSL:** Idiomatic Ruby API (`click_button`, `find`, `visit`) that feels like home.\n- **🚀 Modern Engine:** Powered by Microsoft Playwright (WebKit, Firefox, Chromium).\n- **🛠 Escape Hatch:** Direct access to the `native` Playwright object for **any** complex scenario.\n- **🔄 Shared Connection:** Built-in support for sharing DB connections between test and app threads (transactional tests support).\n- **👮‍♀️ Lint Friendly:** Includes auto-configuration for RuboCop to respect E2E testing patterns.\n\n## Installation\n\nAdd this line to your application's `Gemfile`:\n\n```ruby\ngroup :test do\n  gem \"e2e\"\nend\n```\n\nAnd then execute:\n\n```bash\nbundle install\nnpx playwright install # Install browser binaries\n```\n\n### Rails Setup\n\nRun the generator to create the helper file:\n\n```bash\nrails g e2e:install\n```\n\nThis will create `spec/e2e_helper.rb` (for RSpec) or `test/e2e_helper.rb` (for Minitest) and configure your `.rubocop.yml`.\n\n## RuboCop Integration\n\n`e2e` comes with a built-in RuboCop configuration that relaxes strict RSpec rules (like `DescribeClass` or `ExampleLength`) which are often not suitable for high-level E2E tests.\n\nThe generator adds this to your `.rubocop.yml` automatically:\n\n```yaml\ninherit_gem:\n  e2e: config/rubocop.yml\n```\n\nThis will apply the following changes to all files in `spec/e2e/` and `test/e2e/`:\n\n- Disable `RSpec/DescribeClass` and `RSpec/DescribeMethod`.\n- Disable `RSpec/ExampleLength` and `RSpec/MultipleExpectations`.\n- Increase `RSpec/NestedGroups` allowance.\n\n## Usage\n\nYou can generate a new test using:\n\n```bash\nrails g e2e:test UserLogin\n```\n\n### RSpec Example\n\nWrite your tests in `spec/e2e/`.\n\n```ruby\n# spec/e2e/login_spec.rb\nrequire \"e2e_helper\"\n\nRSpec.describe \"User Login\", type: :e2e do\n  it \"signs in successfully\" do\n    visit \"/login\"\n\n    fill_in \"Email\", with: \"user@example.com\"\n    fill_in \"Password\", with: \"password\"\n    click_button \"Sign In\"\n\n    expect(page).to have_current_path(\"/dashboard\")\n    expect(page).to have_content(\"Welcome, User!\")\n  end\nend\n```\n\n### Minitest Example\n\nWrite your tests in `test/e2e/`.\n\n```ruby\n# test/e2e/login_test.rb\nrequire \"e2e_helper\"\n\nclass UserLoginTest \u003c E2E::Minitest::TestCase\n  def test_sign_in\n    visit \"/login\"\n\n    fill_in \"Email\", with: \"user@example.com\"\n    fill_in \"Password\", with: \"password\"\n    click_button \"Sign In\"\n\n    assert_text \"Welcome, User!\"\n    assert_current_path \"/dashboard\"\n  end\nend\n```\n\n## Debugging \u0026 UI Mode\n\nBy default, tests run in **headless** mode (no browser window). For debugging, you can run tests in **headful** mode to see what's happening.\n\n### Running with a visible browser\n\nSet the `HEADLESS` environment variable to `false`:\n\n```bash\nHEADLESS=false bundle exec rspec spec/e2e\n# or for minitest\nHEADLESS=false ruby test/e2e/login_test.rb\n```\n\n### Pausing for Debugging\n\nIf the browser closes too fast, you can pause the execution to inspect the page or step through the test. This will open the **Playwright Inspector**.\n\nAdd this to your test:\n\n```ruby\nit \"debugs something\" do\n  visit \"/path\"\n  pause # The test will stop here, and a debugger UI will open\n  click_button \"Submit\"\nend\n```\n\nAlternatively, you can just use `sleep(10)` if you want the browser to stay open for a few seconds without opening the inspector.\n\n### Automatic Screenshots\n\nIf a test fails, a screenshot is automatically saved to `tmp/screenshots/` for quick investigation.\n\n### API Reference\n\n#### Navigation\n\n```ruby\nvisit(\"/path\")\ncurrent_url\n```\n\n#### Interaction\n\n```ruby\nclick_button \"Submit\"\nclick_link \"Read more\"\nclick \"#nav-menu\" # CSS selector\n\nfill_in \"Email\", with: \"test@example.com\"  # Matches by label, placeholder, id, or name\nfill_in \"#email\", with: \"test@example.com\" # CSS selector fallback\ncheck \"I agree\"\nuncheck \"Subscribe\"\nattach_file \"#upload\", \"path/to/file.png\"\n```\n\n#### Finding Elements\n\n```ruby\nfind(\".btn\")          # Returns E2E::Element\nall(\"li\")             # Returns Array\u003cE2E::Element\u003e\nfind(\"button\", text: \"Save\") # Filter by text\n```\n\n#### RSpec Matchers\n\nAll text and path matchers **automatically wait** for the expected condition to be met (up to `wait_timeout` seconds), making your tests resilient to page transitions and async rendering.\n\n```ruby\n# Check for content (auto-waiting)\nexpect(page).to have_text(\"Success\")\nexpect(page).to have_content(\"Success\")         # Alias for have_text\nexpect(find(\".alert\")).to have_text(\"Success\")   # Works on elements too\nexpect(page).to have_text(/welcome/i)            # Regexp support\n\n# Check current path (auto-waiting)\nexpect(page).to have_current_path(\"/dashboard\")\nexpect(page).to have_current_path(/\\/users\\/\\d+/) # Regexp support\n\n# Check for classes\nexpect(find(\".alert\")).to have_class(\"success\")\nexpect(find(\".alert\")).not_to have_class(\"error\")\n\n# Check visibility\nexpect(find(\"#modal\")).to be_visible\n\n# Check attributes \u0026 value\nexpect(find(\"input\")).to have_value(\"test\")\nexpect(find(\"div\")).to have_attribute(\"data-id\", \"123\")\n\n# Check states\nexpect(find(\"#checkbox\")).to be_checked\nexpect(find(\"button\")).to be_disabled\nexpect(find(\"input\")).to be_enabled\n```\n\n#### Minitest Assertions\n\nThese assertions mimic the behavior of RSpec matchers, including **auto-waiting**.\n\n```ruby\n# Check for content (auto-waiting)\nassert_text \"Welcome\"\nrefute_text \"Error\"\nassert_text /welcome/i\n\n# Check for specific elements (auto-waiting)\nassert_selector \".user-profile\"\nrefute_selector \"#loading-spinner\"\n\n# Check current path (auto-waiting)\nassert_current_path \"/dashboard\"\nassert_current_path /\\/users\\/\\d+/\nrefute_current_path \"/login\"\n```\n\n#### Assertions \u0026 Data\n\n```ruby\npage.body             # Full HTML\nevaluate(\"document.title\") # Execute JS\nsave_screenshot(\"tmp/screen.png\")\n```\n\n### Auto-Waiting\n\nThe `have_text`, `have_content`, and `have_current_path` matchers automatically retry until the condition is met or the configured `wait_timeout` expires (default: 5 seconds). This eliminates flaky tests caused by page navigations, redirects, and async rendering.\n\n```ruby\nclick_button \"Submit\"\n# No need for sleep or manual waiting — the matcher will poll until\n# the page transitions and the expected content appears\nexpect(page).to have_current_path(\"/success\")\nexpect(page).to have_content(\"Your order has been placed\")\n```\n\nFor custom waiting logic, use `E2E.wait_until`:\n\n```ruby\nE2E.wait_until(timeout: 10) do\n  page.current_url.include?(\"/ready\")\nend\n```\n\nOr use built-in DSL helpers that raise clear errors on timeout:\n\n```ruby\nwait_for { SomeModel.count \u003e 0 }\nwait_for_text(\"Saved\")\nwait_for_current_path(\"/dashboard\")\nwait_for_flash(\"Profile updated\", type: :notice)\n\nclick_button_and_wait_for_text(\"Save\", \"Saved\")\nclick_link_and_wait_for_text(\"Next\", /done/i)\nclick_button_and_wait_for_path(\"Submit\", \"/dashboard\")\nclick_link_and_wait_for_path(\"Continue\", /checkout/)\nclick_button_and_wait_for_flash(\"Save\", \"Updated\", type: :notice)\nclick_link_and_wait_for_flash(\"Delete\", /failed/i, type: :alert)\n```\n\n### 🔓 Native Access (The Escape Hatch)\n\nWe believe you shouldn't be limited by the wrapper. You can access the underlying `Playwright::Page` object at any time using `.native`.\n\n**This means you have access to 100% of Playwright's features.**\n\n#### 1. Keyboard Shortcuts\n\n```ruby\nfind(\"input\").click\npage.native.keyboard.press(\"Enter\")\npage.native.keyboard.press(\"Control+C\")\n```\n\n#### 2. Network Interception (Mocking)\n\n```ruby\npage.native.route(\"**/api/users\") do |route|\n  route.fulfill(status: 200, body: '{\"users\": []}')\nend\n```\n\n#### 3. Handling Dialogs (Alerts/Confirms)\n\n```ruby\npage.native.on(\"dialog\") do |dialog|\n  dialog.accept\nend\nclick_button \"Delete Account\"\n```\n\n#### 4. Emulation (Mobile/Dark Mode)\n\n```ruby\npage.native.viewport_size = { width: 375, height: 667 }\npage.native.emulate_media(color_scheme: 'dark')\n```\n\n## Performance: Transactional Tests\n\nBy default, Rails system tests run in a separate thread, meaning they can't see data created in a database transaction.\n`e2e` solves this with **Shared Connection**.\n\nEnable it in your `spec/e2e_helper.rb`:\n\n```ruby\nE2E.enable_shared_connection!\n```\n\nNow you can use fast transactional tests (standard RSpec behavior) instead of slow `DatabaseCleaner` truncation strategies.\n\n## Configuration\n\n```ruby\nE2E.configure do |config|\n  config.driver = :playwright\n  config.browser_type = :chromium # Options: :chromium (default), :firefox, :webkit\n  config.headless = ENV.fetch(\"HEADLESS\", \"true\") == \"true\"\n  config.app = Rails.application # Automatic Rack booting\n  config.wait_timeout = 5 # Seconds to wait in auto-waiting matchers (default: 5)\n  config.flash_selectors = {\n    any: \"[role='alert'], [role='status'], .flash\",\n    notice: \"[role='status'], .flash.notice\",\n    alert: \"[role='alert'], .flash.alert\"\n  }\nend\n```\n\n### Multiple Browsers\n\nOne of the key advantages of Playwright is true cross-browser testing. You can easily switch engines:\n\n- **Chromium:** Google Chrome, Microsoft Edge, etc.\n- **Firefox:** Mozilla Firefox.\n- **WebKit:** Safari, iOS browsers.\n\nTo test in Safari (WebKit):\n\n```ruby\nE2E.configure { |config| config.browser_type = :webkit }\n```\n\nDon't forget to install all browser binaries:\n\n```bash\nnpx playwright install\n```\n\n### Switching Browsers per Test (RSpec)\n\nYou can run specific tests in different browsers using metadata (requires simple setup in your helper).\n\nFirst, update your `spec/e2e_helper.rb` to respect metadata:\n\n```ruby\nRSpec.configure do |config|\n  config.before(:each) do |example|\n    browser = example.metadata[:browser]\n    if browser\n      E2E.config.browser_type = browser\n      E2E.reset_session! # Force new browser launch\n    end\n  end\n\n  config.after(:each) do\n    if E2E.config.browser_type != :chromium\n      E2E.config.browser_type = :chromium\n      E2E.reset_session!\n    end\n  end\nend\n```\n\nThen use it in your specs:\n\n```ruby\nit \"works in Safari\", browser: :webkit do\n  visit \"/\"\n  expect(page.body).to include(\"Safari specific feature\")\nend\n```\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","funding_links":["https://github.com/sponsors/alec-c4"],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falec-c4%2Fe2e","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falec-c4%2Fe2e","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falec-c4%2Fe2e/lists"}