Repository: phoenixframework/phoenix
Pull request #4166 (2021-01-24 05:47:52)

Add phx.gen.auth tests to main test suite
This adds for `phx.gen.auth` to the main test suite. I found that trying to debug using the integration tests is difficult because they are running in a different BEAM instance and tools like `IO.inspect/1` don't output anything to the console (because output is being captured).

## Things to note

1. In order to get these tests to work, I had to add an optional second parameter to `Mix.Tasks.Phx.Gen.Auth.run/1` that allows me to pass in the database adapter and skip the dependency check. With these two options set, I'm able to bypass the need to compile the app that's being tested and test this like the rest of the `phx.gen.*` tasks.
2. Since `phx.gen.auth` injects code into several files in an existing phoenix project, instead of trying to create those needed files in something like `in_tmp_live_project/1`, I found that I could just call `Mix.Tasks.Phx.New.run/1` and have the project generated for me automatically. This required me adding `phx_new` as a path-based, test dependency, but it doesn't seem to cause any issues and makes the test set up much simpler.
3. Now that I can use `phx.new` to generate projects, not every project needs to be named `:phoenix`. I modified `with_generator_env/3` to accept an optional app name.
4. This test currently takes about 30 seconds to run because of the size of some of the eex templates. My next task is to precompile these templates while still allowing templates to be overridden in the`priv` directory. I added a test in this PR that overrides a template in the priv directory and ensures the override works.
5. I added new tests for some of the user prompts and found some could use formatting improvements. I updated one of the prompts to make it look a little nicer.

Please let me know what you think. I hope these tests will make the developer experience working on `phx.gen.auth` much easier.

josevalim: > This adds for phx.gen.auth to the main test suite. I found that trying to debug using the integration tests is difficult because they are running in a different BEAM instance and tools like IO.inspect/1 don't output anything to the console (because output is being captured).

Btw, one option is to have an env var that makes you print the captured result as you capture them (or afterwards), but unit tests are also welcome.
aaronrenner: > Btw, one option is to have an env var that makes you print the captured result as you capture them (or afterwards), but unit tests are also welcome.

I thought about that as I was posting this PR. :sweat_smile: Seems like a useful option I should add in the future.

> Just please double check if there is anything you can remove from the integration tests now that we have this, as we should definitely prefer unit test ones. :)

That's a good idea and something I need to figure out the best way to do. I tried adding `Mix.Tasks.Format.run(~w(--check-formatted))` into one of the phx.gen.auth tests and it errored because I didn't have the dependencies installed.

```
     ** (Mix.Error) Unavailable dependency :ecto_sql given to :import_deps in the formatter configuration. The dependency cannot be found in the file system, please run "mix deps.get" and try again
```

I was able to install the dependencies with `Mix.Tasks.Deps.Get.run([])` and the formatter passed, but I realize since we're out of the integration test project, we don't currently have the ability to control the versions of the dependencies that are being installed. This may require some more thinking to figure out.

Also, I tried adding `Mix.Tasks.Compile.run(~w(--warnings-as-errors))` and got the following error
```
     ** (Mix.Error) Could not compile dependency :ranch, "/home/aaron/.mix/rebar3 bare compile --paths /home/aaron/projects/phoenix/tmp/ttmBOgMq4y/test works with apps generated with --live/my_app/_build/test/lib/*/ebin" command failed. You can recompile this dependency with "mix deps.compile ranch", update it with "mix deps.update ranch" or clean it with "mix deps.clean ranch"
```
I tried to see if I could run `Mix.Tasks.Local.Rebar.run(~w(--force))`, but that didn't fix it.

Another thing I'm weighing is the integration tests can be run concurrently, whereas the unit tests cannot (changing directories, environment config and maybe `Mix.shell(Mix.Shell.Process)`?).  I'm wondering if we figured out a way to move the formatting, compilation and test suite  checks into the unit tests, would it actually provide an overall speed improvement because we're not running them concurrently anymore? :thinking: 

Any suggestions on a better way to check formatting, code compilation and ensure the generated test suite passes would definitely be appreciated! :slightly_smiling_face: 
josevalim: I think those are all hard to check within unit tests because they assume a mix project, that’s why we started the integration suite. Only the formatting should be relatively easy to check, but then I would manually call Code.format_string and compare the results before and after. :)
aaronrenner: > Only the formatting should be relatively easy to check, but then I would manually call Code.format_string and compare the results before and after.

