develop remote page and test it

This commit is contained in:
Victor Martinez 2024-03-29 15:56:08 +01:00
parent adba07c752
commit 643ec77f0b
4 changed files with 257 additions and 0 deletions

View file

@ -0,0 +1,109 @@
defmodule IntisyncWeb.RemoteLive do
alias Intisync.SessionPubSub
use IntisyncWeb, :live_view
alias IntisyncWeb.LiveViewMonitor
alias Intisync.SessionsSupervisor
alias Intisync.SessionServer
def mount(_params, _session, socket) do
{:ok, assign(socket, :devices, %{})}
end
def handle_params(%{"id" => session_id}, _uri, socket) do
if connected?(socket),
do: handle_connected(session_id, socket),
else: {:noreply, socket}
end
def unmount(_reason, {session_server_pid}) do
SessionServer.remote_disconnected(session_server_pid)
end
defp handle_connected(session_id, socket) do
case lookup_session_server(session_id) do
{:error, :not_found} ->
socket = socket |> put_flash(:error, "This session expired!") |> redirect(to: "/")
{:noreply, socket}
{:error, :unauthorized} ->
socket = socket |> put_flash(:error, "Unauthorized") |> redirect(to: "/")
{:noreply, socket}
{:ok, pid} ->
enable_subscriptions(session_id)
LiveViewMonitor.monitor(self(), __MODULE__, {pid})
SessionServer.remote_connected(pid)
devices = SessionServer.get_devices(pid)
socket =
socket
|> assign(:session_id, session_id)
|> assign(:session_server_pid, pid)
|> assign(:devices, devices)
{:noreply, socket}
end
end
defp lookup_session_server(session_id) do
case SessionsSupervisor.whereis(session_id) do
nil ->
{:error, :not_found}
pid ->
if SessionServer.is_full?(pid) do
{:error, :unauthorized}
else
{:ok, pid}
end
end
end
defp enable_subscriptions(session_id) do
SessionPubSub.subscribe!(session_id, "session", "closed")
SessionPubSub.subscribe!(session_id, "devices", "connected")
SessionPubSub.subscribe!(session_id, "devices", "disconnected")
SessionPubSub.subscribe!(session_id, "devices", "empty")
end
##############
# Hub events #
##############
def handle_info(%{topic: "session:closed:" <> _session_id}, socket) do
socket = socket |> put_flash(:info, "The host terminated the session") |> redirect(to: "/")
{:noreply, socket}
end
def handle_info(%{topic: "devices:empty:" <> _session_id}, socket) do
{:noreply, assign(socket, :devices, %{})}
end
def handle_info(%{topic: "devices:connected:" <> _session_id}, socket) do
devices = SessionServer.get_devices(socket.assigns.session_server_pid)
{:noreply, assign(socket, :devices, devices)}
end
def handle_info(%{topic: "devices:disconnected:" <> _session_id}, socket) do
devices = SessionServer.get_devices(socket.assigns.session_server_pid)
{:noreply, assign(socket, :devices, devices)}
end
##############
# UI events #
##############
def handle_event(
"vibrate_device:" <> index,
%{"vibration" => vibration},
socket
) do
vibration = String.to_integer(vibration)
index = String.to_integer(index)
:ok = SessionServer.vibrate_device(socket.assigns.session_server_pid, index, vibration)
devices = SessionServer.get_devices(socket.assigns.session_server_pid)
{:noreply, assign(socket, :devices, devices)}
end
end

View file

@ -0,0 +1,30 @@
<header class="mb-10">
<h1 class="text-5xl mb-4 font-bold text-indigo-500">
IntiSync Controller
</h1>
<p class="text-2xl mt-2 text-zinc-600 font-semibold">
You are controlling a remote device, be careful!
</p>
</header>
<section>
<h2 class="text-3xl mb-4 font-bold text-indigo-500">
Connected devices
</h2>
<p :if={Enum.empty?(@devices)} class="text-lg text-amber-500 font-semibold">
<.icon name="hero-signal-slash" class="mr-1.5" /> No devices connected yet
</p>
<div class="flex flex-col gap-4 mt-2">
<%= for {index, device} <- @devices do %>
<form phx-change={"vibrate_device:#{index}"}>
<IntisyncWeb.DeviceCardComponent.view
id={"device-#{index}"}
device={device}
disabled={false}
/>
</form>
<% end %>
</div>
</section>

View file

