# wallaby **Repository Path**: markhoo/wallaby ## Basic Information - **Project Name**: wallaby - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-28 - **Last Updated**: 2025-07-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #  [](https://github.com/elixir-wallaby/wallaby/actions) [](https://hex.pm/packages/wallaby) [](https://hexdocs.pm/wallaby/) [](https://github.com/elixir-wallaby/wallaby/blob/master/LICENSE) Wallaby helps you test your web applications by simulating realistic user interactions. By default it runs each test case concurrently and manages browsers for you. Here's an example test for a simple Todo application: ```elixir defmodule MyApp.Features.TodoTest do use ExUnit.Case, async: true use Wallaby.Feature import Wallaby.Query, only: [css: 2, text_field: 1, button: 1] feature "users can create todos", %{session: session} do session |> visit("/todos") |> fill_in(text_field("New Todo"), with: "Write my first Wallaby test") |> click(button("Save")) |> assert_has(css(".alert", text: "You created a todo")) |> assert_has(css(".todo-list > .todo", text: "Write my first Wallaby test")) end end ``` Because Wallaby manages multiple browsers for you, its possible to test several users interacting with a page simultaneously. ```elixir defmodule MyApp.Features.MultipleUsersTest do use ExUnit.Case, async: true use Wallaby.Feature import Wallaby.Query, only: [text_field: 1, button: 1, css: 2] @page message_path(Endpoint, :index) @message_field text_field("Share Message") @share_button button("Share") def message(msg), do: css(".messages > .message", text: msg) @sessions 2 feature "That users can send messages to each other", %{sessions: [user1, user2]} do user1 |> visit(@page) |> fill_in(@message_field, with: "Hello there!") |> click(@share_button) user2 |> visit(@page) |> fill_in(@message_field, with: "Hello yourself") |> click(@share_button) user1 |> assert_has(message("Hello yourself")) user2 |> assert_has(message("Hello there!")) end end ``` Read on to see what else Wallaby can do or check out the [Official Documentation](https://hexdocs.pm/wallaby). ## Sponsors _Your company's name and logo could be here!_ ## Setup ### Requirements Wallaby requires Elixir 1.12+ and OTP 22+. Wallaby also requires `bash` to be installed. Generally `bash` is widely available, but it does not come pre-installed on Alpine Linux. ### Installation Add Wallaby to your list of dependencies in `mix.exs`: ```elixir def deps do [ {:wallaby, "~> 0.30", runtime: false, only: :test} ] end ``` Configure the driver. ```elixir # Chrome config :wallaby, driver: Wallaby.Chrome # default # Selenium config :wallaby, driver: Wallaby.Selenium ``` You'll need to install the actual drivers as well. - Chrome - [`chromedriver`](https://chromedriver.chromium.org/downloads) - Selenium - [`selenium`](https://www.selenium.dev/downloads/) - [`geckodriver`](https://github.com/mozilla/geckodriver) (for Firefox) or [`chromedriver`](https://chromedriver.chromium.org/downloads) (for Chrome) Ensure that Wallaby is started in your `test_helper.exs`: ```elixir {:ok, _} = Application.ensure_all_started(:wallaby) ``` When calling `use Wallaby.Feature` and using Ecto, please configure your `otp_app`. ```elixir config :wallaby, otp_app: :your_app ``` ### Phoenix Enable Phoenix to serve endpoints in tests: ```elixir # config/test.exs config :your_app, YourAppWeb.Endpoint, server: true ``` In your `test_helper.exs` you can provide some configuration to Wallaby. At a minimum, you need to specify a `:base_url`, so Wallaby knows how to resolve relative paths. ```elixir # test/test_helper.exs Application.put_env(:wallaby, :base_url, YourAppWeb.Endpoint.url) ``` #### Ecto If 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`. It's important that this is at the top of `endpoint.ex` before any other plugs. ```elixir # lib/your_app_web/endpoint.ex defmodule YourAppWeb.Endpoint do use Phoenix.Endpoint, otp_app: :your_app if Application.compile_env(:your_app, :sandbox, false) do plug Phoenix.Ecto.SQL.Sandbox end # ... socket("/live", Phoenix.LiveView.Socket, websocket: [connect_info: [:user_agent, session: @session_options]] ) ``` It'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. Then make sure sandbox is enabled: ```elixir # config/test.exs config :your_app, :sandbox, Ecto.Adapters.SQL.Sandbox ``` This 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. If 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: ```elixir # test/support/sandbox.ex defmodule YourApp.Sandbox do def allow(repo, owner_pid, child_pid) do # Delegate to the Ecto sandbox Ecto.Adapters.SQL.Sandbox.allow(repo, owner_pid, child_pid) # Add custom process-sharing configuration Mox.allow(MyMock, owner_pid, child_pid) end end ``` And update the test config to use your custom sandbox: ```elixir # config/test.exs config :your_app, :sandbox, YourApp.Sandbox ``` #### Assets Assets are not re-compiled when you run `mix test`. This can lead to confusion if you've made changes in JavaScript or CSS but tests are still failing. There are two common ways to avoid this confusion. ##### esbuild If you're using [`esbuild`](https://hex.pm/packages/esbuild) you can add `esbuild default` to the `test` alias in the mix config file. ```elixir defp aliases do [ "test": [ "esbuild default", "ecto.create --quiet", "ecto.migrate", "test", ] ] end ``` ##### Webpack The first solution is to run `webpack --mode development --watch` from the assets directory. This will ensure that assets get recompiled after any changes. The second solution is to add a new alias to your mix config that recompiles assets for you: ```elixir def project do [ app: :my_app, version: "1.0.0", aliases: aliases() ] end defp aliases, do: [ "test": [ "assets.compile --quiet", "ecto.create --quiet", "ecto.migrate", "test", ], "assets.compile": &compile_assets/1 ] defp compile_assets(_) do Mix.shell().cmd("cd assets && ./node_modules/.bin/webpack --mode development", quiet: true ) end ``` This method is less error prone but it will cause a delay when starting your test suite. #### LiveView In 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: ```elixir defmodule MyApp.Hooks.AllowEctoSandbox do import Phoenix.LiveView import Phoenix.Component def on_mount(:default, _params, _session, socket) do allow_ecto_sandbox(socket) {:cont, socket} end defp allow_ecto_sandbox(socket) do %{assigns: %{phoenix_ecto_sandbox: metadata}} = assign_new(socket, :phoenix_ecto_sandbox, fn -> if connected?(socket), do: get_connect_info(socket, :user_agent) end) Phoenix.Ecto.SQL.Sandbox.allow(metadata, Application.get_env(:your_app, :sandbox)) end end ``` and then including the function usage in the router: ```elixir live_session :default, on_mount: MyApp.Hooks.AllowEctoSandbox do # ... end ``` #### Umbrella Apps If 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. ```elixir # my_web_app/lib/endpoint.ex defmodule MyWebApp.Endpoint do use Phoenix.Endpoint, otp_app: :my_web_app if Application.compile_env(:my_persistence_app, :sandbox, false) do plug Phoenix.Ecto.SQL.Sandbox end ``` Make sure `MyWebApp` is set up to serve endpoints in tests and that the SQL sandbox is enabled: ```elixir # my_web_app/config/test.exs config :my_web_app, MyWebApp.Endpoint, server: true config :my_persistence_app, :sql_sandbox, true ``` Then in `MyWebApp`'s `test_helper.exs` you can provide some configuration to Wallaby. At minimum, you need to specify a `:base_url`, so Wallaby knows how to resolve relative paths. ```elixir # my_web_app/test/test_helper.exs Application.put_env(:wallaby, :base_url, MyWebApp.Endpoint.url) ``` You will also want to add `phoenix_ecto` as a dependency to `MyWebApp`: ```elixir # my_web_app/mix.exs def deps do [ {:phoenix_ecto, "~> 3.0", only: :test} ] end ``` ### Writing tests It's easiest to add Wallaby to your test suite by using the `Wallaby.Feature` module. ```elixir defmodule YourApp.UserListTest do use ExUnit.Case, async: true use Wallaby.Feature feature "users have names", %{session: session} do session |> visit("/users") |> find(Query.css(".user", count: 3)) |> List.first() |> assert_has(Query.css(".user-name", text: "Chris")) end end ``` ## API The full documentation for the DSL is in the [official documentation](https://hexdocs.pm/wallaby). ### Queries and Actions Wallaby's API is broken into 2 concepts: Queries and Actions. Queries 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. Lets say that our html looks like this: ```html