Finished basic onboarding flow; added tests

This commit is contained in:
Kieran Eglin 2024-02-20 09:26:38 -08:00
parent 2de585aae6
commit 329c2e5530
No known key found for this signature in database
GPG key ID: 193984967FCF432D
17 changed files with 183 additions and 46 deletions

View file

@ -100,7 +100,6 @@ defmodule Pinchflat.Media do
field -> List.wrap(mapped_struct[field]) field -> List.wrap(mapped_struct[field])
end) end)
|> List.flatten() |> List.flatten()
# TODO: test
|> Enum.filter(&is_binary/1) |> Enum.filter(&is_binary/1)
end end

View file

@ -11,11 +11,6 @@ defmodule Pinchflat.MediaSource do
alias Pinchflat.MediaSource.Source alias Pinchflat.MediaSource.Source
alias Pinchflat.MediaClient.SourceDetails alias Pinchflat.MediaClient.SourceDetails
# TODO: test
def sources_exist? do
Repo.exists?(Source)
end
@doc """ @doc """
Returns the list of sources. Returns [%Source{}, ...] Returns the list of sources. Returns [%Source{}, ...]
""" """

View file

@ -8,11 +8,6 @@ defmodule Pinchflat.Profiles do
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile
# TODO: test
def media_profiles_exist? do
Repo.exists?(MediaProfile)
end
@doc """ @doc """
Returns the list of media_profiles. Returns [%MediaProfile{}, ...] Returns the list of media_profiles. Returns [%MediaProfile{}, ...]
""" """

View file

@ -7,7 +7,7 @@
> >
<.icon name="hero-bars-3" /> <.icon name="hero-bars-3" />
</button> </button>
<a class="hidden sm:block flex-shrink-0 lg:hidden" href="#"> <a class="hidden sm:block flex-shrink-0 lg:hidden" href="/">
<h2 class="text-title-md2 font-bold text-white">Pinchflat</h2> <h2 class="text-title-md2 font-bold text-white">Pinchflat</h2>
</a> </a>
</div> </div>
@ -22,7 +22,7 @@
name="q" name="q"
value={@params["q"]} value={@params["q"]}
placeholder="Type to search..." placeholder="Type to search..."
class="w-full bg-transparent pl-9 pr-4 border-0 focus:ring-0 focus:outline-none xl:w-125" class="w-full bg-transparent pl-9 pr-4 border-0 focus:ring-0 focus:outline-none lg:w-125"
/> />
</form> </form>
</div> </div>

View file

@ -4,7 +4,9 @@
@click.outside="sidebarToggle = false" @click.outside="sidebarToggle = false"
> >
<div class="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5"> <div class="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
<a href="/">
<h2 class="text-title-md2 font-bold text-white">Pinchflat</h2> <h2 class="text-title-md2 font-bold text-white">Pinchflat</h2>
</a>
<button class="block lg:hidden" @click.stop="sidebarToggle = !sidebarToggle"> <button class="block lg:hidden" @click.stop="sidebarToggle = !sidebarToggle">
<.icon name="hero-arrow-left" class="fill-current" /> <.icon name="hero-arrow-left" class="fill-current" />
@ -15,6 +17,8 @@
<div> <div>
<h3 class="mb-4 ml-4 text-sm font-medium text-bodydark2">MENU</h3> <h3 class="mb-4 ml-4 text-sm font-medium text-bodydark2">MENU</h3>
<ul class="mb-6 flex flex-col gap-1.5"> <ul class="mb-6 flex flex-col gap-1.5">
<.sidebar_item icon="hero-home" text="Home" navigate={~p"/"} />
<.sidebar_item icon="hero-tv" text="Sources" navigate={~p"/sources"} /> <.sidebar_item icon="hero-tv" text="Sources" navigate={~p"/sources"} />
<.sidebar_item icon="hero-adjustments-vertical" text="Media Profiles" navigate={~p"/media_profiles"} /> <.sidebar_item icon="hero-adjustments-vertical" text="Media Profiles" navigate={~p"/media_profiles"} />

View file

