Skip to content

Commit 8518e61

Browse files
committed
Allow sharing assigns between live navigation
This commit relies on a yet to be finalized feature in Phoenix Channels to perform a custom handover between channel rejoins. It changes the LV code to not leave the old channel before rejoining and also instructs Phoenix to not kill the old process before starting the new channel process. After the new channel is joined, the old one is killed. Any `assign_new` calls in the LV mount will try to fetch assigns from the old LV. This is a backwards compatible optimization, so if a version of Phoenix is used that does not support handover, it just falls back to calling the function supplied to assign_new as usual. Closes #3357.
1 parent 4d3def7 commit 8518e61

10 files changed

Lines changed: 134 additions & 33 deletions

File tree

assets/js/phoenix_live_view/live_socket.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ export default class LiveSocket {
390390
let removeEls = DOM.all(this.outgoingMainEl, `[${this.binding("remove")}]`)
391391
let newMainEl = DOM.cloneNode(this.outgoingMainEl, "")
392392
this.main.showLoader(this.loaderTimeout)
393-
this.main.destroy()
393+
this.main.destroy(false)
394394

395395
this.main = this.newRootView(newMainEl, flash, liveReferer)
396396
this.main.setRedirect(href)
@@ -407,7 +407,7 @@ export default class LiveSocket {
407407
onDone()
408408
})
409409
}
410-
})
410+
}, true)
411411
}
412412

