How to test LiveView with conditional form elements

I have a form that starts like this:

image

When clicking “yes”, another form element appears:

The markup looks something like this:

    <%= f = form_container @changeset %>
      <%= question_title "Are you currently married?" %>
      <%= yes_no_buttons(f, :is_married) %>
      <%= if get_field(f, :is_married) do %>
        <div>
         <%= date_input_container(f, :marriage_date, "Marriage Date") %>
        </div>
      <% end %>
      <%= submit_button(f, @form_info) %>
    </form>

My initial attempt at a test looked something like this:

test "integration test", %{conn: conn} do
  form = create_form()

  {:ok, view, _disconnected_html} =
    live(conn, Routes.the_thing_path(conn, :index, form.uuid))

  view
  |> form("#step-form", data: %{"is_married" => "yes", "marriage_date" => "03/03/1999"})
  |> render_submit()
end

But this gives the following error, since the marriage_date input doesn’t exist until after is_married has been set:

     ** (ArgumentError) could not find non-disabled input, select or textarea with name "data[marriage_date]" within:

         <input name="_csrf_token" type="hidden" value="MHc6UyMLHB4HXjMDKCAkAiUJFTUGLTx0b2vgBHCHIleiwpirqBbpYBD1"/>
         <input class="form-check-input" id="step-form_is_married_yes" name="data[is_married]" type="radio" value="yes"/>
         <input class="form-check-input" id="step-form_is_married_no" name="data[is_married]" type="radio" value="no"/>

What would be a good way to integration test this scenario? I could attempt to “preload” some initial data into the data structure, but that sort of defeats the purpose of integration testing. I would hope that I could do something like:

view
|> form("#step-form")
|> update_form(data: %{"is_married" => true})
|> update_form(data: %{"is_married" => true, "marriage_date" => "03/03/1999"})
|> render_submit()

But that’s not a thing, and as far as I can tell, none of the provided functionality in LiveViewTest allows one to “interactively” fill out the form. I could settle for maybe manually calling the handle_event call in my LiveView that handles the data updates, but I don’t see how that’s done either. Any ideas?

1 Like
view
|> element("#step-form") 
|> render_change(%{data: %{"is_married": "yes"}})

view
|> form("#step-form", %{data: %{"is_married" => "yes", "marriage_date" => "03/03/1999"}})
|> render_submit
3 Likes

Awesome, thank you! It throws me off that the view is stateful, I kept thinking once a render_* method was called, the view was “gone” and you just had the rendered string left, but it makes more sense now.

3 Likes