Skip to content

Commit

Permalink
feat!: add mix mneme.test supporting mneme config
Browse files Browse the repository at this point in the history
Closes #100.

This change requires that users of `mix mneme.watch` add `mneme.test` to
their `:preferred_cli_env`:

    ["mneme.test": :test, "mneme.watch": :test]

This can be done automatically by running `MIX_ENV=test mix
mneme.install`.
  • Loading branch information
zachallaun committed Nov 4, 2024
1 parent a43f8ed commit db6af0f
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 13 deletions.
12 changes: 9 additions & 3 deletions lib/mix/tasks/mneme.install.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule Mix.Tasks.Mneme.Install do
Running this command will automatically patch the following:
* `mix.exs` - Adds `"mneme.watch": :test` to `:preferred_cli_env`
* `mix.exs` - Adds Mneme's tasks to `:preferred_cli_env`
* `.formatter.exs` - Adds `:mneme` to `:import_deps`
* `test/test_helper.exs` - Adds `Mneme.start()` after `ExUnit.start()`
Expand All @@ -16,7 +16,7 @@ defmodule Mix.Tasks.Mneme.Install do
Since your `:mneme` dependency is usually specified with `only: :test`,
this task should be run with `MIX_ENV=test`.
```bash
```shell
$ #{example}
Igniter:
Expand All @@ -37,7 +37,7 @@ defmodule Mix.Tasks.Mneme.Install do
9 9 | start_permanent: Mix.env() == :prod,
10 - | deps: deps()
10 + | deps: deps(),
11 + | preferred_cli_env: ["mneme.watch": :test]
11 + | preferred_cli_env: ["mneme.test": :test, "mneme.watch": :test]
11 12 | ]
12 13 | end
...|
Expand Down Expand Up @@ -98,6 +98,12 @@ defmodule Mix.Tasks.Mneme.Install do
Igniter.update_elixir_file(igniter, "mix.exs", fn zipper ->
# First, try to update a keyword literal in the project
with {:ok, zipper} <- Function.move_to_def(zipper, :project, 0),
{:ok, zipper} <-
Igniter.Code.Keyword.put_in_keyword(
zipper,
[:preferred_cli_env, :"mneme.test"],
:test
),
{:ok, zipper} <-
Igniter.Code.Keyword.put_in_keyword(
zipper,
Expand Down
73 changes: 73 additions & 0 deletions lib/mix/tasks/mneme.test.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule Mix.Tasks.Mneme.Test do
@shortdoc "Run tests with support for Mneme's options at the command line"
@moduledoc """
#{@shortdoc}
This task is like `mix test`, except that it accepts some command line
options specific to Mneme. See ["Command line options"](#module-command-line-options)
below.
## Setup
To ensure `mix mneme.test` runs in the test environment, add a
`:preferred_cli_env` entry in `mix.exs`:
def project do
[
...
preferred_cli_env: [
"mneme.test": :test,
"mneme.watch": :test
],
...
]
end
## Command line options
In addition to the options supported by `mix test`, which runs under
the hood, the following CLI options are available:
* `--action [prompt,accept,reject]`
* `--default-pattern [infer,first,last]`
* `--diff [text,semantic]`
* `--diff-style [side_by_side,stacked]`
* `--force-update`
* `--target [mneme,ex_unit]`
Option documentation is found here: [Mneme – Options](`Mneme#module-options`)
"""

use Mix.Task

@switches [
action: :string,
default_pattern: :string,
diff: :string,
diff_style: :string,
force_update: :boolean,
target: :string,
dry_run: :boolean
]

# Ensure that these switches are collected instead of all but last
# discarded so that they can be passed to `mix test`.
@mix_test_keep [
include: :keep,
exclude: :keep,
only: :keep,
formatter: :keep
]

@impl Mix.Task
def run(argv) do
{opts, argv} = OptionParser.parse!(argv, switches: @switches ++ @mix_test_keep)
{opts, mix_test_opts} = Keyword.split(opts, Keyword.keys(@switches))
mix_test_argv = OptionParser.to_argv(mix_test_opts) ++ argv

Application.put_env(:mneme, :cli_opts, opts)

Mix.Task.reenable("test")
Mix.Task.run("test", mix_test_argv)
end
end
9 changes: 7 additions & 2 deletions lib/mix/tasks/mneme.watch.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Mix.Tasks.Mneme.Watch do
@shortdoc "Run tests when files change"
@shortdoc "Runs the tests for a project when source files change."
@moduledoc """
Runs the tests for a project when source files change.
#{@shortdoc}
This task is similar to [`mix test.watch`](https://hex.pm/packages/mix_test_watch),
but updated to work with Mneme:
Expand All @@ -18,6 +18,7 @@ defmodule Mix.Tasks.Mneme.Watch do
[
...
preferred_cli_env: [
"mneme.test": :test,
"mneme.watch": :test
],
...
Expand All @@ -26,6 +27,10 @@ defmodule Mix.Tasks.Mneme.Watch do
## Command line options
In addition to the options supported by `mix mneme.test` and `mix test`,
which this task runs under the hood, the following CLI options are
available:
* `--exit-on-success` - stops the test watcher the first time the
test suite passes.
Expand Down
12 changes: 11 additions & 1 deletion lib/mneme/options.ex
Original file line number Diff line number Diff line change
Expand Up @@ -211,21 +211,31 @@ defmodule Mneme.Options do
|> collect_attributes(Map.get(attrs, @test_attr, []))
|> collect_attributes(Map.get(attrs, @describe_attr, []))
|> collect_attributes(Map.get(attrs, @module_attr, []))
|> collect_attributes(Application.get_env(:mneme, :cli_opts, []))
|> collect_attributes([persistent_term_get(@config_cache, [])])
end

defp collect_attributes(_), do: %{}

defp collect_attributes(acc, lower_priority) do
for_result =
for attrs <- lower_priority, kv <- List.wrap(attrs), reduce: %{} do
for attrs <- lower_priority,
kv <- List.wrap(attrs),
reduce: %{} do
acc ->
{k, v} =
case kv do
{k, v} -> {k, v}
k when is_atom(k) -> {k, true}
end

v =
if is_binary(v) do
String.to_atom(v)
else
v
end

Map.update(acc, k, [v], &[v | &1])
end

Expand Down
4 changes: 2 additions & 2 deletions lib/mneme/watch/test_runner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ defmodule Mneme.Watch.TestRunner do
def run_tests(cli_args, system_restart_marker) do
Code.unrequire_files(Code.required_files())
recompile()
Mix.Task.reenable(:test)
Mix.Task.run(:test, cli_args)
Mix.Task.reenable("mneme.test")
Mix.Task.run("mneme.test", cli_args)
catch
:exit, _ ->
write_system_restart_marker!(system_restart_marker)
Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ defmodule Mneme.MixProject do
coveralls: :test,
"coveralls.html": :test,
"test.mneme_not_started": :test,
"mneme.test": :test,
"mneme.watch": :test
]
end
Expand Down
52 changes: 47 additions & 5 deletions test/mix/tasks/mneme.install_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ defmodule Mix.Tasks.Mneme.InstallTest do
9 9 | start_permanent: Mix.env() == :prod,
10 - | deps: deps()
10 + | deps: deps(),
11 + | preferred_cli_env: ["mneme.watch": :test]
11 + | preferred_cli_env: ["mneme.test": :test, "mneme.watch": :test]
11 12 | ]
12 13 | end
...|
Expand Down Expand Up @@ -81,9 +81,10 @@ defmodule Mix.Tasks.Mneme.InstallTest do
7 7 | preferred_cli_env: [
8 - | "existing.task": :dev
8 + | "existing.task": :dev,
9 + | "mneme.watch": :test
9 10 | ]
10 11 | ]
9 + | "mneme.test": :test,
10 + | "mneme.watch": :test
9 11 | ]
10 12 | ]
...|
""" <-
Expand All @@ -92,7 +93,7 @@ defmodule Mix.Tasks.Mneme.InstallTest do
|> igniter_diff(only: "mix.exs")
end

test "when :preferred_cli_env already contains mneme.watch" do
test "when :preferred_cli_env already contains mneme tasks" do
test_project =
test_project(
files: %{
Expand All @@ -104,6 +105,7 @@ defmodule Mix.Tasks.Mneme.InstallTest do
[
app: :test,
preferred_cli_env: [
"mneme.test": :test,
"mneme.watch": :test
]
]
Expand All @@ -119,6 +121,46 @@ defmodule Mix.Tasks.Mneme.InstallTest do
|> igniter_diff(only: "mix.exs")
end

test "when :preferred_cli_env already contains only mneme.watch" do
test_project =
test_project(
files: %{
"mix.exs" => """
defmodule Test.MixProject do
use Mix.Project
def project do
[
app: :test,
preferred_cli_env: [
"mneme.watch": :test
]
]
end
end
"""
}
)

auto_assert """
Update: mix.exs
...|
6 6 | app: :test,
7 7 | preferred_cli_env: [
8 - | "mneme.watch": :test
8 + | "mneme.watch": :test,
9 + | "mneme.test": :test
9 10 | ]
10 11 | ]
...|
""" <-
test_project
|> Igniter.compose_task("mneme.install")
|> igniter_diff(only: "mix.exs")
end

test "when :preferred_cli_env is a call to a local function" do
test_project =
test_project(
Expand Down

0 comments on commit db6af0f

Please sign in to comment.