When a user has at least one active game, they will see a Games in Progress section on their authenticated home page. This list only shows the join code string for each active game which isn’t too helpful unless the user knows how to plug in the code to the correct URL path. The next feature I’m building is to add clickable links which take the user to the associated game page.

I start by writing a feature test using Wallaby for how I expect this scenario to behave, not worrying that the setup functions do not yet exist.

defmodule MinotaurWeb.Home.UserJoinsActiveGameTest do
  @moduledoc false

  use Minotaur.FeatureCase

  describe "user clicks join link in active games list" do
    setup [:create_user, :start_games, :sign_in_user, :click_join_link]

    test "redirects user to game view for chosen game", ctx do
      assert current_path(ctx.session) == "/games/#{ctx.join_code}"
    end
  end
end

I then start the cycle of running the test, getting a compilation error, fixing the compilation error, and repeating until the test fully compiles.

defmodule MinotaurWeb.Home.UserJoinsActiveGameTest do
  @moduledoc false

  use Minotaur.FeatureCase

  import Minotaur.GameEngineFixtures

  alias Minotaur.GameEngine

  describe "user clicks join button in active games list" do
    setup [:create_user, :start_games, :sign_in_user, :click_join_button]

    test "redirects user to game view for chosen game", ctx do
      assert current_path(ctx.session) == "/games/#{ctx.game2_join_code}"
    end
  end

  defp create_user(_ctx) do
    [user: user_fixture()]
  end

  defp start_game(join_code, user) do
    players = [
      player_fixture(%{user_id: user.id}),
      player_fixture(%{user_id: user_fixture().id})
    ]

    game = game_fixture(players: players)
    {:ok, _pid} = GameEngine.continue_game(join_code, game)

    game
  end

  defp start_games(%{user: user}) do
    game1_join_code = "AABB"
    game2_join_code = "CCDD"
    start_game(game1_join_code, user)
    start_game(game2_join_code, user)

    [game2_join_code: game2_join_code]
  end

  defp sign_in_user(%{session: session, user: user}) do
    sign_in(session, as: user)

    :ok
  end

  defp click_join_link(ctx) do
    :ok
  end
end

I next add logic for click_join_link which will help me design the way links will be added to the page in a testable way.

  defp click_join_button(%{session: session, game2_join_code: join_code}) do
    session =
      session
      |> find(Query.css("#games-in-progress #game_#{join_code}"), fn element ->
        click(element, Query.link("Join"))
      end)

    [session: session]
  end

The test is still failing since the join link does not yet exist. I update the HEEx template for the home live page to match the elements expected by the feature test helper function. I won’t worry about styling until after the functionality is complete first.

<%= for game <- @games_in_progress do %>
  <li id={"game_#{game.join_code}"}>
    <%= game.join_code %>
    <.link navigate={~p"/game/#{game.join_code}"}>Join</.link>
  </li>
<% end %>

The test is now finding the join link, but the assertion on the URL path is failing. After debugging with Wallaby’s take_screenshot call after the click event, I see the test probably needs more time for the browser to load the view. I update the test with a find call to look for the round timer in the game view which will block until the queried element is found.

    test "redirects user to game view for chosen game", ctx do
      ctx.session
      |> find(Query.css("#round-timer"), fn element ->
        assert_text(element, "00:")
      end)

      assert current_path(ctx.session) == "/game/#{ctx.game2_join_code}"
    end

With that change, the test is passing!