@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} /> <meta name="csrf-token" content={get_csrf_token()} />
<.live_title suffix=" · Phoenix Framework"> <.live_title>
<%= assigns[:page_title] || "Pinchflat" %> <%= assigns[:page_title] || "Pinchflat" %>
</.live_title> </.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} /> <link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />

View file

@ -13,9 +13,9 @@ defmodule PinchflatWeb.MediaProfiles.MediaProfileController do
changeset = Profiles.change_media_profile(%MediaProfile{}) changeset = Profiles.change_media_profile(%MediaProfile{})
if get_session(conn, :onboarding) do if get_session(conn, :onboarding) do
render(conn, :new, changeset: changeset, onboarding: true, layout: {Layouts, :onboarding}) render(conn, :new, changeset: changeset, layout: {Layouts, :onboarding})
else else
render(conn, :new, changeset: changeset, onboarding: false) render(conn, :new, changeset: changeset)
end end
end end
@ -23,16 +23,20 @@ defmodule PinchflatWeb.MediaProfiles.MediaProfileController do
case Profiles.create_media_profile(media_profile_params) do case Profiles.create_media_profile(media_profile_params) do
{:ok, media_profile} -> {:ok, media_profile} ->
redirect_location = redirect_location =
if get_session(conn, :onboarding), do: ~p"/", else: ~p"/media_profiles/#{media_profile}" if get_session(conn, :onboarding), do: ~p"/?onboarding=1", else: ~p"/media_profiles/#{media_profile}"
conn conn
|> put_flash(:info, "Media profile created successfully.") |> put_flash(:info, "Media profile created successfully.")
|> redirect(to: redirect_location) |> redirect(to: redirect_location)
{:error, %Ecto.Changeset{} = changeset} -> {:error, %Ecto.Changeset{} = changeset} ->
if get_session(conn, :onboarding) do
render(conn, :new, changeset: changeset, layout: {Layouts, :onboarding})
else
render(conn, :new, changeset: changeset) render(conn, :new, changeset: changeset)
end end
end end
end
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
media_profile = Profiles.get_media_profile!(id) media_profile = Profiles.get_media_profile!(id)

View file

