Packages
wallaby
0.28.1
0.31.0
0.30.12
0.30.11
0.30.10
0.30.9
0.30.8
0.30.7
0.30.6
0.30.5
0.30.4
0.30.3
0.30.2
0.30.1
0.30.0
0.29.1
0.29.0
0.28.1
0.28.0
0.27.0
0.26.2
0.26.1
0.26.0
0.25.1
0.25.0
0.24.1
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.0
0.16.1
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.1
0.3.0
0.2.0
0.1.0
0.0.1
Concurrent feature tests for elixir
Current section
Files
Jump to
Current section
Files
lib/wallaby/feature.ex
defmodule Wallaby.Feature do
@moduledoc """
Helpers for writing features.
You can `use` or `import` this module.
## use Wallaby.Feature
Calling this module with `use` will automatically call `use Wallaby.DSL`.
When called with `use` and you are using Ecto, please configure your `otp_app`.
```
config :wallaby, otp_app: :your_app
```
"""
defmacro __using__(_) do
quote do
ExUnit.Case.register_attribute(__MODULE__, :sessions)
use Wallaby.DSL
import Wallaby.Feature
setup context do
metadata = unquote(__MODULE__).Utils.maybe_checkout_repos(context[:async])
start_session_opts =
[metadata: metadata]
|> unquote(__MODULE__).Utils.put_create_session_fn(context[:create_session_fn])
get_in(context, [:registered, :sessions])
|> unquote(__MODULE__).Utils.sessions_iterable()
|> Enum.map(fn
opts when is_list(opts) ->
unquote(__MODULE__).Utils.start_session(opts, start_session_opts)
i when is_number(i) ->
unquote(__MODULE__).Utils.start_session([], start_session_opts)
end)
|> unquote(__MODULE__).Utils.build_setup_return()
end
end
end
@doc """
Defines a feature with a message.
Adding `import Wallaby.Feature` to your test module will import the `Wallaby.Feature.feature/3` macro. This is a drop in replacement for the `ExUnit.Case.test/3` macro that you normally use.
Adding `use Wallaby.Feature` to your test module will act the same as `import Wallaby.Feature`, as well as configure your Ecto repos properly and pass a `Wallaby.Session` into the test context.
## Sessions
When called with `use`, the `Wallaby.Feature.feature/3` macro will automatically start a single session using the currently configured capabilities and is passed to the feature via the `:session` key in the context.
```
feature "test with a single session", %{session: session} do
# ...
end
```
If you would like to start multiple sessions, assign the `@sessions` attribute to the number of sessions that the feature should start, and they will be pass to the feature via the `:sessions` key in the context.
```
@sessions 2
feature "test with a two sessions", %{sessions: [session_1, sessions_2]} do
# ...
end
```
If you need to change the capabilities sent to the session for a specific feature, you can assign `@sessions` to a list of keyword lists of the options to be passed to `Wallaby.start_session/1`. This will start the number of sessions equal to the size of the list.
```
@sessions [
[capabilities: %{}]
]
feature "test with different capabilities", %{session: session} do
# ...
end
```
If you don't wish to `use Wallaby.Feature` in your test module, you can add the following code to configure Ecto and create a session.
```
setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(YourApp.Repo)
unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(YourApp.Repo, {:shared, self()})
end
metadata = Phoenix.Ecto.SQL.Sandbox.metadata_for(YourApp.Repo, self())
{:ok, session} = Wallaby.start_session(metadata: metadata)
{:ok, session: session}
end
```
## Screenshots
If you have configured `screenshot_on_failure` to be true, any exceptions raised during the feature will trigger a screenshot to be taken.
"""
defmacro feature(message, context \\ quote(do: _), contents) do
contents =
quote do
try do
unquote(contents)
:ok
rescue
e ->
if Wallaby.screenshot_on_failure?() do
unquote(__MODULE__).Utils.take_screenshots_for_sessions(self(), unquote(message))
end
reraise(e, __STACKTRACE__)
end
end
context = Macro.escape(context)
contents = Macro.escape(contents, unquote: true)
quote bind_quoted: [context: context, contents: contents, message: message] do
name = ExUnit.Case.register_test(__ENV__, :feature, message, [:feature])
def unquote(name)(unquote(context)), do: unquote(contents)
end
end
defmodule Utils do
@includes_ecto Code.ensure_loaded?(Ecto.Adapters.SQL.Sandbox) &&
Code.ensure_loaded?(Phoenix.Ecto.SQL.Sandbox)
@moduledoc false
def build_setup_return([session]) do
[session: session]
end
def build_setup_return(sessions) do
[sessions: sessions]
end
def sessions_iterable(nil), do: 1..1
def sessions_iterable(count) when is_number(count), do: 1..count
def sessions_iterable(capabilities) when is_list(capabilities), do: capabilities
def start_session(more_opts, start_session_opts) when is_list(more_opts) do
{:ok, session} =
start_session_opts
|> Keyword.merge(more_opts)
|> Wallaby.start_session()
session
end
def put_create_session_fn(opts, nil), do: opts
def put_create_session_fn(opts, func), do: Keyword.put(opts, :create_session_fn, func)
if @includes_ecto do
def maybe_checkout_repos(async?) do
otp_app()
|> ecto_repos()
|> Enum.map(&checkout_ecto_repos(&1, async?))
|> metadata_for_ecto_repos()
end
defp otp_app(), do: Application.get_env(:wallaby, :otp_app)
defp ecto_repos(nil), do: []
defp ecto_repos(otp_app), do: Application.get_env(otp_app, :ecto_repos, [])
defp checkout_ecto_repos(repo, async) do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(repo)
unless async, do: Ecto.Adapters.SQL.Sandbox.mode(repo, {:shared, self()})
repo
end
defp metadata_for_ecto_repos([]), do: Map.new()
defp metadata_for_ecto_repos(repos) do
Phoenix.Ecto.SQL.Sandbox.metadata_for(repos, self())
end
else
def maybe_checkout_repos(_) do
""
end
end
def take_screenshots_for_sessions(pid, test_name) do
time = :erlang.system_time(:second) |> to_string()
test_name = String.replace(test_name, " ", "_")
screenshot_paths =
Wallaby.SessionStore.list_sessions_for(owner_pid: pid)
|> Enum.with_index(1)
|> Enum.flat_map(fn {s, i} ->
filename = time <> "_" <> test_name <> "(#{i})"
Wallaby.Browser.take_screenshot(s, name: filename).screenshots
end)
|> Enum.map_join("\n- ", &Wallaby.Browser.build_file_url/1)
IO.write("\n- #{screenshot_paths}")
end
end
end