I’m looking through the files generated by mix phx.gen.auth and see the entire browser interface for authentication uses live views except for the session management which can’t be done with web sockets alone. The current authentication system for Minotaur uses controllers and “dead” views for all pages. I will copy the generated live views to my application to replace the existing authentication interface.

I start with the registration live view by copying the first test case from the generated user_registration_live_test.exs.

defmodule MinotaurWeb.UserRegistrationLiveTest do
  @moduledoc false

  use MinotaurWeb.ConnCase, async: true

  import Phoenix.LiveViewTest
  import Minotaur.AccountsFixtures

  describe "Registration page" do
    test "renders registration page", %{conn: conn} do
      {:ok, _lv, html} = live(conn, ~p"/users/register")

      assert html =~ "Register"
      assert html =~ "Log in"
    end
  end
end

I have not configured a route for /users/register so the test fails with:

** (FunctionClauseError) no function clause matching in Phoenix.LiveViewTest.connect_from_static_token/2

I add the minimal code needed to get past this error by updating the Router module with a route to a non-existent UserRegistrationLive module.

defmodule MinotaurWeb.Router do
  use MinotaurWeb, :router

  # …

  scope "/", MinotaurWeb do
    pipe_through [:browser]

    live "/users/register", UserRegistrationLive, :new
  end
end

The test now complains about the undefined module as expected.

** (UndefinedFunctionError) function MinotaurWeb.UserRegistrationLive.__live__/0 is undefined (module MinotaurWeb.UserRegistrationLive is not available)

I create the missing module and define a placeholder render function to check that requests are being routed correctly through the application.

defmodule MinotaurWeb.UserRegistrationLive do
  use MinotaurWeb, :live_view

  def render(assigns) do
    ~H"""
    OK
    """
  end
end

Now the test is compiling and failing at the first assert statement since it does not find “Register” anywhere in rendered string. I replace the render function with the render from the generated auth live view and methodically work through the cycle of running the test and adding any missing references the new render depends on.

  def render(assigns) do
    ~H"""
    <div class="mx-auto max-w-sm">
      <.header class="text-center">
        Register for an account
        <:subtitle>
          Already registered?
          <.link navigate={~p"/users/log_in"} class="font-semibold text-brand hover:underline">
            Log in
          </.link>
          to your account now.
        </:subtitle>
      </.header>

      <.simple_form
        for={@form}
        id="registration_form"
        phx-submit="save"
        phx-change="validate"
        phx-trigger-action={@trigger_submit}
        action={~p"/users/log_in?_action=registered"}
        method="post"
      >
        <.error :if={@check_errors}>
          Oops, something went wrong! Please check the errors below.
        </.error>

        <.input field={@form[:email]} type="email" label="Email" required />
        <.input field={@form[:password]} type="password" label="Password" required />

        <:actions>
          <.button phx-disable-with="Creating account..." class="w-full">Create an account</.button>
        </:actions>
      </.simple_form>
    </div>
    """
  end

Once the test is passing, I continue the same cycle with the remaining tests to build out the registration live view. I skip any logic involving account confirmations since I don’t plan to implement this feature at this stage.