-
-
Notifications
You must be signed in to change notification settings - Fork 99
Expand file tree
/
Copy pathlive_view_test.exs
More file actions
302 lines (250 loc) · 8.41 KB
/
Copy pathlive_view_test.exs
File metadata and controls
302 lines (250 loc) · 8.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# SPDX-FileCopyrightText: 2020 ash_phoenix contributors <https://github.com/ash-project/ash_phoenix/graphs/contributors>
#
# SPDX-License-Identifier: MIT
defmodule AshPhoenixTest.LiveViewTest do
use ExUnit.Case
doctest AshPhoenix.LiveView
alias AshPhoenix.LiveView
# Tracks subscribe/unsubscribe calls during tests
defmodule TrackingPubSub do
def subscribe(topic), do: send(:live_view_test_proc, {:subscribed, topic})
def unsubscribe(topic), do: send(:live_view_test_proc, {:unsubscribed, topic})
end
setup do
if Process.whereis(:live_view_test_proc) == nil do
Process.register(self(), :live_view_test_proc)
end
on_exit(fn ->
if Process.whereis(:live_view_test_proc) == self() do
Process.unregister(:live_view_test_proc)
end
end)
:ok
end
# Builds a minimal non-LiveView socket compatible with keep_live/handle_live.
# Uses %Phoenix.Socket{} so Phoenix.Socket.assign/3 works.
defp socket(assigns \\ %{}) do
%Phoenix.Socket{assigns: assigns}
end
describe "subscriptions/1 validator" do
test "accepts a binary" do
assert {:ok, "topic"} = LiveView.subscriptions("topic")
end
test "accepts a list of binaries" do
assert {:ok, ["a", "b"]} = LiveView.subscriptions(["a", "b"])
end
test "accepts a 1-arity function" do
fun = fn _result -> "topic" end
assert {:ok, ^fun} = LiveView.subscriptions(fun)
end
test "rejects a 2-arity function" do
assert {:error, _} = LiveView.subscriptions(fn _a, _b -> "topic" end)
end
test "rejects a list with non-strings" do
assert {:error, _} = LiveView.subscriptions(["ok", :not_a_string])
end
end
describe "keep_live/4 with static subscribe" do
test "subscribes to a single static topic" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> :result end,
subscribe: "some:topic",
pub_sub: TrackingPubSub
)
assert_received {:subscribed, "some:topic"}
config = socket.assigns.ash_live_config[:data]
assert config.subscribed_topics == ["some:topic"]
end
test "subscribes to a list of static topics" do
LiveView.keep_live(
socket(),
:data,
fn _socket -> :result end,
subscribe: ["topic:a", "topic:b"],
pub_sub: TrackingPubSub
)
assert_received {:subscribed, "topic:a"}
assert_received {:subscribed, "topic:b"}
end
test "stores nil subscribed_topics when no subscribe configured" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> :result end
)
config = socket.assigns.ash_live_config[:data]
assert config.subscribed_topics == nil
end
end
describe "keep_live/4 with dynamic subscribe function" do
test "calls the subscribe function with the callback result" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> %{id: 42} end,
subscribe: fn result -> "resource:#{result.id}" end,
pub_sub: TrackingPubSub
)
assert_received {:subscribed, "resource:42"}
config = socket.assigns.ash_live_config[:data]
assert config.subscribed_topics == ["resource:42"]
end
test "supports returning a list of topics from the subscribe function" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> %{id: 1, org_id: 99} end,
subscribe: fn result -> ["resource:#{result.id}", "org:#{result.org_id}"] end,
pub_sub: TrackingPubSub
)
assert_received {:subscribed, "resource:1"}
assert_received {:subscribed, "org:99"}
config = socket.assigns.ash_live_config[:data]
assert config.subscribed_topics == ["resource:1", "org:99"]
end
test "uses initial value when provided to compute topics" do
_socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> :not_called end,
initial: %{id: 7},
subscribe: fn result -> "resource:#{result.id}" end,
pub_sub: TrackingPubSub
)
assert_received {:subscribed, "resource:7"}
end
end
describe "handle_live/4 topic matching" do
test "refetches when topic matches a static subscribed topic" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> :v1 end,
subscribe: "my:topic",
pub_sub: TrackingPubSub
)
flush_messages()
updated = LiveView.handle_live(socket, "my:topic", :data)
assert updated.assigns.data == :v1
end
test "does not refetch when topic does not match static subscribed topics" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> :initial end,
subscribe: "my:topic",
pub_sub: TrackingPubSub
)
updated = LiveView.handle_live(socket, "other:topic", :data)
# assign unchanged
assert updated.assigns.data == :initial
assert updated == socket
end
test "refetches when topic matches a dynamically computed topic" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> %{id: 5} end,
subscribe: fn result -> "resource:#{result.id}" end,
pub_sub: TrackingPubSub
)
flush_messages()
updated = LiveView.handle_live(socket, "resource:5", :data)
assert updated.assigns.data == %{id: 5}
end
test "does not refetch when topic does not match dynamic subscribed topics" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> %{id: 5} end,
subscribe: fn result -> "resource:#{result.id}" end,
pub_sub: TrackingPubSub
)
original_config = socket.assigns.ash_live_config
updated = LiveView.handle_live(socket, "resource:99", :data)
assert updated.assigns.ash_live_config == original_config
end
test "refetches on any topic when no subscribe is configured" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> :result end
)
updated = LiveView.handle_live(socket, "anything:at:all", :data)
assert updated.assigns.data == :result
end
end
describe "handle_live/4 subscription diffing on refetch" do
test "unsubscribes from removed topics and subscribes to new ones" do
counter = :counters.new(1, [])
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket ->
count = :counters.get(counter, 1)
:counters.add(counter, 1, 1)
%{id: count}
end,
subscribe: fn result -> "resource:#{result.id}" end,
pub_sub: TrackingPubSub
)
# After keep_live: subscribed to "resource:0"
assert_received {:subscribed, "resource:0"}
# Trigger a refetch — callback now returns %{id: 1}
updated = LiveView.handle_live(socket, "resource:0", :data)
assert_received {:unsubscribed, "resource:0"}
assert_received {:subscribed, "resource:1"}
new_config = updated.assigns.ash_live_config[:data]
assert new_config.subscribed_topics == ["resource:1"]
end
test "does not unsubscribe/subscribe when topics are unchanged after refetch" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> %{id: 42} end,
subscribe: fn _result -> "stable:topic" end,
pub_sub: TrackingPubSub
)
assert_received {:subscribed, "stable:topic"}
_updated = LiveView.handle_live(socket, "stable:topic", :data)
refute_received {:unsubscribed, _}
refute_received {:subscribed, _}
end
test "static subscriptions are not re-evaluated on refetch" do
socket =
LiveView.keep_live(
socket(),
:data,
fn _socket -> :result end,
subscribe: "static:topic",
pub_sub: TrackingPubSub
)
assert_received {:subscribed, "static:topic"}
_updated = LiveView.handle_live(socket, "static:topic", :data)
refute_received {:unsubscribed, _}
refute_received {:subscribed, _}
end
end
# Drain any messages accumulated before the assertion point
defp flush_messages do
receive do
_ -> flush_messages()
after
0 -> :ok
end
end
end