{"id":13509258,"url":"https://github.com/elixir-wallaby/wallaby","last_synced_at":"2025-05-12T13:23:08.316Z","repository":{"id":37550425,"uuid":"52619134","full_name":"elixir-wallaby/wallaby","owner":"elixir-wallaby","description":"Concurrent browser tests for your Elixir web apps.","archived":false,"fork":false,"pushed_at":"2025-02-12T02:43:44.000Z","size":1255,"stargazers_count":1730,"open_issues_count":53,"forks_count":203,"subscribers_count":29,"default_branch":"main","last_synced_at":"2025-05-09T20:05:26.654Z","etag":null,"topics":["ecto","elixir","hacktoberfest","phoenix","testing","wallaby"],"latest_commit_sha":null,"homepage":"https://twitter.com/elixir_wallaby","language":"Elixir","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/elixir-wallaby.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","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},"funding":{"github":["mhanberg"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2016-02-26T16:57:42.000Z","updated_at":"2025-05-06T04:53:03.000Z","dependencies_parsed_at":"2023-02-12T05:16:47.023Z","dependency_job_id":"8d74c952-f695-4706-8bd6-de1b6678f10f","html_url":"https://github.com/elixir-wallaby/wallaby","commit_stats":{"total_commits":510,"total_committers":80,"mean_commits":6.375,"dds":0.6686274509803922,"last_synced_commit":"296ac2de703d07439161f425124205e02bc735ed"},"previous_names":["keathley/wallaby"],"tags_count":50,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-wallaby%2Fwallaby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-wallaby%2Fwallaby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-wallaby%2Fwallaby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-wallaby%2Fwallaby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-wallaby","download_url":"https://codeload.github.com/elixir-wallaby/wallaby/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253487128,"owners_count":21916147,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["ecto","elixir","hacktoberfest","phoenix","testing","wallaby"],"created_at":"2024-08-01T02:01:05.307Z","updated_at":"2025-05-12T13:23:08.285Z","avatar_url":"https://github.com/elixir-wallaby.png","language":"Elixir","readme":"# ![Wallaby](https://i.imgur.com/eQ1tlI3.png)\n\n[![Actions Status](https://github.com/elixir-wallaby/wallaby/workflows/CI/badge.svg)](https://github.com/elixir-wallaby/wallaby/actions)\n[![Module Version](https://img.shields.io/hexpm/v/wallaby.svg)](https://hex.pm/packages/wallaby)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/wallaby/)\n[![License](https://img.shields.io/hexpm/l/wallaby.svg)](https://github.com/elixir-wallaby/wallaby/blob/master/LICENSE)\n\nWallaby helps you test your web applications by simulating realistic user interactions.\nBy default it runs each test case concurrently and manages browsers for you.\nHere's an example test for a simple Todo application:\n\n```elixir\ndefmodule MyApp.Features.TodoTest do\n  use ExUnit.Case, async: true\n  use Wallaby.Feature\n\n  import Wallaby.Query, only: [css: 2, text_field: 1, button: 1]\n\n  feature \"users can create todos\", %{session: session} do\n    session\n    |\u003e visit(\"/todos\")\n    |\u003e fill_in(text_field(\"New Todo\"), with: \"Write my first Wallaby test\")\n    |\u003e click(button(\"Save\"))\n    |\u003e assert_has(css(\".alert\", text: \"You created a todo\"))\n    |\u003e assert_has(css(\".todo-list \u003e .todo\", text: \"Write my first Wallaby test\"))\n  end\nend\n```\n\nBecause Wallaby manages multiple browsers for you, its possible to test several users interacting with a page simultaneously.\n\n```elixir\ndefmodule MyApp.Features.MultipleUsersTest do\n  use ExUnit.Case, async: true\n  use Wallaby.Feature\n\n  import Wallaby.Query, only: [text_field: 1, button: 1, css: 2]\n\n  @page message_path(Endpoint, :index)\n  @message_field text_field(\"Share Message\")\n  @share_button button(\"Share\")\n\n  def message(msg), do: css(\".messages \u003e .message\", text: msg)\n\n  @sessions 2\n  feature \"That users can send messages to each other\", %{sessions: [user1, user2]} do\n    user1\n    |\u003e visit(@page)\n    |\u003e fill_in(@message_field, with: \"Hello there!\")\n    |\u003e click(@share_button)\n\n    user2\n    |\u003e visit(@page)\n    |\u003e fill_in(@message_field, with: \"Hello yourself\")\n    |\u003e click(@share_button)\n\n    user1\n    |\u003e assert_has(message(\"Hello yourself\"))\n\n    user2\n    |\u003e assert_has(message(\"Hello there!\"))\n  end\nend\n```\n\nRead on to see what else Wallaby can do or check out the [Official Documentation](https://hexdocs.pm/wallaby).\n\n## Sponsors\n\n_Your company's name and logo could be here!_\n\n## Setup\n\n### Requirements\n\nWallaby requires Elixir 1.12+ and OTP 22+.\n\nWallaby also requires `bash` to be installed. Generally `bash` is widely available, but it does not come pre-installed on Alpine Linux.\n\n### Installation\n\nAdd Wallaby to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:wallaby, \"~\u003e 0.30\", runtime: false, only: :test}\n  ]\nend\n```\n\nConfigure the driver.\n\n```elixir\n# Chrome\nconfig :wallaby, driver: Wallaby.Chrome # default\n\n# Selenium\nconfig :wallaby, driver: Wallaby.Selenium\n```\n\nYou'll need to install the actual drivers as well.\n\n- Chrome\n  - [`chromedriver`](https://chromedriver.chromium.org/downloads)\n\n- Selenium\n  - [`selenium`](https://www.selenium.dev/downloads/)\n  - [`geckodriver`](https://github.com/mozilla/geckodriver) (for Firefox) or [`chromedriver`](https://chromedriver.chromium.org/downloads) (for Chrome)\n\nEnsure that Wallaby is started in your `test_helper.exs`:\n\n```elixir\n{:ok, _} = Application.ensure_all_started(:wallaby)\n```\n\nWhen calling `use Wallaby.Feature` and using Ecto, please configure your `otp_app`.\n\n```elixir\nconfig :wallaby, otp_app: :your_app\n```\n\n### Phoenix\n\nEnable Phoenix to serve endpoints in tests:\n\n```elixir\n# config/test.exs\n\nconfig :your_app, YourAppWeb.Endpoint,\n  server: true\n```\n\nIn your `test_helper.exs` you can provide some configuration to Wallaby.\nAt a minimum, you need to specify a `:base_url`, so Wallaby knows how to resolve relative paths.\n\n```elixir\n# test/test_helper.exs\n\nApplication.put_env(:wallaby, :base_url, YourAppWeb.Endpoint.url)\n```\n\n#### Ecto\n\nIf you're testing a Phoenix application with Ecto and a database that [supports sandbox mode](https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html), you can enable concurrent testing by adding the `Phoenix.Ecto.SQL.Sandbox` plug to your `Endpoint`.\nIt's important that this is at the top of `endpoint.ex` before any other plugs.\n\n```elixir\n# lib/your_app_web/endpoint.ex\n\ndefmodule YourAppWeb.Endpoint do\n  use Phoenix.Endpoint, otp_app: :your_app\n\n  if sandbox = Application.compile_env(:your_app, :sandbox, false) do\n    plug Phoenix.Ecto.SQL.Sandbox, sandbox: sandbox\n  end\n\n  # ...\n\n  socket(\"/live\", Phoenix.LiveView.Socket,\n    websocket: [connect_info: [:user_agent, session: @session_options]]\n  )\n```\n\nIt's also important to make sure the `user_agent` is passed in the `connect_info` in order to allow the database and browser session to be wired up correctly.\n\nThen make sure sandbox is enabled:\n\n```elixir\n# config/test.exs\n\nconfig :your_app, :sandbox, Ecto.Adapters.SQL.Sandbox\n```\n\nThis enables the database connection to be owned by the process that is running your test, but the connection is shared to the process receiving the HTTP requests from the browser, so that the same data is visible in both processes.\n\nIf you have other resources that should be shared by both processes (e.g. expectations or stubs if using [Mox](https://hexdocs.pm/mox/Mox.html)), then you can define a custom sandbox module:\n\n```elixir\n# test/support/sandbox.ex\n\ndefmodule YourApp.Sandbox do\n  def allow(repo, owner_pid, child_pid) do\n    # Delegate to the Ecto sandbox\n    Ecto.Adapters.SQL.Sandbox.allow(repo, owner_pid, child_pid)\n\n    # Add custom process-sharing configuration\n    Mox.allow(MyMock, owner_pid, child_pid)\n  end\nend\n```\n\nAnd update the test config to use your custom sandbox:\n\n```elixir\n# config/test.exs\n\nconfig :your_app, :sandbox, YourApp.Sandbox\n```\n\n#### Assets\n\nAssets are not re-compiled when you run `mix test`.\nThis can lead to confusion if you've made changes in JavaScript or CSS but tests are still failing.\nThere are two common ways to avoid this confusion.\n\n##### esbuild\n\nIf you're using [`esbuild`](https://hex.pm/packages/esbuild) you can add `esbuild default` to the `test` alias in the mix config file.\n\n```elixir\n  defp aliases do\n    [\n      \"test\": [\n        \"esbuild default\",\n        \"ecto.create --quiet\",\n        \"ecto.migrate\",\n        \"test\",\n      ]\n    ]\n  end\n```\n\n##### Webpack\n\nThe first solution is to run `webpack --mode development --watch` from the assets directory.\nThis will ensure that assets get recompiled after any changes.\n\nThe second solution is to add a new alias to your mix config that recompiles assets for you:\n\n```elixir\n  def project do\n    [\n      app: :my_app,\n      version: \"1.0.0\",\n      aliases: aliases()\n    ]\n  end\n\n  defp aliases, do: [\n    \"test\": [\n      \"assets.compile --quiet\",\n      \"ecto.create --quiet\",\n      \"ecto.migrate\",\n      \"test\",\n    ],\n    \"assets.compile\": \u0026compile_assets/1\n  ]\n\n  defp compile_assets(_) do\n    Mix.shell().cmd(\"cd assets \u0026\u0026 ./node_modules/.bin/webpack --mode development\",\n      quiet: true\n    )\n  end\n```\n\nThis method is less error prone but it will cause a delay when starting your test suite.\n\n#### LiveView\n\nIn order to test Phoenix LiveView (as of [version 0.17.7](https://github.com/phoenixframework/phoenix_live_view/releases/tag/v0.17.7)) with Wallaby you'll also need to add a function to each `mount/3` function in your LiveViews, or use the `on_mount` `live_session` lifecycle hook in the router:\n\n```elixir\ndefmodule MyApp.Hooks.AllowEctoSandbox do\n  import Phoenix.LiveView\n  import Phoenix.Component\n\n  def on_mount(:default, _params, _session, socket) do\n    allow_ecto_sandbox(socket)\n    {:cont, socket}\n  end\n\n  defp allow_ecto_sandbox(socket) do\n    %{assigns: %{phoenix_ecto_sandbox: metadata}} =\n      assign_new(socket, :phoenix_ecto_sandbox, fn -\u003e\n        if connected?(socket), do: get_connect_info(socket, :user_agent)\n      end)\n\n    Phoenix.Ecto.SQL.Sandbox.allow(metadata, Application.get_env(:your_app, :sandbox))\n  end\nend\n```\n\nand then including the function usage in the router:\n\n```elixir\nlive_session :default, on_mount: MyApp.Hooks.AllowEctoSandbox do\n  # ...\nend\n```\n\n#### Umbrella Apps\n\nIf you're testing an umbrella application containing a Phoenix application for the web interface (`MyWebApp`) and a separate persistence application (`MyPersistenceApp`) using Ecto 2 or 3 with a database that supports sandbox mode, then you can use the same setup as above, with a few tweaks.\n\n```elixir\n# my_web_app/lib/endpoint.ex\n\ndefmodule MyWebApp.Endpoint do\n  use Phoenix.Endpoint, otp_app: :my_web_app\n\n  if Application.get_env(:my_persistence_app, :sql_sandbox) do\n    plug Phoenix.Ecto.SQL.Sandbox\n  end\n```\n\nMake sure `MyWebApp` is set up to serve endpoints in tests and that the SQL sandbox is enabled:\n\n```elixir\n# my_web_app/config/test.exs\n\nconfig :my_web_app, MyWebApp.Endpoint,\n  server: true\n\nconfig :my_persistence_app, :sql_sandbox, true\n```\n\nThen in `MyWebApp`'s `test_helper.exs` you can provide some configuration to Wallaby.\nAt minimum, you need to specify a `:base_url`, so Wallaby knows how to resolve relative paths.\n\n```elixir\n# my_web_app/test/test_helper.exs\n\nApplication.put_env(:wallaby, :base_url, MyWebApp.Endpoint.url)\n```\n\nYou will also want to add `phoenix_ecto` as a dependency to `MyWebApp`:\n\n```elixir\n# my_web_app/mix.exs\n\ndef deps do\n  [\n    {:phoenix_ecto, \"~\u003e 3.0\", only: :test}\n  ]\nend\n```\n\n### Writing tests\n\nIt's easiest to add Wallaby to your test suite by using the `Wallaby.Feature` module.\n\n```elixir\ndefmodule YourApp.UserListTest do\n  use ExUnit.Case, async: true\n  use Wallaby.Feature\n\n  feature \"users have names\", %{session: session} do\n    session\n    |\u003e visit(\"/users\")\n    |\u003e find(Query.css(\".user\", count: 3))\n    |\u003e List.first()\n    |\u003e assert_has(Query.css(\".user-name\", text: \"Chris\"))\n  end\nend\n```\n\n## API\n\nThe full documentation for the DSL is in the [official documentation](https://hexdocs.pm/wallaby).\n\n### Queries and Actions\n\nWallaby's API is broken into 2 concepts: Queries and Actions.\n\nQueries allow us to declaratively describe the elements that we would like to interact with and Actions allow us to use those queries to interact with the DOM.\n\nLets say that our html looks like this:\n\n```html\n\u003cul class=\"users\"\u003e\n  \u003cli class=\"user\"\u003e\n    \u003cspan class=\"user-name\"\u003eAda\u003c/span\u003e\n  \u003c/li\u003e\n  \u003cli class=\"user\"\u003e\n    \u003cspan class=\"user-name\"\u003eGrace\u003c/span\u003e\n  \u003c/li\u003e\n  \u003cli class=\"user\"\u003e\n    \u003cspan class=\"user-name\"\u003eAlan\u003c/span\u003e\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\nIf we wanted to interact with all of the users then we could write a query like so `css(\".user\", count: 3)`.\n\nIf we only wanted to interact with a specific user then we could write a query like this `css(\".user-name\", count: 1, text: \"Ada\")`. Now we can use those queries with some actions:\n\n```elixir\nsession\n|\u003e find(css(\".user\", count: 3))\n|\u003e List.first\n|\u003e assert_has(css(\".user-name\", count: 1, text: \"Ada\"))\n```\n\nThere are several queries for common html elements defined in the [Query module](https://hexdocs.pm/wallaby/Wallaby.Query.html#content).\nAll actions accept a query.\nThis makes it easy to use queries we've already defined.\nActions will block until the query is either satisfied or the action times out.\nBlocking reduces race conditions when elements are added or removed dynamically.\n\n### Navigation\n\nWe can navigate directly to pages with `visit`:\n\n```elixir\nvisit(session, \"/page.html\")\nvisit(session, user_path(Endpoint, :index, 17))\n```\n\nIt's also possible to click links directly:\n\n```elixir\nclick(session, link(\"Page 1\"))\n```\n\n### Finding\n\nWe can find a specific element or list of elements with `find`:\n\n```elixir\n@user_form   css(\".user-form\")\n@name_field  text_field(\"Name\")\n@email_field text_field(\"Email\")\n@save_button button(\"Save\")\n\nfind(page, @user_form, fn(form) -\u003e\n  form\n  |\u003e fill_in(@name_field, with: \"Chris\")\n  |\u003e fill_in(@email_field, with: \"c@keathley.io\")\n  |\u003e click(@save_button)\nend)\n```\n\nPassing a callback to `find` will return the parent which makes it easy to chain `find` with other actions:\n\n```elixir\npage\n|\u003e find(css(\".users\"), \u0026 assert has?(\u00261, css(\".user\", count: 3)))\n|\u003e click(link(\"Next Page\"))\n```\n\nWithout the callback `find` returns the element.\nThis provides a way to scope all future actions within an element.\n\n```elixir\npage\n|\u003e find(css(\".user-form\"))\n|\u003e fill_in(text_field(\"Name\"), with: \"Chris\")\n|\u003e fill_in(text_field(\"Email\"), with: \"c@keathley.io\")\n|\u003e click(button(\"Save\"))\n```\n\n### Interacting with forms\n\nThere are a few ways to interact with form elements on a page:\n\n```elixir\nfill_in(session, text_field(\"First Name\"), with: \"Chris\")\nclear(session, text_field(\"last_name\"))\nclick(session, option(\"Some option\"))\nclick(session, radio_button(\"My Fancy Radio Button\"))\nclick(session, button(\"Some Button\"))\n```\n\nIf you need to send specific keys to an element, you can do that with `send_keys`:\n\n```elixir\nsend_keys(session, [\"Example\", \"Text\", :enter])\n```\n\n### Assertions\n\nWallaby provides custom assertions to make writing tests easier:\n\n```elixir\nassert_has(session, css(\".signup-form\"))\nrefute_has(session, css(\".alert\"))\nhas?(session, css(\".user-edit-modal\", visible: false))\n```\n\n`assert_has` and `refute_has` both take a parent element as their first argument.\nThey return that parent, making it easy to chain them together with other actions.\n\n```elixir\nsession\n|\u003e assert_has(css(\".signup-form\"))\n|\u003e fill_in(text_field(\"Email\", with: \"c@keathley.io\"))\n|\u003e click(button(\"Sign up\"))\n|\u003e refute_has(css(\".error\"))\n|\u003e assert_has(css(\".alert\", text: \"Welcome!\"))\n```\n\n### Window Size\n\nYou can set the default window size by passing in the `window_size` option into `Wallaby.start_session\\1`.\n\n```elixir\nWallaby.start_session(window_size: [width: 1280, height: 720])\n```\n\nYou can also resize the window and get the current window size during the test.\n\n```elixir\nresize_window(session, 100, 100)\nwindow_size(session)\n```\n\n### Screenshots\n\nIt's possible take screenshots:\n\n```elixir\ntake_screenshot(session)\n```\n\nAll screenshots are saved to a `screenshots` directory in the directory that the tests were run in.\n\nIf you want to customize the screenshot directory you can pass it as a config value:\n\n```elixir\n# config/test.exs\nconfig :wallaby, screenshot_dir: \"/file/path\"\n\n# test_helper.exs\nApplication.put_env(:wallaby, :screenshot_dir, \"/file/path\")\n```\n\n### Automatic screenshots\n\nYou can automatically take screenshots on an error when using the `Wallaby.Feature.feature/3` macro.\n\n```elixir\n# config/test.exs\nconfig :wallaby, screenshot_on_failure: true\n\n# test_helper.exs\nApplication.put_env(:wallaby, :screenshot_on_failure, true)\n```\n\n## JavaScript\n\n### Asynchronous code\n\nTesting asynchronous JavaScript code can expose timing issues and race conditions.\nWe might try to interact with an element that hasn't yet appeared on the page.\nElements can become stale while we're trying to interact with them.\n\nWallaby helps solve this by blocking.\nInstead of manually setting timeouts we can use `assert_has` and some declarative queries to block until the DOM is in a good state.\n\n```elixir\nsession\n|\u003e click(button(\"Some Async Button\"))\n|\u003e assert_has(css(\".async-result\"))\n|\u003e click(button(\"Next Action\"))\n```\n\n### Interacting with dialogs\n\nWallaby provides several ways to interact with JavaScript dialogs such as `window.alert`, `window.confirm` and `window.prompt`.\n\nYou can use one of the following functions:\n\n- For `window.alert` use `accept_alert/2`\n- For `window.confirm` use `accept_confirm/2` or `dismiss_confirm/2`\n- For `window.prompt` use `accept_prompt/2-3` or `dismiss_prompt/2`\n\nAll of these take a function as last parameter, which must include the necessary interactions to trigger the dialog. For example:\n\n```elixir\nalert_message = accept_alert session, fn(session) -\u003e\n  click(session, link(\"Trigger alert\"))\nend\n```\n\nTo emulate user input for a prompt, `accept_prompt` takes an optional parameter:\n\n```elixir\nprompt_message = accept_prompt session, [with: \"User input\"], fn(session) -\u003e\n  click(session, link(\"Trigger prompt\"))\nend\n```\n\n### JavaScript logging and errors\n\nWallaby captures both JavaScript logs and errors.\nAny uncaught exceptions in JavaScript will be re-thrown in Elixir.\nThis can be disabled by specifying `js_errors: false` in your Wallaby config.\n\nJavaScript logs are written to :stdio by default.\nThis can be changed to any IO device by setting the `:js_logger` option in your Wallaby config.\nFor instance if you want to write all JavaScript console logs to a file you could do something like this:\n\n```elixir\n{:ok, file} = File.open(\"browser_logs.log\", [:write])\nApplication.put_env(:wallaby, :js_logger, file)\n```\n\nLogging can be disabled by setting `:js_logger` to `nil`.\n\n### Enabling WebAuthn Virtual Authenticator (Chrome only)\n\nWhen wanting to test Passkeys with Wallaby, you have to make sure WebAuthn Virtual Authenticator is enabled. You must execute this code to enable this feature in Chrome via the Chromedriver. This configuration will make Chrome automatically present a virtual Passkey whenever WebAuthn [create()](https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create) or [get()](https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get) APIs are called in browser.\n\n```elixir\n{:ok, _result} =\n  Wallaby.HTTPClient.request(:post, \"#{session.url}/chromium/send_command_and_get_result\", %{\n    cmd: \"WebAuthn.enable\",\n    params: %{}\n  })\n\n{:ok, result} =\n  Wallaby.HTTPClient.request(:post, \"#{session.url}/chromium/send_command_and_get_result\", %{\n    cmd: \"WebAuthn.addVirtualAuthenticator\",\n    params: %{\n      options: %{\n        protocol: \"ctap2\",\n        transport: \"internal\",\n        hasResidentKey: true,\n        hasUserVerification: true,\n        isUserVerified: true,\n        automaticPresenceSimulation: true\n      }\n    }\n  })\n```\n\n## Configuration\n\n### Adjusting timeouts\n\nWallaby uses [hackney](https://github.com/benoitc/hackney) under the hood, so we offer a hook that allows you to control any hackney options you'd like to have sent along on every request.\nThis can be controlled with the `:hackney_options` setting in `config.exs`.\n\n```elixir\nconfig :wallaby,\n  hackney_options: [timeout: :infinity, recv_timeout: :infinity]\n\n# Overriding a value\nconfig :wallaby,\n  hackney_options: [timeout: 5_000]\n```\n\n## Contributing\n\nWallaby is a community project. Pull Requests (PRs) and reporting issues are greatly welcome.\n\nTo get started and setup the project, make sure you've got Elixir 1.7+ installed and then:\n\n### Development Dependencies\n\nWallaby requires the following tools.\n\n- ChromeDriver\n- Google Chrome\n- GeckoDriver\n- Mozilla Firefox\n- selenium-server-standalone\n\n```shell\n# Unit tests\n$ mix test\n\n# Integration tests for all drivers\n$ mix test.drivers\n\n# Integration tests for a specific driver\n$ WALLABY_DRIVER=chrome mix test\n$ WALLABY_DRIVER=selenium mix test\n\n# All tests\n$ mix test.all\n```\n\n### Helpful Links\n\n- [ChromeDriver Issue Tracker](https://issues.chromium.org/issues?q=status:open%20componentid:1608258\u0026s=created_time:desc)\n","funding_links":["https://github.com/sponsors/mhanberg"],"categories":["Testing","Elixir"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-wallaby%2Fwallaby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-wallaby%2Fwallaby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-wallaby%2Fwallaby/lists"}