413413
transitionRemoves(elements, skipSticky, callback){

assets/js/phoenix_live_view/view.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ export default class View {
205205
return val === "" ? null : val
206206
}
207207

208-
destroy(callback = function (){ }){
208+
destroy(leave = true, callback = function (){ }){
209209
this.destroyAllChildren()
210210
this.destroyed = true
211211
delete this.root.children[this.id]
@@ -221,10 +221,14 @@ export default class View {
221221
DOM.markPhxChildDestroyed(this.el)
222222

223223
this.log("destroyed", () => ["the child has been removed from the parent"])
224-
this.channel.leave()
225-
.receive("ok", onFinished)
226-
.receive("error", onFinished)
227-
.receive("timeout", onFinished)
224+
if(leave){
225+
this.channel.leave()
226+
.receive("ok", onFinished)
227+
.receive("error", onFinished)
228+
.receive("timeout", onFinished)
229+
} else {
230+
onFinished()
231+
}
228232
}
229233

230234
setContainerClasses(...classes){
@@ -795,7 +799,7 @@ export default class View {
795799
return this.joinPush
796800
}
797801

798-
join(callback){
802+
join(callback, handover = false){
799803
this.showLoader(this.liveSocket.loaderTimeout)
800804
this.bindChannel()
801805
if(this.isMain()){
@@ -806,7 +810,7 @@ export default class View {
806810
callback ? callback(this.joinCount, onDone) : onDone()
807811
}
808812

809-
this.wrapPush(() => this.channel.join(), {
813+
this.wrapPush(() => this.channel.join(this.liveSocket.socket.timeout, handover), {
810814
ok: (resp) => this.liveSocket.requestDOMUpdate(() => this.onJoin(resp)),
811815
error: (error) => this.onJoinError(error),
812816
timeout: () => this.onJoinError({reason: "timeout"})

lib/phoenix_live_view/channel.ex

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ defmodule Phoenix.LiveView.Channel do
399399
end)
400400
end
401401

402-
def handle_call({@prefix, :child_mount, _child_pid, assign_new}, _from, state) do
402+
def handle_call({@prefix, :get_assigns, _child_pid, assign_new}, _from, state) do
403403
assigns = Map.take(state.socket.assigns, assign_new)
404404
{:reply, {:ok, assigns}, state}
405405
end
@@ -1123,6 +1123,10 @@ defmodule Phoenix.LiveView.Channel do
11231123
transport_pid: transport_pid
11241124
} = phx_socket
11251125

1126+
# TODO: change this to directly pattern match on handover_pid above
1127+
# when we require Phoenix 1.8
1128+
handover_pid = Map.get(phx_socket, :handover_pid)
1129+
11261130
Process.put(:"$initial_call", {view, :mount, 3})
11271131

11281132
case params do
@@ -1164,7 +1168,15 @@ defmodule Phoenix.LiveView.Channel do
11641168
merged_session = Map.merge(socket_session, verified_user_session)
11651169
lifecycle = load_lifecycle(config, route)
11661170

1167-
case mount_private(parent, root_view, assign_new, connect_params, connect_info, lifecycle) do
1171+
case mount_private(
1172+
parent,
1173+
root_view,
1174+
assign_new,
1175+
connect_params,
1176+
connect_info,
1177+
lifecycle,
1178+
handover_pid
1179+
) do
11681180
{:ok, mount_priv} ->
11691181
socket = Utils.configure_socket(socket, mount_priv, action, flash, host_uri)
11701182

@@ -1254,20 +1266,37 @@ defmodule Phoenix.LiveView.Channel do
12541266
socket
12551267
end
12561268

1257-
defp mount_private(nil, root_view, assign_new, connect_params, connect_info, lifecycle) do
1269+
defp mount_private(
1270+
nil,
1271+
root_view,
1272+
assign_new,
1273+
connect_params,
1274+
connect_info,
1275+
lifecycle,
1276+
handover_pid
1277+
) do
12581278
{:ok,
12591279
%{
12601280
connect_params: connect_params,
12611281
connect_info: connect_info,
12621282
assign_new: {%{}, assign_new},
12631283
lifecycle: lifecycle,
12641284
root_view: root_view,
1265-
live_temp: %{}
1285+
live_temp: %{},
1286+
handover_pid: handover_pid
12661287
}}
12671288
end
12681289

1269-
defp mount_private(parent, root_view, assign_new, connect_params, connect_info, lifecycle) do
1270-
case sync_with_parent(parent, assign_new) do
1290+
defp mount_private(
1291+
parent,
1292+
root_view,
1293+
assign_new,
1294+
connect_params,
1295+
connect_info,
1296+
lifecycle,
1297+
_handover_pid
1298+
) do
1299+
case get_assigns(parent, assign_new) do
12711300
{:ok, parent_assigns} ->
12721301
# Child live views always ignore the layout on `:use`.
12731302
{:ok,
@@ -1278,17 +1307,18 @@ defmodule Phoenix.LiveView.Channel do
12781307
live_layout: false,
12791308
lifecycle: lifecycle,
12801309
root_view: root_view,
1281-
live_temp: %{}
1310+
live_temp: %{},
1311+
handover_pid: nil
12821312
}}
12831313

12841314
{:error, :noproc} ->
12851315
{:error, :noproc}
12861316
end
12871317
end
12881318

1289-
defp sync_with_parent(parent, assign_new) do
1319+
def get_assigns(pid, keys) do
12901320
try do
1291-
GenServer.call(parent, {@prefix, :child_mount, self(), assign_new})
1321+
GenServer.call(pid, {@prefix, :get_assigns, self(), keys})
12921322
catch
12931323
:exit, {:noproc, _} -> {:error, :noproc}
12941324
end

lib/phoenix_live_view/socket.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ defmodule Phoenix.LiveView.Socket do
9696
}
9797

9898
channel "lvu:*", Phoenix.LiveView.UploadChannel
99-
channel "lv:*", Phoenix.LiveView.Channel
99+
channel "lv:*", Phoenix.LiveView.Channel, handover_on_rejoin: true
100100

101101
@impl Phoenix.Socket
102102
def connect(_params, %Phoenix.Socket{} = socket, connect_info) do
@@ -111,7 +111,7 @@ defmodule Phoenix.LiveView.Socket do
111111
use Phoenix.Socket
112112

113113
channel "lvu:*", Phoenix.LiveView.UploadChannel
114-
channel "lv:*", Phoenix.LiveView.Channel
114+
channel "lv:*", Phoenix.LiveView.Channel, handover_on_rejoin: true
115115

116116
def connect(params, socket, info), do: {:ok, socket}
117117
defdelegate id(socket), to: unquote(__MODULE__)

lib/phoenix_live_view/utils.ex

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ defmodule Phoenix.LiveView.Utils do
4848
%{assigns: %{^key => _}} ->
4949
socket
5050

51-
%{private: %{assign_new: {assigns, keys}}} ->
51+
%{private: %{assign_new: {assigns, keys}} = private} ->
5252
# It is important to store the keys even if they are not in assigns
5353
# because maybe the controller doesn't have it but the view does.
5454
socket = put_in(socket.private.assign_new, {assigns, [key | keys]})
@@ -58,7 +58,7 @@ defmodule Phoenix.LiveView.Utils do
5858
key,
5959
case assigns do
6060
%{^key => value} -> value
61-
%{} -> fun.(socket.assigns)
61+
%{} -> maybe_handover_assign(socket, key, private[:handover_pid], fun)
6262
end
6363
)
6464

@@ -72,17 +72,36 @@ defmodule Phoenix.LiveView.Utils do
7272
%{assigns: %{^key => _}} ->
7373
socket
7474

75-
%{private: %{assign_new: {assigns, keys}}} ->
75+
%{private: %{assign_new: {assigns, keys}} = private} ->
7676
# It is important to store the keys even if they are not in assigns
7777
# because maybe the controller doesn't have it but the view does.
7878
socket = put_in(socket.private.assign_new, {assigns, [key | keys]})
79-
Phoenix.LiveView.Utils.force_assign(socket, key, Map.get_lazy(assigns, key, fun))
79+
80+
Phoenix.LiveView.Utils.force_assign(
81+
socket,
82+
key,
83+
Map.get_lazy(assigns, key, fn ->
84+
maybe_handover_assign(socket, key, private[:handover_pid], fun)
85+
end)
86+
)
8087

8188
%{} ->
8289
Phoenix.LiveView.Utils.force_assign(socket, key, fun.())
8390
end
8491
end
8592

93+
defp maybe_handover_assign(_socket, _key, nil, fun) when is_function(fun, 0), do: fun.()
94+
95+
defp maybe_handover_assign(socket, _key, nil, fun) when is_function(fun, 1),
96+
do: fun.(socket.assigns)
97+
98+
defp maybe_handover_assign(socket, key, pid, fun) when is_pid(pid) do
99+
case Phoenix.LiveView.Channel.get_assigns(pid, [key]) do
100+
{:ok, %{^key => value}} -> value
101+
_ -> maybe_handover_assign(socket, key, nil, fun)
102+
end
103+
end
104+
86105
@doc """
87106
Forces an assign on a socket.
88107
"""
@@ -194,7 +213,7 @@ defmodule Phoenix.LiveView.Utils do
194213
socket
195214
|> clear_changed()
196215
|> clear_temp()
197-
|> drop_private([:connect_info, :connect_params, :assign_new])
216+
|> drop_private([:connect_info, :connect_params, :assign_new, :handover_pid])
198217
end
199218

200219
@doc """

mix.exs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ defmodule Phoenix.LiveView.MixProject do
4141

4242
defp deps do
4343
[
44-
{:phoenix, "~> 1.6.15 or ~> 1.7.0"},
44+
# {:phoenix, "~> 1.6.15 or ~> 1.7.0"},
45+
# TODO: remove before merging
46+
{:phoenix,
47+
github: "phoenixframework/phoenix", branch: "sd-handover-assigns", override: true},
4548
{:plug, "~> 1.15"},
4649
{:phoenix_template, "~> 1.0"},
4750
{:phoenix_html, "~> 3.3 or ~> 4.0 or ~> 4.1"},

mix.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
%{
22
"bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"},
3-
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
3+
"castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"},
44
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
55
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
66
"ecto": {:hex, :ecto, "3.12.1", "626765f7066589de6fa09e0876a253ff60c3d00870dd3a1cd696e2ba67bfceea", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df0045ab9d87be947228e05a8d153f3e06e0d05ab10c3b3cc557d2f7243d1940"},
@@ -19,7 +19,7 @@
1919
"makeup_html": {:hex, :makeup_html, "0.1.1", "c3d4abd39d5f7e925faca72ada6e9cc5c6f5fa7cd5bc0158315832656cf14d7f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "44f2a61bc5243645dd7fafeaa6cc28793cd22f3c76b861e066168f9a5b2c26a4"},
2020
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
2121
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
22-
"phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"},
22+
"phoenix": {:git, "https://github.com/phoenixframework/phoenix.git", "58cae7e9ca0e53bdc96c445210ce3f11bfdd5dce", [branch: "sd-handover-assigns"]},
2323
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.2", "3b83b24ab5a2eb071a20372f740d7118767c272db386831b2e77638c4dcc606d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "3f94d025f59de86be00f5f8c5dd7b5965a3298458d21ab1c328488be3b5fcd59"},
2424
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
2525
"phoenix_html_helpers": {:hex, :phoenix_html_helpers, "1.0.1", "7eed85c52eff80a179391036931791ee5d2f713d76a81d0d2c6ebafe1e11e5ec", [:mix], [{:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cffd2385d1fa4f78b04432df69ab8da63dc5cf63e07b713a4dcf36a3740e3090"},
@@ -29,7 +29,7 @@
2929
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
3030
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
3131
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
32-
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
32+
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
3333
"thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"},
3434
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
3535
"websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"},

test/e2e/support/navigation.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ defmodule Phoenix.LiveViewTest.E2E.Navigation.ALive do
5656
def mount(_params, _session, socket) do
5757
socket
5858
|> assign(:param_current, nil)
59+
|> assign_new(:foo, fn -> "bar" end)
5960
|> then(&{:ok, &1})
6061
end
6162

@@ -73,6 +74,7 @@ defmodule Phoenix.LiveViewTest.E2E.Navigation.ALive do
7374
def render(assigns) do
7475
~H"""
7576
<h1>This is page A</h1>
77+
<p>Foo: <%= @foo %></p>
7678
7779
<p>Current param: <%= @param_current %></p>
7880
@@ -100,6 +102,7 @@ defmodule Phoenix.LiveViewTest.E2E.Navigation.BLive do
100102
@impl Phoenix.LiveView
101103
def mount(_params, _session, socket) do
102104
socket
105+
|> assign_new(:foo, fn -> "baz" end)
103106
|> then(&{:ok, &1})
104107
end
105108

@@ -128,6 +131,7 @@ defmodule Phoenix.LiveViewTest.E2E.Navigation.BLive do
128131
def render(assigns) do
129132
~H"""
130133
<h1>This is page B</h1>
134+
<p>Foo: <%= @foo %></p>
131135
132136
<a
133137
href="#items-item-42"

test/e2e/tests/navigation.spec.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@ test("can navigate between LiveViews in the same live session over websocket", a
5656
await expect(networkEvents).toEqual([]);
5757
// we don't assert the order of the events here, because they are not deterministic
5858
await expect(webSocketEvents).toEqual(expect.arrayContaining([
59-
{ type: "sent", payload: expect.stringContaining("phx_leave") },
6059
{ type: "sent", payload: expect.stringContaining("phx_join") },
61-
{ type: "received", payload: expect.stringContaining("phx_close") },
6260
{ type: "received", payload: expect.stringContaining("phx_reply") },
6361
{ type: "received", payload: expect.stringContaining("phx_reply") },
6462
]));
@@ -128,7 +126,6 @@ test("falls back to http navigation when navigating between live sessions", asyn
128126

129127
await expect(networkEvents).toEqual(expect.arrayContaining([{ method: "GET", url: "http://localhost:4004/stream" }]));
130128
await expect(webSocketEvents).toEqual(expect.arrayContaining([
131-
{ type: "sent", payload: expect.stringContaining("phx_leave") },
132129
{ type: "sent", payload: expect.stringContaining("phx_join") },
133130
{ type: "received", payload: expect.stringMatching(/error.*unauthorized/) },
134131
].concat(browserName === "webkit" ? [] : [{ type: "close" }])));
@@ -236,7 +233,24 @@ test("navigating all the way back works without remounting (only patching)", asy
236233
await syncLV(page);
237234
await expect(networkEvents).toEqual([]);
238235
// we only expect patch navigation
239-
await expect(webSocketEvents.filter(e => e.payload.indexOf("phx_leave") !== -1)).toHaveLength(0);
236+
await expect(webSocketEvents.filter(e => e.payload.indexOf("phx_join") !== -1)).toHaveLength(1);
240237
// we patched 2 times
241238
await expect(webSocketEvents.filter(e => e.payload.indexOf("live_patch") !== -1)).toHaveLength(2);
242239
});
240+
241+
test("sharing assigns between live navigation", async ({ page }) => {
242+
await page.goto("/navigation/a");
243+
await syncLV(page);
244+
245+
await expect(page.getByText("Foo:")).toContainText("bar");
246+
await page.getByRole("link", { name: "LiveView B" }).click();
247+
await syncLV(page);
248+
await expect(page.getByText("Foo:")).toContainText("bar");
249+
250+
await page.reload();
251+
await syncLV(page);
252+
await expect(page.getByText("Foo:")).toContainText("baz");
253+
await page.getByRole("link", { name: "LiveView A" }).click();
254+
await syncLV(page);
255+
await expect(page.getByText("Foo:")).toContainText("baz");
256+
});

0 commit comments

Comments
 (0)