@ -1,5 +1,5 @@
<div class="mb-6 flex gap-3 flex-row items-center"> <div class="mb-6 flex gap-3 flex-row items-center">
<.link :if={not @onboarding} navigate={~p"/media_profiles"}> <.link :if={!Plug.Conn.get_session(@conn, :onboarding)} navigate={~p"/media_profiles"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" /> <.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link> </.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Media Profile</h2> <h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Media Profile</h2>

View file

@ -20,11 +20,10 @@ defmodule PinchflatWeb.MediaSources.SourceController do
render(conn, :new, render(conn, :new,
changeset: changeset, changeset: changeset,
media_profiles: media_profiles(), media_profiles: media_profiles(),
onboarding: true,
layout: {Layouts, :onboarding} layout: {Layouts, :onboarding}
) )
else else
render(conn, :new, changeset: changeset, media_profiles: media_profiles(), onboarding: false) render(conn, :new, changeset: changeset, media_profiles: media_profiles())
end end
end end
@ -32,16 +31,24 @@ defmodule PinchflatWeb.MediaSources.SourceController do
case MediaSource.create_source(source_params) do case MediaSource.create_source(source_params) do
{:ok, source} -> {:ok, source} ->
redirect_location = redirect_location =
if get_session(conn, :onboarding), do: ~p"/", else: ~p"/sources/#{source}" if get_session(conn, :onboarding), do: ~p"/?onboarding=1", else: ~p"/sources/#{source}"
conn conn
|> put_flash(:info, "Source created successfully.") |> put_flash(:info, "Source created successfully.")
|> redirect(to: redirect_location) |> redirect(to: redirect_location)
{:error, %Ecto.Changeset{} = changeset} -> {:error, %Ecto.Changeset{} = changeset} ->
if get_session(conn, :onboarding) do
render(conn, :new,
changeset: changeset,
media_profiles: media_profiles(),
layout: {Layouts, :onboarding}
)
else
render(conn, :new, changeset: changeset, media_profiles: media_profiles()) render(conn, :new, changeset: changeset, media_profiles: media_profiles())
end end
end end
end
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
source = source =

View file

@ -1,5 +1,5 @@
<div class="mb-6 flex gap-3 flex-row items-center"> <div class="mb-6 flex gap-3 flex-row items-center">
<.link :if={not @onboarding} navigate={~p"/sources"}> <.link :if={!Plug.Conn.get_session(@conn, :onboarding)} navigate={~p"/sources"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" /> <.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link> </.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Source</h2> <h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Source</h2>

View file

@ -1,18 +1,29 @@
defmodule PinchflatWeb.Pages.PageController do defmodule PinchflatWeb.Pages.PageController do
use PinchflatWeb, :controller use PinchflatWeb, :controller
alias Pinchflat.Profiles alias Pinchflat.Repo
alias Pinchflat.MediaSource alias Pinchflat.MediaSource.Source
alias Pinchflat.Profiles.MediaProfile
def home(conn, _params) do def home(conn, params) do
media_profiles_exist = Profiles.media_profiles_exist?() force_onboarding = params["onboarding"]
sources_exist = MediaSource.sources_exist?() media_profiles_exist = Repo.exists?(MediaProfile)
sources_exist = Repo.exists?(Source)
if media_profiles_exist && sources_exist do if !force_onboarding && media_profiles_exist && sources_exist do
render_home_page(conn)
else
render_onboarding_page(conn, media_profiles_exist, sources_exist)
end
end
defp render_home_page(conn) do
conn conn
|> put_session(:onboarding, false) |> put_session(:onboarding, false)
|> render(:home) |> render(:home)
else end
defp render_onboarding_page(conn, media_profiles_exist, sources_exist) do
conn conn
|> put_session(:onboarding, true) |> put_session(:onboarding, true)
|> render(:onboarding_checklist, |> render(:onboarding_checklist,
@ -21,5 +32,4 @@ defmodule PinchflatWeb.Pages.PageController do
layout: {Layouts, :onboarding} layout: {Layouts, :onboarding}
) )
end end
end
end end

View file

@ -1 +1,8 @@
<h1>hey</h1> <div class="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div class="max-w-full overflow-x-auto">
<div class="flex flex-col gap-10 text-center my-10">
<h2 class="text-2xl">TODO: Put some useful info or analytics here</h2>
<p>For now, the options in the sidebar have what you need</p>
</div>
</div>
</div>

View file

@ -245,6 +245,18 @@ defmodule Pinchflat.MediaTest do
"video/test.srt" "video/test.srt"
] ]
end end
test "strips out nil values" do
filepaths = %{
media_filepath: "/video/test.mp4",
thumbnail_filepath: nil,
subtitle_filepaths: [["en", nil]]
}
media_item = media_item_fixture(filepaths)
assert Media.media_filepaths(media_item) == ["/video/test.mp4"]
end
end end
describe "create_media_item/1" do describe "create_media_item/1" do

View file

@ -22,6 +22,15 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
conn = get(conn, ~p"/media_profiles/new") conn = get(conn, ~p"/media_profiles/new")
assert html_response(conn, 200) =~ "New Media Profile" assert html_response(conn, 200) =~ "New Media Profile"
end end
test "renders correct layout when onboarding", %{session_conn: session_conn} do
session_conn =
session_conn
|> put_session(:onboarding, true)
|> get(~p"/media_profiles/new")
refute html_response(session_conn, 200) =~ "MENU"
end
end end
describe "create media_profile" do describe "create media_profile" do
@ -39,6 +48,24 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
conn = post(conn, ~p"/media_profiles", media_profile: @invalid_attrs) conn = post(conn, ~p"/media_profiles", media_profile: @invalid_attrs)
assert html_response(conn, 200) =~ "New Media Profile" assert html_response(conn, 200) =~ "New Media Profile"
end end
test "redirects to onboarding when onboarding", %{session_conn: session_conn} do
session_conn =
session_conn
|> put_session(:onboarding, true)
|> post(~p"/media_profiles", media_profile: @create_attrs)
assert redirected_to(session_conn) == ~p"/?onboarding=1"
end
test "renders correct layout on error when onboarding", %{session_conn: session_conn} do
session_conn =
session_conn
|> put_session(:onboarding, true)
|> post(~p"/media_profiles", media_profile: @invalid_attrs)
refute html_response(session_conn, 200) =~ "MENU"
end
end end
describe "edit media_profile" do describe "edit media_profile" do