I like that idea! I always have trouble with `mix format --check-formatted` because I wonder what the suggested changes are and can't see them without committing what I have to git, running `mix format` and then a `git diff`. It would be awesome to do some sort of diffing in a test helper like this. 👍 If I was going to do this, it seems like I'd need to extract some of the logic to load the `.formatter.exs` files from the generated phoenix app (so it doesn't try to add parentheses on the routes and migrations) and pass that configuration along into `Code.format_string`. I'll keep this improvement in mind too. 🙂 

Patch:
diff --git a/guides/controllers.md b/guides/controllers.md
index e1d166531..6f27eb06f 100644
--- a/guides/controllers.md
+++ b/guides/controllers.md
@@ -416,7 +416,7 @@ Fortunately, our application layout, `lib/hello_web/templates/layout/app.html.ee
 
 When we reload the [Welcome Page](http://localhost:4000/), our messages should appear just above "Welcome to Phoenix!"
 
-The flash functionality is handy when mixed with redirects. Perhaps you want to redirect to a page with some extra information. If we re-use the redirect action from the previous section, we can do:
+The flash functionality is handy when mixed with redirects. Perhaps you want to redirect to a page with some extra information. If we reuse the redirect action from the previous section, we can do:
 
 ```elixir
   def index(conn, _params) do
diff --git a/guides/request_lifecycle.md b/guides/request_lifecycle.md
index a27ff073d..568f8bb87 100644
--- a/guides/request_lifecycle.md
+++ b/guides/request_lifecycle.md
@@ -188,7 +188,7 @@ As we did last time, the first thing we'll do is create a new route.
 
 ### Another new Route
 
-For this exercise, we're going to re-use the `HelloController` we just created and just add a new `show` action. We'll add a line just below our last route, like this:
+For this exercise, we're going to reuse the `HelloController` we just created and just add a new `show` action. We'll add a line just below our last route, like this:
 
 ```elixir
 scope "/", HelloWeb do
diff --git a/lib/mix/tasks/phx.gen.auth.ex b/lib/mix/tasks/phx.gen.auth.ex
index a079c1672..26c580ad6 100644
--- a/lib/mix/tasks/phx.gen.auth.ex
+++ b/lib/mix/tasks/phx.gen.auth.ex
@@ -85,7 +85,7 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
              table: :string, merge_with_existing_context: :boolean]
 
   @doc false
-  def run(args) do
+  def run(args, test_opts \\ []) do
     if Mix.Project.umbrella?() do
       Mix.raise("mix phx.gen.auth can only be run inside an application directory")
     end
@@ -99,16 +99,21 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
     {context, schema} = Gen.Context.build(context_args, __MODULE__)
     Gen.Context.prompt_for_code_injection(context)
 
-    # Needed so we can get the ecto adapter and ensure other
-    # libraries are loaded.
-    #
-    # As far as I can tell, everything after this must be tested with an
-    # integration test.
-    Mix.Task.run("compile")
+    if Keyword.get(test_opts, :validate_dependencies?, true) do
+      # Needed so we can get the ecto adapter and ensure other
+      # libraries are loaded.
+      Mix.Task.run("compile")
 
-    validate_required_dependencies!()
+      validate_required_dependencies!()
+    end
+
+    ecto_adapter =
+      Keyword.get_lazy(
+        test_opts,
+        :ecto_adapter,
+        fn -> get_ecto_adapter!(schema) end
+      )
 
-    ecto_adapter = get_ecto_adapter!(schema)
     migration = Migration.build(ecto_adapter)
 
     binding = [
@@ -311,7 +316,7 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
       {:error, :unable_to_inject} ->
         Mix.shell().info("""
 
-        Add your #{inspect(mix_dependency)} dependency to #{file_path}:
+        Add your #{mix_dependency} dependency to #{file_path}:
 
             defp deps do
               [
diff --git a/mix.exs b/mix.exs
index c513708e6..08c3f221b 100644
--- a/mix.exs
+++ b/mix.exs
@@ -79,6 +79,7 @@ defmodule Phoenix.MixProject do
       {:telemetry_metrics, "~> 0.4", only: :docs},
 
       # Test dependencies
+      {:phx_new, path: "./installer", only: :test},
       {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
     ]
   end


Test patch:
diff --git a/guides/testing/testing_channels.md b/guides/testing/testing_channels.md
index 7a0a398cb..e996d7874 100644
--- a/guides/testing/testing_channels.md
+++ b/guides/testing/testing_channels.md
@@ -77,7 +77,7 @@ setup do
 end
 ```
 
-The `setup` block sets up a `Phoenix.Socket` based on the `UserSocket` module, which you can find at "lib/hello_web/channels/user_socket.ex". Then it says we want to subscribe and join the `RoomChannel`, accessible as "room:lobby" in the `UserSocket`. At the end of the test, we return the `%{socket: socket}` as metadata, so we can re-use it on every test.
+The `setup` block sets up a `Phoenix.Socket` based on the `UserSocket` module, which you can find at "lib/hello_web/channels/user_socket.ex". Then it says we want to subscribe and join the `RoomChannel`, accessible as "room:lobby" in the `UserSocket`. At the end of the test, we return the `%{socket: socket}` as metadata, so we can reuse it on every test.
 
 In a nutshell, `subscribe_and_join/3` emulates the client joining a channel and subscribes the test process to the given topic. This is a necessary step since clients need to join a channel before they can send and receive events on that channel.
 
diff --git a/installer/test/mix_helper.exs b/installer/test/mix_helper.exs
index fc06436b9..3513896d5 100644
--- a/installer/test/mix_helper.exs
+++ b/installer/test/mix_helper.exs
@@ -98,12 +98,27 @@ defmodule MixHelper do
     end
   end
 
-  def with_generator_env(new_env, fun) do
-    Application.put_env(:phoenix, :generators, new_env)
+  def modify_file(path, function) when is_binary(path) and is_function(function, 1) do
+    path
+    |> File.read!()
+    |> function.()
+    |> write_file!(path)
+  end
+
+  defp write_file!(content, path) do
+    File.write!(path, content)
+  end
+
+  def with_generator_env(app_name \\ :phoenix, new_env, fun) do
+    config_before = Application.fetch_env(app_name, :generators)
+    Application.put_env(app_name, :generators, new_env)
     try do
       fun.()
     after
-      Application.delete_env(:phoenix, :generators)
+      case config_before do
+        {:ok, config} -> Application.put_env(app_name, :generators, config)
+        :error -> Application.delete_env(app_name, :generators)
+      end
     end
   end
 
diff --git a/test/mix/tasks/phx.gen.auth_test.exs b/test/mix/tasks/phx.gen.auth_test.exs
index 8890c8e37..1eac38a1d 100644
--- a/test/mix/tasks/phx.gen.auth_test.exs
+++ b/test/mix/tasks/phx.gen.auth_test.exs
@@ -11,8 +11,26 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
     :ok
   end
 
+  defp in_tmp_phx_project(test, additional_args \\ [], func) do
+    in_tmp(test, fn ->
+      Mix.Tasks.Phx.New.run(~w(my_app --no-install) ++ additional_args)
+      in_project(:my_app, "my_app", fn _module ->
+        func.()
+      end)
+    end)
+  end
+
+  defp in_tmp_phx_umbrella_project(test, func) do
+    in_tmp(test, fn ->
+      Mix.Tasks.Phx.New.run(~w(my_app --umbrella --no-install))
+      File.cd!("my_app_umbrella", fn ->
+        func.()
+      end)
+    end)
+  end
+
   test "invalid mix arguments", config do
-    in_tmp_project(config.test, fn ->
+    in_tmp_phx_project(config.test, fn ->
       assert_raise Mix.Error, ~r/Expected the context, "accounts", to be a valid module name.*phx\.gen\.auth/s, fn ->
         Gen.Auth.run(~w(accounts User users))
       end
@@ -25,12 +43,12 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
         Gen.Auth.run(~w(User User users))
       end
 
-      assert_raise Mix.Error, ~r/Cannot generate context Phoenix because it has the same name as the application/, fn ->
-        Gen.Auth.run(~w(Phoenix User users))
+      assert_raise Mix.Error, ~r/Cannot generate context MyApp because it has the same name as the application/, fn ->
+        Gen.Auth.run(~w(MyApp User users))
       end
 
-      assert_raise Mix.Error, ~r/Cannot generate schema Phoenix because it has the same name as the application/, fn ->
-        Gen.Auth.run(~w(Accounts Phoenix users))
+      assert_raise Mix.Error, ~r/Cannot generate schema MyApp because it has the same name as the application/, fn ->
+        Gen.Auth.run(~w(Accounts MyApp users))
       end
 
       assert_raise Mix.Error, ~r/Invalid arguments/, fn ->
@@ -54,4 +72,804 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
       end
     end)
   end
+
+  test "generates with defaults", config do
+    in_tmp_phx_project(config.test, fn ->
+      Gen.Auth.run(
+        ~w(Accounts User users),
+        [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+      )
+
+      assert_file "config/test.exs", fn file ->
+        assert file =~ "config :bcrypt_elixir, :log_rounds, 1"
+      end
+
+      assert_file "lib/my_app/accounts.ex"
+      assert_file "lib/my_app/accounts/user.ex"
+      assert_file "lib/my_app/accounts/user_token.ex"
+      assert_file "lib/my_app/accounts/user_notifier.ex"
+      assert_file "test/my_app/accounts_test.exs"
+      assert_file "test/support/fixtures/accounts_fixtures.ex"
+      assert_file "lib/my_app_web/controllers/user_auth.ex"
+      assert_file "test/my_app_web/controllers/user_auth_test.exs"
+      assert_file "lib/my_app_web/views/user_confirmation_view.ex"
+      assert_file "lib/my_app_web/templates/user_confirmation/new.html.eex"
+      assert_file "lib/my_app_web/controllers/user_confirmation_controller.ex"
+      assert_file "test/my_app_web/controllers/user_confirmation_controller_test.exs"
+      assert_file "lib/my_app_web/templates/layout/_user_menu.html.eex"
+      assert_file "lib/my_app_web/controllers/user_registration_controller.ex"
+      assert_file "lib/my_app_web/views/user_registration_view.ex"
+      assert_file "test/my_app_web/controllers/user_registration_controller_test.exs"
+      assert_file "lib/my_app_web/controllers/user_reset_password_controller.ex"
+      assert_file "lib/my_app_web/templates/user_reset_password/edit.html.eex"
+      assert_file "lib/my_app_web/templates/user_reset_password/new.html.eex"
+      assert_file "lib/my_app_web/views/user_reset_password_view.ex"
+      assert_file "test/my_app_web/controllers/user_reset_password_controller_test.exs"
+      assert_file "lib/my_app_web/controllers/user_session_controller.ex"
+      assert_file "lib/my_app_web/templates/user_session/new.html.eex"
+      assert_file "test/my_app_web/controllers/user_session_controller_test.exs"
+      assert_file "lib/my_app_web/views/user_session_view.ex"
+      assert_file "lib/my_app_web/controllers/user_settings_controller.ex"
+      assert_file "lib/my_app_web/templates/user_settings/edit.html.eex"
+      assert_file "lib/my_app_web/views/user_settings_view.ex"
+      assert_file "test/my_app_web/controllers/user_settings_controller_test.exs"
+
+      assert [migration] = Path.wildcard("priv/repo/migrations/*_create_users_auth_tables.exs")
+      assert_file migration, fn file ->
+        assert file =~ "create table(:users) do"
+        assert file =~ "create table(:users_tokens) do"
+      end
+
+      assert_file "mix.exs", fn file ->
+        assert file =~ ~s|{:bcrypt_elixir, "~> 2.0"},|
+      end
+
+      assert_file "lib/my_app_web/router.ex", fn file ->
+        assert file =~ "import MyAppWeb.UserAuth"
+        assert file =~ "plug :fetch_current_user"
+
+        assert file =~ """
+          ## Authentication routes
+
+          scope "/", MyAppWeb do
+            pipe_through [:browser, :redirect_if_user_is_authenticated]
+
+            get "/users/register", UserRegistrationController, :new
+            post "/users/register", UserRegistrationController, :create
+            get "/users/log_in", UserSessionController, :new
+            post "/users/log_in", UserSessionController, :create
+            get "/users/reset_password", UserResetPasswordController, :new
+            post "/users/reset_password", UserResetPasswordController, :create
+            get "/users/reset_password/:token", UserResetPasswordController, :edit
+            put "/users/reset_password/:token", UserResetPasswordController, :update
+          end
+
+          scope "/", MyAppWeb do
+            pipe_through [:browser, :require_authenticated_user]
+
+            get "/users/settings", UserSettingsController, :edit
+            put "/users/settings", UserSettingsController, :update
+            get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
+          end
+
+          scope "/", MyAppWeb do
+            pipe_through [:browser]
+
+            delete "/users/log_out", UserSessionController, :delete
+            get "/users/confirm", UserConfirmationController, :new
+            post "/users/confirm", UserConfirmationController, :create
+            get "/users/confirm/:token", UserConfirmationController, :confirm
+          end
+        """
+      end
+
+      assert_file "lib/my_app_web/templates/layout/app.html.eex", fn file ->
+        assert file =~ ~s|<%= render "_user_menu.html", assigns %>|
+      end
+
+      assert_file "test/support/conn_case.ex", fn file ->
+        assert file =~ "def register_and_log_in_user(%{conn: conn})"
+        assert file =~ "def log_in_user(conn, user)"
+      end
+
+      assert_received {:mix_shell, :info, ["""
+
+      Please re-fetch your dependencies with the following command:
+
+          mix deps.get
+      """]}
+
+      assert_received {:mix_shell, :info, ["""
+
+      Remember to update your repository by running migrations:
+
+        $ mix ecto.migrate
+      """]}
+    end)
+  end
+
+  test "works with apps generated with --live", config do
+    in_tmp_phx_project(config.test, ~w(--live), fn ->
+      Gen.Auth.run(
+        ~w(Accounts User users),
+        [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+      )
+
+      assert_file "lib/my_app_web/templates/layout/root.html.leex", fn file ->
+        assert file =~ ~s|<%= render "_user_menu.html", assigns %>|
+      end
+
+      assert_file "lib/my_app_web/templates/layout/app.html.eex", fn file ->
+        refute file =~ ~s|<%= render "_user_menu.html", assigns %>|
+      end
+    end)
+  end
+
+  test "generates with --web option", config do
+    in_tmp_phx_project(config.test, fn ->
+      Gen.Auth.run(
+        ~w(Accounts User users --web warehouse),
+        [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+      )
+
+      assert_file "lib/my_app/accounts.ex"
+      assert_file "lib/my_app/accounts/user.ex"
+      assert_file "lib/my_app/accounts/user_token.ex"
+      assert_file "lib/my_app/accounts/user_notifier.ex"
+      assert_file "test/my_app/accounts_test.exs"
+      assert_file "test/support/fixtures/accounts_fixtures.ex"
+
+      assert_file "lib/my_app_web/controllers/warehouse/user_auth.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserAuth do"
+      end
+
+      assert_file "test/my_app_web/controllers/warehouse/user_auth_test.exs", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserAuthTest do"
+      end
+
+      assert_file "lib/my_app_web/views/warehouse/user_confirmation_view.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserConfirmationView do"
+      end
+
+      assert_file "lib/my_app_web/templates/warehouse/user_confirmation/new.html.eex", fn file ->
+        assert file =~ ~S|<%= form_for :user, Routes.warehouse_user_confirmation_path(@conn, :create), fn f -> %>|
+        assert file =~ ~S|<%= link "Register", to: Routes.warehouse_user_registration_path(@conn, :new) %>|
+        assert file =~ ~S|<%= link "Log in", to: Routes.warehouse_user_session_path(@conn, :new) %>|
+      end
+
+      assert_file "lib/my_app_web/controllers/warehouse/user_confirmation_controller.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserConfirmationController do"
+      end
+
+      assert_file "test/my_app_web/controllers/warehouse/user_confirmation_controller_test.exs", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserConfirmationControllerTest do"
+      end
+
+      assert_file "lib/my_app_web/templates/layout/_user_menu.html.eex", fn file ->
+        assert file =~ ~S|<%= link "Settings", to: Routes.warehouse_user_settings_path(@conn, :edit) %>|
+        assert file =~ ~S|<%= link "Log out", to: Routes.warehouse_user_session_path(@conn, :delete), method: :delete %>|
+        assert file =~ ~S|<%= link "Register", to: Routes.warehouse_user_registration_path(@conn, :new) %>|
+        assert file =~ ~S|<%= link "Log in", to: Routes.warehouse_user_session_path(@conn, :new) %>|
+      end
+
+      assert_file "lib/my_app_web/controllers/warehouse/user_registration_controller.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserRegistrationController do"
+      end
+
+      assert_file "lib/my_app_web/views/warehouse/user_registration_view.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserRegistrationView do"
+      end
+
+      assert_file "test/my_app_web/controllers/warehouse/user_registration_controller_test.exs", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserRegistrationControllerTest do"
+      end
+
+      assert_file "lib/my_app_web/controllers/warehouse/user_reset_password_controller.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserResetPasswordController do"
+      end
+
+      assert_file "lib/my_app_web/templates/warehouse/user_reset_password/edit.html.eex", fn file ->
+        assert file =~ ~S|<%= form_for @changeset, Routes.warehouse_user_reset_password_path(@conn, :update, @token), fn f -> %>|
+        assert file =~ ~S|<%= link "Register", to: Routes.warehouse_user_registration_path(@conn, :new) %>|
+        assert file =~ ~S|<%= link "Log in", to: Routes.warehouse_user_session_path(@conn, :new) %>|
+      end
+
+      assert_file "lib/my_app_web/templates/warehouse/user_reset_password/new.html.eex", fn file ->
+        assert file =~ ~S|<%= form_for :user, Routes.warehouse_user_reset_password_path(@conn, :create), fn f -> %>|
+        assert file =~ ~S|<%= link "Register", to: Routes.warehouse_user_registration_path(@conn, :new) %>|
+        assert file =~ ~S|<%= link "Log in", to: Routes.warehouse_user_session_path(@conn, :new) %>|
+      end
+
+      assert_file "lib/my_app_web/views/warehouse/user_reset_password_view.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserResetPasswordView do"
+      end
+
+      assert_file "test/my_app_web/controllers/warehouse/user_reset_password_controller_test.exs", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserResetPasswordControllerTest do"
+      end
+
+      assert_file "lib/my_app_web/controllers/warehouse/user_session_controller.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserSessionController do"
+      end
+
+      assert_file "lib/my_app_web/templates/warehouse/user_session/new.html.eex", fn file ->
+        assert file =~ ~S|<%= form_for @conn, Routes.warehouse_user_session_path(@conn, :create), [as: :user], fn f -> %>|
+        assert file =~ ~S|<%= link "Register", to: Routes.warehouse_user_registration_path(@conn, :new) %>|
+        assert file =~ ~S|<%= link "Forgot your password?", to: Routes.warehouse_user_reset_password_path(@conn, :new) %>|
+      end
+
+      assert_file "test/my_app_web/controllers/warehouse/user_session_controller_test.exs", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserSessionControllerTest do"
+      end
+
+      assert_file "lib/my_app_web/views/warehouse/user_session_view.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserSessionView do"
+      end
+
+      assert_file "lib/my_app_web/controllers/warehouse/user_settings_controller.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserSettingsController do"
+      end
+
+      assert_file "lib/my_app_web/templates/warehouse/user_settings/edit.html.eex", fn file ->
+        assert file =~ ~S|<%= form_for @email_changeset, Routes.warehouse_user_settings_path(@conn, :update), fn f -> %>|
+        assert file =~ ~S|<%= form_for @password_changeset, Routes.warehouse_user_settings_path(@conn, :update), fn f -> %>|
+      end
+
+      assert_file "lib/my_app_web/views/warehouse/user_settings_view.ex", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserSettingsView do"
+      end
+
+      assert_file "test/my_app_web/controllers/warehouse/user_settings_controller_test.exs", fn file ->
+        assert file =~ "defmodule MyAppWeb.Warehouse.UserSettingsControllerTest do"
+      end
+
+      assert [migration] = Path.wildcard("priv/repo/migrations/*_create_users_auth_tables.exs")
+      assert_file migration, fn file ->
+        assert file =~ "create table(:users) do"
+        assert file =~ "create table(:users_tokens) do"
+      end
+
+      assert_file "lib/my_app_web/router.ex", fn file ->
+        assert file =~ "import MyAppWeb.Warehouse.UserAuth"
+        assert file =~ "plug :fetch_current_user"
+
+        assert file =~ """
+          ## Authentication routes
+
+          scope "/warehouse", MyAppWeb.Warehouse, as: :warehouse do
+            pipe_through [:browser, :redirect_if_user_is_authenticated]
+
+            get "/users/register", UserRegistrationController, :new
+            post "/users/register", UserRegistrationController, :create
+            get "/users/log_in", UserSessionController, :new
+            post "/users/log_in", UserSessionController, :create
+            get "/users/reset_password", UserResetPasswordController, :new
+            post "/users/reset_password", UserResetPasswordController, :create
+            get "/users/reset_password/:token", UserResetPasswordController, :edit
+            put "/users/reset_password/:token", UserResetPasswordController, :update
+          end
+
+          scope "/warehouse", MyAppWeb.Warehouse, as: :warehouse do
+            pipe_through [:browser, :require_authenticated_user]
+
+            get "/users/settings", UserSettingsController, :edit
+            put "/users/settings", UserSettingsController, :update
+            get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
+          end
+
+          scope "/warehouse", MyAppWeb.Warehouse, as: :warehouse do
+            pipe_through [:browser]
+
+            delete "/users/log_out", UserSessionController, :delete
+            get "/users/confirm", UserConfirmationController, :new
+            post "/users/confirm", UserConfirmationController, :create
+            get "/users/confirm/:token", UserConfirmationController, :confirm
+          end
+        """
+      end
+
+      assert_file "lib/my_app_web/templates/layout/app.html.eex", fn file ->
+        assert file =~ ~s|<%= render "_user_menu.html", assigns %>|
+      end
+
+      assert_file "test/support/conn_case.ex", fn file ->
+        assert file =~ "def register_and_log_in_user(%{conn: conn})"
+        assert file =~ "def log_in_user(conn, user)"
+      end
+    end)
+  end
+
+  describe "--database option" do
+    test "when the database is postgres", config do
+      in_tmp_phx_project(config.test, fn ->
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert [migration] = Path.wildcard("priv/repo/migrations/*_create_users_auth_tables.exs")
+        assert_file migration, fn file ->
+          assert file =~ ~r/execute "CREATE EXTENSION IF NOT EXISTS citext", ""$/m
+          assert file =~ ~r/add :email, :citext, null: false$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_auth_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase, async: true$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_confirmation_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase, async: true$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_registration_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase, async: true$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_reset_password_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase, async: true$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_session_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase, async: true$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_settings_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase, async: true$/m
+        end
+      end)
+    end
+
+    test "when the database is mysql", config do
+      in_tmp_phx_project(config.test, fn ->
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.MyXQL, validate_dependencies?: false]
+        )
+
+        assert [migration] = Path.wildcard("priv/repo/migrations/*_create_users_auth_tables.exs")
+        assert_file migration, fn file ->
+          refute file =~ ~r/execute "CREATE EXTENSION IF NOT EXISTS citext", ""$/m
+          assert file =~ ~r/add :email, :string, null: false, size: 160$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_auth_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_confirmation_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_registration_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_reset_password_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_session_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_settings_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+      end)
+    end
+
+    test "when the database is mssql", config do
+      in_tmp_phx_project(config.test, fn ->
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.TDS, validate_dependencies?: false]
+        )
+
+        assert [migration] = Path.wildcard("priv/repo/migrations/*_create_users_auth_tables.exs")
+        assert_file migration, fn file ->
+          refute file =~ ~r/execute "CREATE EXTENSION IF NOT EXISTS citext", ""$/m
+          assert file =~ ~r/add :email, :string, null: false, size: 160$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_auth_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_confirmation_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_registration_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_reset_password_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_session_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+
+        assert_file "test/my_app_web/controllers/user_settings_controller_test.exs", fn file ->
+          assert file =~ ~r/use MyAppWeb\.ConnCase$/m
+        end
+      end)
+    end
+  end
+
+  test "supports --binary_id option", config do
+    in_tmp_phx_project(config.test, fn ->
+      Gen.Auth.run(
+        ~w(Accounts User users --binary-id),
+        [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+      )
+
+      assert_file "lib/my_app/accounts/user.ex", fn file ->
+        assert file =~ "@primary_key {:id, :binary_id, autogenerate: true}"
+        assert file =~ "@foreign_key_type :binary_id"
+      end
+
+      assert_file "lib/my_app/accounts/user_token.ex", fn file ->
+        assert file =~ "@primary_key {:id, :binary_id, autogenerate: true}"
+        assert file =~ "@foreign_key_type :binary_id"
+      end
+
+      assert [migration] = Path.wildcard("priv/repo/migrations/*_create_users_auth_tables.exs")
+      assert_file migration, fn file ->
+        assert file =~ "create table(:users, primary_key: false)"
+        assert file =~ "create table(:users_tokens, primary_key: false)"
+        assert file =~ "add :id, :binary_id, primary_key: true"
+      end
+    end)
+  end
+
+  describe "--hashing-lib option" do
+    test "when bcrypt", config do
+      in_tmp_phx_project(config.test, fn ->
+        Gen.Auth.run(
+          ~w(Accounts User users --hashing-lib bcrypt),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_file "mix.exs", fn file ->
+          assert file =~ ~s|{:bcrypt_elixir, "~> 2.0"}|
+        end
+
+        assert_file "config/test.exs", fn file ->
+          assert file =~ "config :bcrypt_elixir, :log_rounds, 1"
+        end
+
+        assert_file "lib/my_app/accounts/user.ex", fn file ->
+          assert file =~ "Bcrypt.verify_pass(password, hashed_password)"
+        end
+      end)
+    end
+
+    test "when pbkdf2", config do
+      in_tmp_phx_project(config.test, fn ->
+        Gen.Auth.run(
+          ~w(Accounts User users --hashing-lib pbkdf2),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_file "mix.exs", fn file ->
+          assert file =~ ~s|{:pbkdf2_elixir, "~> 1.0"}|
+        end
+
+        assert_file "config/test.exs", fn file ->
+          assert file =~ "config :pbkdf2_elixir, :rounds, 1"
+        end
+
+        assert_file "lib/my_app/accounts/user.ex", fn file ->
+          assert file =~ "Pbkdf2.verify_pass(password, hashed_password)"
+        end
+      end)
+    end
+
+    test "when argon2", config do
+      in_tmp_phx_project(config.test, fn ->
+        Gen.Auth.run(
+          ~w(Accounts User users --hashing-lib argon2),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_file "mix.exs", fn file ->
+          assert file =~ ~s|{:argon2_elixir, "~> 2.0"}|
+        end
+
+        assert_file "config/test.exs", fn file ->
+          assert file =~ """
+          config :argon2_elixir,
+            t_cost: 1,
+            m_cost: 8
+          """
+        end
+
+        assert_file "lib/my_app/accounts/user.ex", fn file ->
+          assert file =~ "Argon2.verify_pass(password, hashed_password)"
+        end
+      end)
+    end
+  end
+
+  test "with --table option", config do
+    in_tmp_phx_project(config.test, fn ->
+      Gen.Auth.run(
+        ~w(Accounts User users --table my_users),
+        [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+      )
+
+      assert_file "lib/my_app/accounts/user.ex", fn file ->
+        assert file =~ ~S|schema "my_users" do|
+      end
+
+      assert_file "lib/my_app/accounts/user_token.ex", fn file ->
+        assert file =~ ~S|schema "my_users_tokens" do|
+      end
+
+      assert [migration] = Path.wildcard("priv/repo/migrations/*_create_my_users_auth_tables.exs")
+      assert_file migration, fn file ->
+        assert file =~ "create table(:my_users) do"
+        assert file =~ "create table(:my_users_tokens) do"
+      end
+    end)
+  end
+
+  describe "inside umbrella" do
+    test "without context_app generators config uses web dir", config do
+      in_tmp_phx_umbrella_project(config.test, fn ->
+        in_project(:my_app, "apps/my_app", fn _module ->
+          with_generator_env(:my_app_web, [context_app: nil], fn ->
+            Gen.Auth.run(
+              ~w(Accounts User users),
+              [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+            )
+          end)
+        end)
+
+        assert_file "apps/my_app/lib/my_app/accounts.ex"
+        assert_file "apps/my_app/lib/my_app/accounts/user.ex"
+        assert_file "apps/my_app/lib/my_app/accounts/user_token.ex"
+        assert_file "apps/my_app/lib/my_app/accounts/user_notifier.ex"
+        assert_file "apps/my_app/test/my_app/accounts_test.exs"
+        assert_file "apps/my_app/test/support/fixtures/accounts_fixtures.ex"
+        assert_file "apps/my_app/lib/my_app_web/controllers/user_auth.ex"
+        assert_file "apps/my_app/test/my_app_web/controllers/user_auth_test.exs"
+        assert_file "apps/my_app/lib/my_app_web/views/user_confirmation_view.ex"
+        assert_file "apps/my_app/lib/my_app_web/templates/user_confirmation/new.html.eex"
+        assert_file "apps/my_app/lib/my_app_web/controllers/user_confirmation_controller.ex"
+        assert_file "apps/my_app/test/my_app_web/controllers/user_confirmation_controller_test.exs"
+        assert_file "apps/my_app/lib/my_app_web/templates/layout/_user_menu.html.eex"
+        assert_file "apps/my_app/lib/my_app_web/controllers/user_registration_controller.ex"
+        assert_file "apps/my_app/lib/my_app_web/views/user_registration_view.ex"
+        assert_file "apps/my_app/test/my_app_web/controllers/user_registration_controller_test.exs"
+        assert_file "apps/my_app/lib/my_app_web/controllers/user_reset_password_controller.ex"
+        assert_file "apps/my_app/lib/my_app_web/templates/user_reset_password/edit.html.eex"
+        assert_file "apps/my_app/lib/my_app_web/templates/user_reset_password/new.html.eex"
+        assert_file "apps/my_app/lib/my_app_web/views/user_reset_password_view.ex"
+        assert_file "apps/my_app/test/my_app_web/controllers/user_reset_password_controller_test.exs"
+        assert_file "apps/my_app/lib/my_app_web/controllers/user_session_controller.ex"
+        assert_file "apps/my_app/lib/my_app_web/templates/user_session/new.html.eex"
+        assert_file "apps/my_app/test/my_app_web/controllers/user_session_controller_test.exs"
+        assert_file "apps/my_app/lib/my_app_web/views/user_session_view.ex"
+        assert_file "apps/my_app/lib/my_app_web/controllers/user_settings_controller.ex"
+        assert_file "apps/my_app/lib/my_app_web/templates/user_settings/edit.html.eex"
+        assert_file "apps/my_app/lib/my_app_web/views/user_settings_view.ex"
+        assert_file "apps/my_app/test/my_app_web/controllers/user_settings_controller_test.exs"
+      end)
+    end
+
+    test "with context_app generators config does not use web dir", config do
+      in_tmp_phx_umbrella_project(config.test, fn ->
+        in_project(:my_app_web, "apps/my_app_web", fn _module ->
+          with_generator_env(:my_app_web, [context_app: :my_app], fn ->
+            Gen.Auth.run(
+              ~w(Accounts User users),
+              [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+            )
+          end)
+        end)
+
+        assert_file "apps/my_app/lib/my_app/accounts.ex"
+        assert_file "apps/my_app/lib/my_app/accounts/user.ex"
+        assert_file "apps/my_app/lib/my_app/accounts/user_token.ex"
+        assert_file "apps/my_app/lib/my_app/accounts/user_notifier.ex"
+        assert_file "apps/my_app/test/my_app/accounts_test.exs"
+        assert_file "apps/my_app/test/support/fixtures/accounts_fixtures.ex"
+        assert_file "apps/my_app_web/lib/my_app_web/controllers/user_auth.ex"
+        assert_file "apps/my_app_web/test/my_app_web/controllers/user_auth_test.exs"
+        assert_file "apps/my_app_web/lib/my_app_web/views/user_confirmation_view.ex"
+        assert_file "apps/my_app_web/lib/my_app_web/templates/user_confirmation/new.html.eex"
+        assert_file "apps/my_app_web/lib/my_app_web/controllers/user_confirmation_controller.ex"
+        assert_file "apps/my_app_web/test/my_app_web/controllers/user_confirmation_controller_test.exs"
+        assert_file "apps/my_app_web/lib/my_app_web/templates/layout/_user_menu.html.eex"
+        assert_file "apps/my_app_web/lib/my_app_web/controllers/user_registration_controller.ex"
+        assert_file "apps/my_app_web/lib/my_app_web/views/user_registration_view.ex"
+        assert_file "apps/my_app_web/test/my_app_web/controllers/user_registration_controller_test.exs"
+        assert_file "apps/my_app_web/lib/my_app_web/controllers/user_reset_password_controller.ex"
+        assert_file "apps/my_app_web/lib/my_app_web/templates/user_reset_password/edit.html.eex"
+        assert_file "apps/my_app_web/lib/my_app_web/templates/user_reset_password/new.html.eex"
+        assert_file "apps/my_app_web/lib/my_app_web/views/user_reset_password_view.ex"
+        assert_file "apps/my_app_web/test/my_app_web/controllers/user_reset_password_controller_test.exs"
+        assert_file "apps/my_app_web/lib/my_app_web/controllers/user_session_controller.ex"
+        assert_file "apps/my_app_web/lib/my_app_web/templates/user_session/new.html.eex"
+        assert_file "apps/my_app_web/test/my_app_web/controllers/user_session_controller_test.exs"
+        assert_file "apps/my_app_web/lib/my_app_web/views/user_session_view.ex"
+        assert_file "apps/my_app_web/lib/my_app_web/controllers/user_settings_controller.ex"
+        assert_file "apps/my_app_web/lib/my_app_web/templates/user_settings/edit.html.eex"
+        assert_file "apps/my_app_web/lib/my_app_web/views/user_settings_view.ex"
+        assert_file "apps/my_app_web/test/my_app_web/controllers/user_settings_controller_test.exs"
+      end)
+    end
+
+    test "raises with false context_app", config do
+      in_tmp_phx_umbrella_project config.test, fn ->
+        in_project(:my_app_web, "apps/my_app_web", fn _module ->
+          with_generator_env(:my_app_web, [context_app: :false], fn ->
+            assert_raise Mix.Error, ~r/no context_app configured/, fn ->
+              Gen.Auth.run(
+                ~w(Accounts User users),
+                [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+              )
+              end
+          end)
+        end)
+      end
+    end
+  end
+
+  describe "user prompts" do
+    test "when unable to inject dependencies in mix.exs", config do
+      in_tmp_phx_project(config.test, fn ->
+        File.write!("mix.exs", "")
+
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_received {:mix_shell, :info, ["""
+
+        Add your {:bcrypt_elixir, "~> 2.0"} dependency to mix.exs:
+
+            defp deps do
+              [
+                {:bcrypt_elixir, "~> 2.0"},
+                ...
+              ]
+            end
+        """]}
+      end)
+    end
+
+    test "when unable to inject authentication import into router.ex", config do
+      in_tmp_phx_project(config.test, fn ->
+        modify_file("lib/my_app_web/router.ex", fn file ->
+          String.replace(file, "use MyAppWeb, :router", "")
+        end)
+
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_received {:mix_shell, :info, ["""
+
+        Add your MyAppWeb.UserAuth import to lib/my_app_web/router.ex:
+
+            defmodule MyAppWeb.Router do
+              use MyAppWeb, :router
+
+              # Import authentication plugs
+              import MyAppWeb.UserAuth
+
+              ...
+            end
+
+        """]}
+      end)
+    end
+
+    test "when unable to inject plugs into router.ex", config do
+      in_tmp_phx_project(config.test, fn ->
+        modify_file("lib/my_app_web/router.ex", fn file ->
+          String.replace(file, "plug :put_secure_browser_headers\n", "")
+        end)
+
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_received {:mix_shell, :info, ["""
+
+        Add the :fetch_current_user plug to the :browser pipeline in lib/my_app_web/router.ex:
+
+            pipeline :browser do
+              ...
+              plug :put_secure_browser_headers
+              plug :fetch_current_user
+            end
+
+        """]}
+      end)
+    end
+
+    test "when layout file is not found", config do
+      in_tmp_phx_project(config.test, fn ->
+        File.rm!("lib/my_app_web/templates/layout/app.html.eex")
+
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_received {:mix_shell, :error, ["""
+
+        Unable to find an application layout file to inject a render
+        call for "_user_menu.html".
+
+        Missing files:
+
+          * lib/my_app_web/templates/layout/root.html.leex
+          * lib/my_app_web/templates/layout/app.html.eex
+
+        Please ensure this phoenix app was not generated with
+        --no-html. If you have changed the name of your application
+        layout file, please add the following code to it where you'd
+        like "_user_menu.html" to be rendered.
+
+            <%= render "_user_menu.html", assigns %>
+        """]}
+      end)
+    end
+
+    test "when user menu can't be injected into layout", config do
+      in_tmp_phx_project(config.test, fn ->
+        modify_file("lib/my_app_web/templates/layout/app.html.eex", fn _file ->
+          ""
+        end)
+
+        Gen.Auth.run(
+          ~w(Accounts User users),
+          [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+        )
+
+        assert_received {:mix_shell, :info, ["""
+
+        Add a render call for "_user_menu.html" to lib/my_app_web/templates/layout/app.html.eex:
+
+          <nav role="navigation">
+            <%= render "_user_menu.html", assigns %>
+          </nav>
+
+        """]}
+      end)
+    end
+  end
+
+  test "allows templates to be overridden", config do
+    in_tmp_phx_project(config.test, fn ->
+      File.mkdir_p!("priv/templates/phx.gen.auth")
+      File.write!("priv/templates/phx.gen.auth/_menu.html.eex", """
+      <ul>
+        <%%= if @current_<%= schema.singular %> do %>
+          You're logged in
+        <%% end %>
+      </ul>
+      """)
+
+      Gen.Auth.run(
+        ~w(Accounts Admin admins),
+        [ecto_adapter: Ecto.Adapters.Postgres, validate_dependencies?: false]
+      )
+
+      assert_file "lib/my_app_web/templates/layout/_admin_menu.html.eex", fn file ->
+        assert file =~ ~S|<%= if @current_admin do %>|
+            assert file =~ ~S|You're logged in|
+          end
+      end)
+  end
+
 end