@ -18,6 +18,7 @@ defmodule IntisyncWeb.Router do
pipe_through :browser
live "/", LobbyLive
live "/sessions/:id/remote", RemoteLive
live_session :hub, root_layout: {IntisyncWeb.Layouts, :hub_root} do
live "/sessions/:id", HubLive

View file

@ -0,0 +1,117 @@
defmodule IntisyncWeb.RemoteLiveTest do
use IntisyncWeb.ConnCase
import Phoenix.LiveViewTest
defp generate_session_id(_) do
%{session_id: Intisync.Puid.generate()}
end
setup [:generate_session_id]
test "A remote can join a fresh new session", %{conn: conn, session_id: session_id} do
{:ok, _hub_view, _html} = live(conn, ~p"/sessions/#{session_id}")
{:ok, _view, _html} = live(conn, ~p"/sessions/#{session_id}/remote")
end
test "A remote can't join a session that already has a controller", %{
conn: conn,
session_id: session_id
} do
{:ok, _hub_view, _html} = live(conn, ~p"/sessions/#{session_id}")
{:ok, _view, _html} = live(conn, ~p"/sessions/#{session_id}/remote")
assert {:error, {:redirect, %{to: "/"}}} = live(conn, ~p"/sessions/#{session_id}/remote")
end
test "A remote can't join a non existing session", %{conn: conn, session_id: session_id} do
assert {:error, {:redirect, %{to: "/"}}} = live(conn, ~p"/sessions/#{session_id}/remote")
end
test "Renders a device card when a device connected event is received", %{
conn: conn,
session_id: session_id
} do
{:ok, hub_view, _html} = live(conn, ~p"/sessions/#{session_id}")
{:ok, view, _html} = live(conn, ~p"/sessions/#{session_id}/remote")
render_click(hub_view, "connected", %{})
render_click(hub_view, "device_connected", %{index: 0, name: "My dummy device"})
assert view |> element("#device-0") |> render() =~ "My dummy device"
end
test "Removes a device card when a device disconnected event is received", %{
conn: conn,
session_id: session_id
} do
{:ok, hub_view, _html} = live(conn, ~p"/sessions/#{session_id}")
{:ok, view, _html} = live(conn, ~p"/sessions/#{session_id}/remote")
render_click(hub_view, "connected", %{})
render_click(hub_view, "device_connected", %{index: 0, name: "My dummy device"})
assert view |> element("#device-0") |> has_element?()
render_click(hub_view, "device_disconnected", %{index: 0})
refute view |> element("#device-0") |> has_element?()
end
test "Updates device vibration when sliding the device vibration bar", %{
conn: conn,
session_id: session_id
} do
{:ok, hub_view, _html} = live(conn, ~p"/sessions/#{session_id}")
{:ok, view, _html} = live(conn, ~p"/sessions/#{session_id}/remote")
render_click(hub_view, "connected", %{})
render_click(hub_view, "device_connected", %{index: 0, name: "My dummy device"})
assert view |> element("#device-0") |> has_element?()
render_change(view, "vibrate_device:0", %{"vibration" => "45"})
assert view |> element("#device-0") |> render() =~ "value=\"45\""
end
test "When a devices empty event is received, all devices are removed from the view", %{
conn: conn,
session_id: session_id
} do
{:ok, hub_view, _html} = live(conn, ~p"/sessions/#{session_id}")
{:ok, view, _html} = live(conn, ~p"/sessions/#{session_id}/remote")
render_click(hub_view, "connected", %{})
render_click(hub_view, "device_connected", %{index: 0, name: "My dummy device"})
render_click(hub_view, "device_connected", %{index: 1, name: "My dummy device"})
assert view |> element("#device-0") |> has_element?()
assert view |> element("#device-1") |> has_element?()
render_click(hub_view, "disconnected", %{})
refute view |> element("#device-0") |> has_element?()
refute view |> element("#device-1") |> has_element?()
end
test "Shows all the connected devices when joining a session with already connected devices", %{
conn: conn,
session_id: session_id
} do
{:ok, hub_view, _html} = live(conn, ~p"/sessions/#{session_id}")
render_click(hub_view, "connected", %{})
render_click(hub_view, "device_connected", %{index: 0, name: "My dummy device"})
render_click(hub_view, "device_connected", %{index: 1, name: "My dummy device"})
{:ok, view, _html} = live(conn, ~p"/sessions/#{session_id}/remote")
assert view |> element("#device-0") |> has_element?()
assert view |> element("#device-1") |> has_element?()
end
end