View file

@ -1,8 +1,52 @@
defmodule PinchflatWeb.PageControllerTest do defmodule PinchflatWeb.PageControllerTest do
use PinchflatWeb.ConnCase use PinchflatWeb.ConnCase
test "GET /", %{conn: conn} do import Pinchflat.ProfilesFixtures
import Pinchflat.MediaSourceFixtures
describe "GET / when testing onboarding" do
test "sets the onboarding session to true when onboarding", %{conn: conn} do
conn = get(conn, ~p"/") conn = get(conn, ~p"/")
assert html_response(conn, 200) =~ "Peace of mind from prototype to production" assert get_session(conn, :onboarding)
end
test "displays the onboarding page when no media profiles exist", %{conn: conn} do
conn = get(conn, ~p"/")
assert html_response(conn, 200) =~ "Welcome to Pinchflat"
end
test "displays the onboarding page when no sources exist", %{conn: conn} do
_ = media_profile_fixture()
conn = get(conn, ~p"/")
assert html_response(conn, 200) =~ "Welcome to Pinchflat"
end
test "displays the onboarding page when onboarding is forced", %{conn: conn} do
_ = media_profile_fixture()
_ = source_fixture()
conn = get(conn, ~p"/?onboarding=1")
assert html_response(conn, 200) =~ "Welcome to Pinchflat"
end
test "sets the onboarding session to false when not onboarding", %{conn: conn} do
conn = get(conn, ~p"/")
assert get_session(conn, :onboarding)
_ = media_profile_fixture()
_ = source_fixture()
conn = get(conn, ~p"/")
refute get_session(conn, :onboarding)
end
test "displays the home page when not onboarding", %{conn: conn} do
_ = media_profile_fixture()
_ = source_fixture()
conn = get(conn, ~p"/")
assert html_response(conn, 200) =~ "MENU"
end
end end
end end

View file

@ -38,6 +38,15 @@ defmodule PinchflatWeb.SourceControllerTest do
conn = get(conn, ~p"/sources/new") conn = get(conn, ~p"/sources/new")
assert html_response(conn, 200) =~ "New Source" assert html_response(conn, 200) =~ "New Source"
end end
test "renders correct layout when onboarding", %{session_conn: session_conn} do
session_conn =
session_conn
|> put_session(:onboarding, true)
|> get(~p"/sources/new")
refute html_response(session_conn, 200) =~ "MENU"
end
end end
describe "create source" do describe "create source" do
@ -56,6 +65,26 @@ defmodule PinchflatWeb.SourceControllerTest do
conn = post(conn, ~p"/sources", source: invalid_attrs) conn = post(conn, ~p"/sources", source: invalid_attrs)
assert html_response(conn, 200) =~ "New Source" assert html_response(conn, 200) =~ "New Source"
end end
test "redirects to onboarding when onboarding", %{session_conn: session_conn, create_attrs: create_attrs} do
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
session_conn =
session_conn
|> put_session(:onboarding, true)
|> post(~p"/sources", source: create_attrs)
assert redirected_to(session_conn) == ~p"/?onboarding=1"
end
test "renders correct layout on error when onboarding", %{session_conn: session_conn, invalid_attrs: invalid_attrs} do
session_conn =
session_conn
|> put_session(:onboarding, true)
|> post(~p"/sources", source: invalid_attrs)
refute html_response(session_conn, 200) =~ "MENU"
end
end end
describe "edit source" do describe "edit source" do

View file

@ -34,6 +34,10 @@ defmodule PinchflatWeb.ConnCase do
setup tags do setup tags do
Pinchflat.DataCase.setup_sandbox(tags) Pinchflat.DataCase.setup_sandbox(tags)
{:ok, conn: Phoenix.ConnTest.build_conn()}
conn = Phoenix.ConnTest.build_conn()
session_conn = Plug.Test.init_test_session(conn, %{})
{:ok, conn: conn, session_conn: session_conn}
end end
end end