I’m testing a LiveView page with components that rely on an attach_hook to run.
The hook looks like this:
attach_hook(socket, :set_current_url, :handle_params, fn
_params, url, socket ->
uri = URI.parse(url)
if uri.query do
{:cont, assign(socket, current_url: uri.path <> "?" <> uri.query)}
else
{:cont, assign(socket, current_url: uri.path)}
end
end)
The component responses to a handle_event with: {:noreply, push_redirect(socket, to: socket.assigns.current_url)} and fails if current_url is not set. Since handle_params should be executed multiple times in the lifecycle, we do not experience any failure in dev/prod.
This executes the {:noreply, push_redirect(socket, to: socket.assigns.current_url)} mentioned above and fails because key :current_url not found in .... I tried debugging the issue and can see the hook being executed multiple times (simple IO.inspect() inside the hook.
You are correct, my code would not work! The actual code is split into a setup and test code. It works, the form is submitted but the code fails in the redirect because the current_url is missing.
can you show how the hook is attached so someone can try to reproduce the error? For example, are you attaching it in your mount/3 call back, or an on_mount/4 hook declared in the module body or in the router as a live session? And what version of live view? Looks like a strange error, if you provide more details on how to reproduce I would gladly give it a try.
Also just b/c I have to ask, there’s no little typo like uri vs url?
def live_view do
quote do
use Phoenix.LiveView,
layout: {AppWeb.LayoutView, "live.html"}
import AppWeb.LiveHelpers
unquote(view_helpers())
end
end
and inside the AppWeb.LiveHelpers, we have
def assign_defaults(session, socket) do
# some code
socket =
attach_hook(socket, :set_current_url, :handle_params, fn
_params, url, socket ->
uri = URI.parse(url)
if uri.query do
{:cont, assign(socket, current_url: uri.path <> "?" <> uri.query)}
else
{:cont, assign(socket, current_url: uri.path)}
end
end)
socket
end
Our LiveView version is 0.17.10.
Since it’s working when using it in dev, I don’t think there are any typos or weird stuff. And, as said, if I put a IO.inspect into the attach_hook callback, it gets called in the tests.
Feel free to ask as many questions as you need. I am the one to be thankful for the help!
In the LiveView itself, we use socket = assign_defaults(session, socket) inside the mount, yeah. The component does not have a mount and the update does not have the assign_defaults, but I think it doesn’t need it since it’s never mounted outside the LiveView itself?
There’s nothing that looks suspicious to me here, I think you’re gonna have to share more code. Even better, you could create a little github repo to showcase the error.
I agree, need to see more code. I tried to reproduce the error with the context that’s been given, but no success. I created a gist
Note that while I don’t actually push_redirect, my live view depends on the current_url assign being present or else it will crash. But my tests are green.
I have a suspicion that the answer lies in your update/2 callback of your live component. All of the assigns you need in your component must be explicitly handled in update/2 if you are defining the callback. If handle_event/3 is triggered on the component rather than the live_view, and your form has phx-target={@myself}, and you are not explicitly assigning :current_url in update/2, then you will get the key error on submit. This is probably the code we need to see that we haven’t yet seen . Your update/2 callback should have something like this:
def update(assigns, socket) do
{:ok, assign(socket, :current_url, assigns.current_url}
end
I think that your test is revealing a bug that you did not know that you have, because it is hidden from you in the browser. Since you are doing a self redirect to the same page, presumably reloading fresh data on mount, you are not noticing that you are actually crashing the live view and reloading the page from scratch. The result in the browser would look the same, you’d have to look at your logs to see the crash
You are correct. Wow, I wasted a ton of time there because I didn’t notice this crash and assumed the code is correct and something is wrong with the test. As you pointed out, I missed assigning the current_url in the update function and it crashed, but since it restarted right away and the redirect is intended to go to the same page, this crash is not noticeable from the frontend.
Thanks for your help! And also thanks to @trisolaran!