Phoenix Live View Render Children

A few weeks ago, I introduced a regression into our application that should have been caught by the test suite. This bug was in a LiveView that rendered an embedded LiveView. The embeded LiveView contains a Javascript client hook on mount that updates the sockets assigns and finally the DOM based on the results of a handle_call/3 callback. That’s a difficult few sentences to follow, but the following diagram encapsulates my explanation nicely:

Server
+----------------------------+                         
|      Parent LiveView       |                         
|                            |     Client                    
|                            |     +------------------+
|   +---------------+        |     |                  |
|   |  Embedded LV  |        |     |   JS Mount Hook  |
|   |               |        |     |                  |
|   |               |        |     |                  |
|   |               +--------+---->|                  |
|   |               |        |     |                  |
|   |     "do-work" |<-------+-----+ handle_call/3    |
|   +---------------+        |     |                  |
|                            |     +------------------+
|                            |                         
+----------------------------+                         

There was an untested bug within the handle_call/3 callback from the client. In order to assert the output of an embedded LiveView handle_call/3 function from the parent, one needs to reach for the live_children/1 function.

In my case, I had introduced a bug into the “do-work” function which raised an error. live_children/1 gives me the ability to have the test suite catch this bug at an ‘integration’ test level. The live_children/1 docs state:

Returns the current list of LiveView children for the parent LiveView. Children are returned in the order they appear in the rendered HTML.

Additionally, I need to call the “do-work” callback from the parent process. This is accomplished with the render_hook/3 function which:

Sends a hook event to the view or an element and returns the rendered result.

The test code looks similar to:

{:ok, view, html} = live(conn)
# match on the parent `view` process
assert [embedded_lv] = live_children(view)
# match on a list of child LiveView processes

render_hook(embedded_lv, "do-work")

The combination of live_children/1 with render_hook/3 gives you the ability to call hooks from a parent LiveView process. This approach is more useful than testing a callback in isolation.