Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test using hidden inputs #37

Closed
revoltaxz opened this issue Feb 27, 2024 · 8 comments
Closed

Test using hidden inputs #37

revoltaxz opened this issue Feb 27, 2024 · 8 comments

Comments

@revoltaxz
Copy link

Hello! I'm trying to test a Live page with a form with a LiveSelect component. The LiveSelect provides a component with a hidden input with value, but when I try using the fill_form or submit_form to pass the arguments, I get the following error:

######### Test ############# 
 form_attrs = %{
        symbol: "PETR",
        category: "stock",
        symbol_text_input: "PETR4",
        price: "12.50",
        quantity: 100,
        open_date: "2024-04-01",
        direction: "short"
      }

      conn
      |> visit("/strategy-builder")
      |> submit_form("#position_form", position: form_attrs)
      |> open_browser()

####### Error ##########
 test/myield_web/live/strategy_builder_live/index_test.exs:5
     ** (ArgumentError) value for hidden "position[symbol]" must be one of [""], got: "PETR"
     code: |> submit_form("#position_form", position: form_attrs)
     stacktrace:
       (phoenix_live_view 0.20.9) lib/phoenix_live_view/test/live_view_test.ex:1102: Phoenix.LiveViewTest.call/2
       (phoenix_test 0.2.7) lib/phoenix_test/live.ex:127: PhoenixTest.Driver.PhoenixTest.Live.submit_form/3
       test/myield_web/live/strategy_builder_live/index_test.exs:19: (test)

Is there a way to test with hidden inputs in the form?

@germsvel
Copy link
Owner

Hi @revoltaxz, thanks for opening this issue!

I think I see why your form is failing, and I think it's something we should fix.

The problem

PhoenixTest uses LiveViewTest's render_submit/2 helper for LiveView form submissions.

From the docs we can see:

To submit a form along with some with hidden input values:

assert view
       |> form("#term", user: %{name: "hello"})
       |> render_submit(%{user: %{"hidden_field" => "example"}}) =~ "Name updated"

But right now, we're passing all the form data to form/3 -- so we use render_submit/2 without passing hidden values there.

We need to update the code to pass hidden inputs through render_submit/2's second argument.

I'll try to get to that when I can, but if someone wants to take a stab at it first, feel free!

I don't think it'll be too straightforward since we have to parse the form and pull the hidden fields. But we already do some parsing, so it might not be too complicated either.

@germsvel
Copy link
Owner

germsvel commented Feb 28, 2024

@revoltaxz could you share the HTML form you're trying to test that's giving you that error? I'm trying to reproduce this issue, but not hitting it for some reason.

I'm now thinking that the current implementation works (regardless of what the docs say).

And I'm guessing that you're getting that error because your hidden field's HTML doesn't have a value attribute set. That's why you're seeing the must be one of [""] error.

@revoltaxz
Copy link
Author

@germsvel Sorry my late! Yes, of course, I could:

 def render(assigns) do
    ~H"""
    <div>
      <.simple_form
        for={@form}
        id={@id}
        phx-submit="submit"
        phx-target={@myself}
        phx-change="validate"
      >
        <div class="grid xl:grid-cols-2 lg:grid-cols-2 md:grid-cols-2 sm:grid-cols-1 gap-3">
          <.live_select
            field={@form[:symbol]}
            label="Buscar por"
            placeholder="Insira ao menos 4 letras"
            phx-target={@myself}
            phx-focus="clear"
            debounce="500"
            options={[]}
            update_min_len={4}
            dropdown_extra_class="max-h-60 overflow-y-scroll"
          />
          <.field
            field={@form[:category]}
            label="Tipo de posição"
            type="select"
            options={Helpers.asset_types()}
            phx-change="change_category"
          />
          <.field
            field={@form[:quantity]}
            label="Quantidade"
            type="number"
            step="100"
            min="100"
            value="100"
          />
          <.field field={@form[:price]} label="Valor unitário (R$)" type="text" phx-hook="MoneyMask" />
          <.field field={@form[:open_date]} label="Data de compra" type="date" />

          <%= if @category == "option" do %>
            <.field
              field={@form[:contract_type]}
              label="Tipo de opção"
              type="select"
              options={Helpers.options_types()}
            />
          <% end %>
        </div>

        <.field
          field={@form[:direction]}
          label="Tipo de operação"
          type="radio-group"
          group_layout="col"
          options={Helpers.operation_types()}
        />
        <.button type="submit" phx-disable-with="Salvando..." label="Adicionar" />
      </.simple_form>
    </div>
    """
  end

