tl;dr: MessageController/create does not add changeset validation errors to the flash, which breaks one of its tests.
On page 182 we write this test:
test "invalid params is rejected", %{conn: conn} do
conn = post(conn, ~p"/messages/new", %{}) assert html_response(conn, 200) =~
Plug.HTML.html_escape("can't be blank")
end
Which resulted in this error when I ran it:
1) test POST /messages/new invalid params is rejected (PhoneAppWeb.MessageControllerTest)
test/phone_app_web/controllers/message_controller_test.exs:34
Assertion with =~ failed
code: assert html_response(conn, 200) =~ Plug.HTML.html_escape("can't be blank")
left: "<!DOCTYPE html>\n<html lang=\"en\" class=\"[scrollbar-gutter:stable]\">\n <head>
[rest of page truncated]
right: "can't be blank"
After some valuable Elixir debugging practice, I believe I have the cause. The test in this case is looking for a “can’t be blank” error somewhere on the page. In MessageController/create, there is code that takes errors and puts them into the flash on line 48. However, that code is never reached because changeset validation happens on line 43 and the error shoots us down to line 55. That error handler doesn’t do anything specifically with the error. It passes the changeset to the view to render, but I don’t see where in the view it would display the error. Thus, the validation error doesn’t appear on the rendered view, and the test can’t find it.
Thanks, and just let me know if I’ve gotten something wrong.
Are you able to include the full test failure, without the rest of the page truncated? That will let me see if the message changed or if there’s some other potential issue here. Looking at the latest Phoenix codebase, I don’t expect this test would fail.
I recommend grabbing the online source code if you hit a wall. Although there’s a lot of value in the debugging process.
diff is telling me that there aren’t any differences from the code I originally copied from the zip archive (and then copied to my work directory) and the code in the zip you linked there, but I will check the views and CoreComponents in my working directory against the fresh zip download. Here’s those test results in the meantime, though!
That’s quite strange. Are you able to zip your full code folder (remove _build, deps, node_modules and it will zip up easily)?
I suspect the code is different somewhere, but it’s hard to tell without seeing the whole project. Immediately my head goes to router misconfiguration or CoreComponents differences.
I found a few differences between my code and the demo code. Most notably, there are changes in CoreComponents that deal with the flash and errors, so that could be the problem.
If I’m reading correctly, that file would have been generated by Phoenix and then never modified by us, no? I think it was generated by the same version of Phoenix in my mix.exs, 1.7.14. But lots of things are possible–at one point I set up a new Phoenix project and tried to copy my work over, and I don’t think I overwrote CoreComponents but I don’t have something like a git history to verify that with.
Thanks for sharing that. I took a look at CoreComponents.input and found the definition was slightly varied. I changed this line:
# used to be:
# errors = if Phoenix.Component.used_input?(field), do: field.errors, else: []
errors = field.errors
and the test passes. This was a change in the latest implementation of CoreComponents, hence the difference.
Now, to get this test passing without modifying the above line, I had to tweak the test to pass in blank inputs:
test "invalid params is rejected", %{conn: conn} do
params = %{message: %{to: "", body: ""}}
conn = post(conn, ~p"/messages/new", params)
assert html_response(conn, 200) =~
Plug.HTML.html_escape("can't be blank")
end
We can look through the implementation of used_input? to see why this works. By passing in the data, the form.params field contains the input field, so it counts as “used”