OBS: I'm using the LiveSelect.

@germsvel
Copy link
Owner

germsvel commented Mar 1, 2024

@revoltaxz thanks for posting the form. I've never used LiveSelect. I don't see a hidden input there. I imagine the LiveSelect library is doing some hidden input stuff behind the scenes?

Do you know how you would test that with LiveView? Or how were you testing that previously?

@revoltaxz
Copy link
Author

@germsvel No, I was trying to write tests using PhoenixTest, but I found a PR on the Live Select repo with some instructions, so I think that it is possible to pass the hidden input as you mentioned here. Do you think this helps you?

@germsvel
Copy link
Owner

germsvel commented Mar 1, 2024

@revoltaxz I don't think it's a matter of whether or not we can test the hidden inputs.

If you look at this branch, I added a test that passes when testing hidden inputs.

I think the issue is that whatever hidden input LiveSelect is using isn't setting a value properly (or maybe it sets it with JS or something). But that's why you're seeing the error you're seeing:

** (ArgumentError) value for hidden "position[symbol]" must be one of [""], got: "PETR"

LiveViewTest is saying that position[symbol] must be one of <potential value in html element>, but it got "PETR" instead.

So, to give you another example, if you had this HTML

<input type="hidden" name="admin" value="true"/>

And you tried testing it like this:

     |> submit_form("#position_form", admin: "Roger")

You would get the error:

** (ArgumentError) value for hidden "admin" must be one of ["true"], got: "Roger"

So, I don't think it's an issue with PhoenixTest. It's an issue of LiveSelect doing something with hidden inputs that is not matching what you're passing to your form.

Does that make sense?

@revoltaxz
Copy link
Author

revoltaxz commented Mar 5, 2024

Oh yes, it makes sense! So, I will close this issue and try a workaround with LiveSelect! Thank you for helping me!

@maxmarcon
Copy link

maxmarcon commented Jun 19, 2024

Late to the party 😃 I just stumbled upon this thread yesterday by chance...

@revoltaxz @germsvel I have no experience with PhoenixTest (seems like a cool library btw, I will take a look!), but in order to test a form containing a LiveSelect input with the standard Phoenix.LiveViewTest functions the following trick should work:

form_attrs = %{
        symbol: "PETR",
        category: "stock",
        symbol_text_input: "PETR4",
        price: "12.50",
        quantity: 100,
        open_date: "2024-04-01",
        direction: "short"
}

Phoenix.LiveView.send_update(
      live.pid, # live = your test's live view
      LiveSelect.Component,
      %{id: live_select_id, value: form_attrs.symbol}
     # `live_select_id` is the id of your live select input component
     # (you can pass it explicitly, otherwise, one is generated automatically from form + field name)
)

conn
|> form("#position_form", form_attrs)
|> render_submit()

I tested it and it worked for me.

Yes,LiveSelect does use a hidden input to send the value to the form, and the hidden input's value is set dynamically according to what the user has selected. The value is not set with JS, it's an ordinary LV binding.
However, the interaction with the user (i.e. opening the dropdown and selecting an element) does use JS, and that doesn't run in LV tests of course.

So the send_update/3 call above is used to bypasss the user interaction and programmatically force a selection, which will set the value for the hidden input field.

I think I will add a section to the docs documenting this, since quite a few folks asked similar questions already

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants