Julian Noble
2 years ago
12 changed files with 652 additions and 0 deletions
@ -0,0 +1,116 @@ |
|||||||
|
defmodule Chirp.Timeline do |
||||||
|
@moduledoc """ |
||||||
|
The Timeline context. |
||||||
|
""" |
||||||
|
|
||||||
|
import Ecto.Query, warn: false |
||||||
|
alias Chirp.Repo |
||||||
|
|
||||||
|
alias Chirp.Timeline.Post |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Returns the list of posts. |
||||||
|
|
||||||
|
## Examples |
||||||
|
|
||||||
|
iex> list_posts() |
||||||
|
[%Post{}, ...] |
||||||
|
|
||||||
|
""" |
||||||
|
def list_posts do |
||||||
|
Repo.all(from p in Post, order_by: [desc: p.id]) |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Gets a single post. |
||||||
|
|
||||||
|
Raises `Ecto.NoResultsError` if the Post does not exist. |
||||||
|
|
||||||
|
## Examples |
||||||
|
|
||||||
|
iex> get_post!(123) |
||||||
|
%Post{} |
||||||
|
|
||||||
|
iex> get_post!(456) |
||||||
|
** (Ecto.NoResultsError) |
||||||
|
|
||||||
|
""" |
||||||
|
def get_post!(id), do: Repo.get!(Post, id) |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Creates a post. |
||||||
|
|
||||||
|
## Examples |
||||||
|
|
||||||
|
iex> create_post(%{field: value}) |
||||||
|
{:ok, %Post{}} |
||||||
|
|
||||||
|
iex> create_post(%{field: bad_value}) |
||||||
|
{:error, %Ecto.Changeset{}} |
||||||
|
|
||||||
|
""" |
||||||
|
def create_post(attrs \\ %{}) do |
||||||
|
%Post{} |
||||||
|
|> Post.changeset(attrs) |
||||||
|
|> Repo.insert() |
||||||
|
|> broadcast(:post_created) |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Updates a post. |
||||||
|
|
||||||
|
## Examples |
||||||
|
|
||||||
|
iex> update_post(post, %{field: new_value}) |
||||||
|
{:ok, %Post{}} |
||||||
|
|
||||||
|
iex> update_post(post, %{field: bad_value}) |
||||||
|
{:error, %Ecto.Changeset{}} |
||||||
|
|
||||||
|
""" |
||||||
|
def update_post(%Post{} = post, attrs) do |
||||||
|
post |
||||||
|
|> Post.changeset(attrs) |
||||||
|
|> Repo.update() |
||||||
|
|> broadcast(:post_updated) |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Deletes a post. |
||||||
|
|
||||||
|
## Examples |
||||||
|
|
||||||
|
iex> delete_post(post) |
||||||
|
{:ok, %Post{}} |
||||||
|
|
||||||
|
iex> delete_post(post) |
||||||
|
{:error, %Ecto.Changeset{}} |
||||||
|
|
||||||
|
""" |
||||||
|
def delete_post(%Post{} = post) do |
||||||
|
Repo.delete(post) |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Returns an `%Ecto.Changeset{}` for tracking post changes. |
||||||
|
|
||||||
|
## Examples |
||||||
|
|
||||||
|
iex> change_post(post) |
||||||
|
%Ecto.Changeset{data: %Post{}} |
||||||
|
|
||||||
|
""" |
||||||
|
def change_post(%Post{} = post, attrs \\ %{}) do |
||||||
|
Post.changeset(post, attrs) |
||||||
|
end |
||||||
|
|
||||||
|
def subscribe do |
||||||
|
Phoenix.PubSub.subscribe(Chirp.PubSub,"posts") |
||||||
|
end |
||||||
|
|
||||||
|
defp broadcast({:error, _reason} = error, _event), do: error |
||||||
|
defp broadcast({:ok,post}, event) do |
||||||
|
Phoenix.PubSub.broadcast(Chirp.PubSub, "posts", {event,post}) |
||||||
|
{:ok, post} |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,22 @@ |
|||||||
|
defmodule Chirp.Timeline.Post do |
||||||
|
use Ecto.Schema |
||||||
|
import Ecto.Changeset |
||||||
|
|
||||||
|
schema "posts" do |
||||||
|
field :body, :string |
||||||
|
field :likes_count, :integer, default: 0 |
||||||
|
field :reposts_count, :integer, default: 0 |
||||||
|
field :username, :string, default: "julz" |
||||||
|
|
||||||
|
timestamps() |
||||||
|
end |
||||||
|
|
||||||
|
@doc false |
||||||
|
def changeset(post, attrs) do |
||||||
|
post |
||||||
|
|> cast(attrs, [:body]) |
||||||
|
|> validate_required([:body]) |
||||||
|
|> validate_length(:body, min: 2, max: 250) |
||||||
|
|
||||||
|
end |
||||||
|
end |
@ -0,0 +1,81 @@ |
|||||||
|
defmodule ChirpWeb.PostLive.FormComponent do |
||||||
|
use ChirpWeb, :live_component |
||||||
|
|
||||||
|
alias Chirp.Timeline |
||||||
|
|
||||||
|
@impl true |
||||||
|
def render(assigns) do |
||||||
|
~H""" |
||||||
|
<div> |
||||||
|
<.header> |
||||||
|
<%= @title %> |
||||||
|
<:subtitle>Use this form to manage post records in your database.</:subtitle> |
||||||
|
</.header> |
||||||
|
|
||||||
|
<.simple_form |
||||||
|
:let={f} |
||||||
|
for={@changeset} |
||||||
|
id="post-form" |
||||||
|
phx-target={@myself} |
||||||
|
phx-change="validate" |
||||||
|
phx-submit="save" |
||||||
|
> |
||||||
|
<.input field={{f, :body}} type="textarea" label="body" /> |
||||||
|
<:actions> |
||||||
|
<.button phx-disable-with="Saving...">Save Post</.button> |
||||||
|
</:actions> |
||||||
|
</.simple_form> |
||||||
|
</div> |
||||||
|
""" |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def update(%{post: post} = assigns, socket) do |
||||||
|
changeset = Timeline.change_post(post) |
||||||
|
|
||||||
|
{:ok, |
||||||
|
socket |
||||||
|
|> assign(assigns) |
||||||
|
|> assign(:changeset, changeset)} |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def handle_event("validate", %{"post" => post_params}, socket) do |
||||||
|
changeset = |
||||||
|
socket.assigns.post |
||||||
|
|> Timeline.change_post(post_params) |
||||||
|
|> Map.put(:action, :validate) |
||||||
|
|
||||||
|
{:noreply, assign(socket, :changeset, changeset)} |
||||||
|
end |
||||||
|
|
||||||
|
def handle_event("save", %{"post" => post_params}, socket) do |
||||||
|
save_post(socket, socket.assigns.action, post_params) |
||||||
|
end |
||||||
|
|
||||||
|
defp save_post(socket, :edit, post_params) do |
||||||
|
case Timeline.update_post(socket.assigns.post, post_params) do |
||||||
|
{:ok, _post} -> |
||||||
|
{:noreply, |
||||||
|
socket |
||||||
|
|> put_flash(:info, "Post updated successfully") |
||||||
|
|> push_navigate(to: socket.assigns.navigate)} |
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{} = changeset} -> |
||||||
|
{:noreply, assign(socket, :changeset, changeset)} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp save_post(socket, :new, post_params) do |
||||||
|
case Timeline.create_post(post_params) do |
||||||
|
{:ok, _post} -> |
||||||
|
{:noreply, |
||||||
|
socket |
||||||
|
|> put_flash(:info, "Post created successfully") |
||||||
|
|> push_navigate(to: socket.assigns.navigate)} |
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{} = changeset} -> |
||||||
|
{:noreply, assign(socket, changeset: changeset)} |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,61 @@ |
|||||||
|
defmodule ChirpWeb.PostLive.Index do |
||||||
|
use ChirpWeb, :live_view |
||||||
|
|
||||||
|
alias Chirp.Timeline |
||||||
|
alias Chirp.Timeline.Post |
||||||
|
|
||||||
|
@impl true |
||||||
|
def mount(_params, _session, socket) do |
||||||
|
if connected?(socket), do: Timeline.subscribe() |
||||||
|
{:ok, assign(socket, :posts, list_posts()), temporary_assigns: [posts: []]} |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def handle_params(params, _url, socket) do |
||||||
|
{:noreply, apply_action(socket, socket.assigns.live_action, params)} |
||||||
|
end |
||||||
|
|
||||||
|
defp apply_action(socket, :edit, %{"id" => id}) do |
||||||
|
socket |
||||||
|
|> assign(:page_title, "Edit Post") |
||||||
|
|> assign(:post, Timeline.get_post!(id)) |
||||||
|
end |
||||||
|
|
||||||
|
defp apply_action(socket, :new, _params) do |
||||||
|
socket |
||||||
|
|> assign(:page_title, "New Post") |
||||||
|
|> assign(:post, %Post{}) |
||||||
|
end |
||||||
|
|
||||||
|
defp apply_action(socket, :index, _params) do |
||||||
|
socket |
||||||
|
|> assign(:page_title, "Listing Posts") |
||||||
|
|> assign(:post, nil) |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def handle_event("delete", %{"id" => id}, socket) do |
||||||
|
post = Timeline.get_post!(id) |
||||||
|
{:ok, _} = Timeline.delete_post(post) |
||||||
|
|
||||||
|
{:noreply, assign(socket, :posts, list_posts())} |
||||||
|
end |
||||||
|
def handle_event("like", %{"id" => _id}, socket) do |
||||||
|
{:noreply,socket} |
||||||
|
end |
||||||
|
def handle_event("repost", %{"id" => _id}, socket) do |
||||||
|
{:noreply,socket} |
||||||
|
end |
||||||
|
|
||||||
|
def handle_info({:post_created,post}, socket) do |
||||||
|
{:noreply, update(socket, :posts, fn posts-> [post|posts] end)} |
||||||
|
end |
||||||
|
|
||||||
|
def handle_info({:post_updated,post}, socket) do |
||||||
|
{:noreply, update(socket, :posts, fn posts-> [post|posts] end)} |
||||||
|
end |
||||||
|
|
||||||
|
defp list_posts do |
||||||
|
Timeline.list_posts() |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,34 @@ |
|||||||
|
<.header> |
||||||
|
Timeline |
||||||
|
<:actions> |
||||||
|
<.link patch={~p"/posts/new"}> |
||||||
|
<.button>New Post</.button> |
||||||
|
</.link> |
||||||
|
</:actions> |
||||||
|
</.header> |
||||||
|
|
||||||
|
<!-- row_click={&JS.navigate(~p"/posts/#{&1}")} --> |
||||||
|
<div id="posts" phx-update="prepend"> |
||||||
|
<%= for post <- @posts do %> |
||||||
|
<.live_component |
||||||
|
module={ChirpWeb.PostLive.PostComponent} |
||||||
|
id={post.id} |
||||||
|
post={post} |
||||||
|
/> |
||||||
|
<% end %> |
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<%= if @live_action in [:new, :edit] do %> |
||||||
|
<.modal id="post-modal" show on_cancel={JS.navigate(~p"/posts")}> |
||||||
|
<.live_component |
||||||
|
module={ChirpWeb.PostLive.FormComponent} |
||||||
|
id={@post.id || :new} |
||||||
|
title={@page_title} |
||||||
|
action={@live_action} |
||||||
|
post={@post} |
||||||
|
navigate={~p"/posts"} |
||||||
|
/> |
||||||
|
</.modal> |
||||||
|
<% end %> |
@ -0,0 +1,78 @@ |
|||||||
|
defmodule ChirpWeb.PostLive.PostComponent do |
||||||
|
use ChirpWeb, :live_component |
||||||
|
|
||||||
|
@spec render(any) :: Phoenix.LiveView.Rendered.t() |
||||||
|
def render(assigns) do |
||||||
|
~H""" |
||||||
|
<div id={"post #{assigns.post.id}"} class="container mx-auto"> |
||||||
|
<div class="flex flex-row"> |
||||||
|
|
||||||
|
<div class="basis-1/4"> |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> |
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> |
||||||
|
</svg> |
||||||
|
<div label="Username"><%= @post.username %></div> |
||||||
|
|
||||||
|
</div> |
||||||
|
<div class="basis-3/4"> |
||||||
|
|
||||||
|
<div class="p-6 max-w-lg bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700"> |
||||||
|
<pre label="Body" class="mb-3 font-normal text-gray-500 dark:text-gray-400"><%= @post.body %></pre> |
||||||
|
<div class="grid grid-flow-col gap-4 auto-cols-max"> |
||||||
|
<div class="p-1"> |
||||||
|
<.link navigate={~p"/posts/#{@post}"} class="inline-flex items-center text-blue-600 hover:underline"> |
||||||
|
Show |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="p-1 w-6 h-6"> |
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" /> |
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> |
||||||
|
</svg> |
||||||
|
</.link> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="p-1 flex flex-row"> |
||||||
|
<.link phx-click="like" phx-value-id={@post.id}> |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> |
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" /> |
||||||
|
</svg> |
||||||
|
</.link> |
||||||
|
<span label="Likes count"> |
||||||
|
<%= @post.likes_count %> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="p-1 flex flex-row"> |
||||||
|
<.link phx-click="repost" phx-value-id={@post.id}> |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> |
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12c0-1.232-.046-2.453-.138-3.662a4.006 4.006 0 00-3.7-3.7 48.678 48.678 0 00-7.324 0 4.006 4.006 0 00-3.7 3.7c-.017.22-.032.441-.046.662M19.5 12l3-3m-3 3l-3-3m-12 3c0 1.232.046 2.453.138 3.662a4.006 4.006 0 003.7 3.7 48.656 48.656 0 007.324 0 4.006 4.006 0 003.7-3.7c.017-.22.032-.441.046-.662M4.5 12l3 3m-3-3l-3 3" /> |
||||||
|
</svg> |
||||||
|
</.link> |
||||||
|
<div label="Reposts countt"> |
||||||
|
<%= @post.reposts_count %> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="p-1"> |
||||||
|
<.link patch={~p"/posts/#{@post}/edit"}>Edit</.link> |
||||||
|
</div> |
||||||
|
<div class="p-1"> |
||||||
|
<.link phx-click={JS.push("delete", value: %{id: @post.id})} data-confirm="Are you sure?"> |
||||||
|
Delete |
||||||
|
</.link> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
""" |
||||||
|
end |
||||||
|
|
||||||
|
end |
@ -0,0 +1,21 @@ |
|||||||
|
defmodule ChirpWeb.PostLive.Show do |
||||||
|
use ChirpWeb, :live_view |
||||||
|
|
||||||
|
alias Chirp.Timeline |
||||||
|
|
||||||
|
@impl true |
||||||
|
def mount(_params, _session, socket) do |
||||||
|
{:ok, socket} |
||||||
|
end |
||||||
|
|
||||||
|
@impl true |
||||||
|
def handle_params(%{"id" => id}, _, socket) do |
||||||
|
{:noreply, |
||||||
|
socket |
||||||
|
|> assign(:page_title, page_title(socket.assigns.live_action)) |
||||||
|
|> assign(:post, Timeline.get_post!(id))} |
||||||
|
end |
||||||
|
|
||||||
|
defp page_title(:show), do: "Show Post" |
||||||
|
defp page_title(:edit), do: "Edit Post" |
||||||
|
end |
@ -0,0 +1,32 @@ |
|||||||
|
<.header> |
||||||
|
Post <%= @post.id %> |
||||||
|
<:subtitle>This is a post record from your database.</:subtitle> |
||||||
|
<:actions> |
||||||
|
<.link patch={~p"/posts/#{@post}/show/edit"} phx-click={JS.push_focus()}> |
||||||
|
<.button>Edit post</.button> |
||||||
|
</.link> |
||||||
|
</:actions> |
||||||
|
</.header> |
||||||
|
|
||||||
|
<.list> |
||||||
|
<:item title="Username"><%= @post.username %></:item> |
||||||
|
<:item title="Body"><%= @post.body %></:item> |
||||||
|
<:item title="Likes count"><%= @post.likes_count %></:item> |
||||||
|
<:item title="Reposts count"><%= @post.reposts_count %></:item> |
||||||
|
</.list> |
||||||
|
|
||||||
|
<.back navigate={~p"/posts"}>Back to posts</.back> |
||||||
|
|
||||||
|
<%= if @live_action in [:edit] do %> |
||||||
|
<.modal id="post-modal" show on_cancel={JS.patch(~p"/posts/#{@post}")}> |
||||||
|
<.live_component |
||||||
|
module={ChirpWeb.PostLive.FormComponent} |
||||||
|
id={@post.id} |
||||||
|
title={@page_title} |
||||||
|
action={@live_action} |
||||||
|
post={@post} |
||||||
|
navigate={~p"/posts/#{@post}"} |
||||||
|
/> |
||||||
|
</.modal> |
||||||
|
<% end %> |
||||||
|
|
@ -0,0 +1,65 @@ |
|||||||
|
defmodule Chirp.TimelineTest do |
||||||
|
use Chirp.DataCase |
||||||
|
|
||||||
|
alias Chirp.Timeline |
||||||
|
|
||||||
|
describe "posts" do |
||||||
|
alias Chirp.Timeline.Post |
||||||
|
|
||||||
|
import Chirp.TimelineFixtures |
||||||
|
|
||||||
|
@invalid_attrs %{body: nil, likes_count: nil, reposts_count: nil, username: nil} |
||||||
|
|
||||||
|
test "list_posts/0 returns all posts" do |
||||||
|
post = post_fixture() |
||||||
|
assert Timeline.list_posts() == [post] |
||||||
|
end |
||||||
|
|
||||||
|
test "get_post!/1 returns the post with given id" do |
||||||
|
post = post_fixture() |
||||||
|
assert Timeline.get_post!(post.id) == post |
||||||
|
end |
||||||
|
|
||||||
|
test "create_post/1 with valid data creates a post" do |
||||||
|
valid_attrs = %{body: "some body", likes_count: 42, reposts_count: 42, username: "some username"} |
||||||
|
|
||||||
|
assert {:ok, %Post{} = post} = Timeline.create_post(valid_attrs) |
||||||
|
assert post.body == "some body" |
||||||
|
assert post.likes_count == 42 |
||||||
|
assert post.reposts_count == 42 |
||||||
|
assert post.username == "some username" |
||||||
|
end |
||||||
|
|
||||||
|
test "create_post/1 with invalid data returns error changeset" do |
||||||
|
assert {:error, %Ecto.Changeset{}} = Timeline.create_post(@invalid_attrs) |
||||||
|
end |
||||||
|
|
||||||
|
test "update_post/2 with valid data updates the post" do |
||||||
|
post = post_fixture() |
||||||
|
update_attrs = %{body: "some updated body", likes_count: 43, reposts_count: 43, username: "some updated username"} |
||||||
|
|
||||||
|
assert {:ok, %Post{} = post} = Timeline.update_post(post, update_attrs) |
||||||
|
assert post.body == "some updated body" |
||||||
|
assert post.likes_count == 43 |
||||||
|
assert post.reposts_count == 43 |
||||||
|
assert post.username == "some updated username" |
||||||
|
end |
||||||
|
|
||||||
|
test "update_post/2 with invalid data returns error changeset" do |
||||||
|
post = post_fixture() |
||||||
|
assert {:error, %Ecto.Changeset{}} = Timeline.update_post(post, @invalid_attrs) |
||||||
|
assert post == Timeline.get_post!(post.id) |
||||||
|
end |
||||||
|
|
||||||
|
test "delete_post/1 deletes the post" do |
||||||
|
post = post_fixture() |
||||||
|
assert {:ok, %Post{}} = Timeline.delete_post(post) |
||||||
|
assert_raise Ecto.NoResultsError, fn -> Timeline.get_post!(post.id) end |
||||||
|
end |
||||||
|
|
||||||
|
test "change_post/1 returns a post changeset" do |
||||||
|
post = post_fixture() |
||||||
|
assert %Ecto.Changeset{} = Timeline.change_post(post) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,110 @@ |
|||||||
|
defmodule ChirpWeb.PostLiveTest do |
||||||
|
use ChirpWeb.ConnCase |
||||||
|
|
||||||
|
import Phoenix.LiveViewTest |
||||||
|
import Chirp.TimelineFixtures |
||||||
|
|
||||||
|
@create_attrs %{body: "some body", likes_count: 42, reposts_count: 42, username: "some username"} |
||||||
|
@update_attrs %{body: "some updated body", likes_count: 43, reposts_count: 43, username: "some updated username"} |
||||||
|
@invalid_attrs %{body: nil, likes_count: nil, reposts_count: nil, username: nil} |
||||||
|
|
||||||
|
defp create_post(_) do |
||||||
|
post = post_fixture() |
||||||
|
%{post: post} |
||||||
|
end |
||||||
|
|
||||||
|
describe "Index" do |
||||||
|
setup [:create_post] |
||||||
|
|
||||||
|
test "lists all posts", %{conn: conn, post: post} do |
||||||
|
{:ok, _index_live, html} = live(conn, ~p"/posts") |
||||||
|
|
||||||
|
assert html =~ "Listing Posts" |
||||||
|
assert html =~ post.body |
||||||
|
end |
||||||
|
|
||||||
|
test "saves new post", %{conn: conn} do |
||||||
|
{:ok, index_live, _html} = live(conn, ~p"/posts") |
||||||
|
|
||||||
|
assert index_live |> element("a", "New Post") |> render_click() =~ |
||||||
|
"New Post" |
||||||
|
|
||||||
|
assert_patch(index_live, ~p"/posts/new") |
||||||
|
|
||||||
|
assert index_live |
||||||
|
|> form("#post-form", post: @invalid_attrs) |
||||||
|
|> render_change() =~ "can't be blank" |
||||||
|
|
||||||
|
{:ok, _, html} = |
||||||
|
index_live |
||||||
|
|> form("#post-form", post: @create_attrs) |
||||||
|
|> render_submit() |
||||||
|
|> follow_redirect(conn, ~p"/posts") |
||||||
|
|
||||||
|
assert html =~ "Post created successfully" |
||||||
|
assert html =~ "some body" |
||||||
|
end |
||||||
|
|
||||||
|
test "updates post in listing", %{conn: conn, post: post} do |
||||||
|
{:ok, index_live, _html} = live(conn, ~p"/posts") |
||||||
|
|
||||||
|
assert index_live |> element("#posts-#{post.id} a", "Edit") |> render_click() =~ |
||||||
|
"Edit Post" |
||||||
|
|
||||||
|
assert_patch(index_live, ~p"/posts/#{post}/edit") |
||||||
|
|
||||||
|
assert index_live |
||||||
|
|> form("#post-form", post: @invalid_attrs) |
||||||
|
|> render_change() =~ "can't be blank" |
||||||
|
|
||||||
|
{:ok, _, html} = |
||||||
|
index_live |
||||||
|
|> form("#post-form", post: @update_attrs) |
||||||
|
|> render_submit() |
||||||
|
|> follow_redirect(conn, ~p"/posts") |
||||||
|
|
||||||
|
assert html =~ "Post updated successfully" |
||||||
|
assert html =~ "some updated body" |
||||||
|
end |
||||||
|
|
||||||
|
test "deletes post in listing", %{conn: conn, post: post} do |
||||||
|
{:ok, index_live, _html} = live(conn, ~p"/posts") |
||||||
|
|
||||||
|
assert index_live |> element("#posts-#{post.id} a", "Delete") |> render_click() |
||||||
|
refute has_element?(index_live, "#post-#{post.id}") |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "Show" do |
||||||
|
setup [:create_post] |
||||||
|
|
||||||
|
test "displays post", %{conn: conn, post: post} do |
||||||
|
{:ok, _show_live, html} = live(conn, ~p"/posts/#{post}") |
||||||
|
|
||||||
|
assert html =~ "Show Post" |
||||||
|
assert html =~ post.body |
||||||
|
end |
||||||
|
|
||||||
|
test "updates post within modal", %{conn: conn, post: post} do |
||||||
|
{:ok, show_live, _html} = live(conn, ~p"/posts/#{post}") |
||||||
|
|
||||||
|
assert show_live |> element("a", "Edit") |> render_click() =~ |
||||||
|
"Edit Post" |
||||||
|
|
||||||
|
assert_patch(show_live, ~p"/posts/#{post}/show/edit") |
||||||
|
|
||||||
|
assert show_live |
||||||
|
|> form("#post-form", post: @invalid_attrs) |
||||||
|
|> render_change() =~ "can't be blank" |
||||||
|
|
||||||
|
{:ok, _, html} = |
||||||
|
show_live |
||||||
|
|> form("#post-form", post: @update_attrs) |
||||||
|
|> render_submit() |
||||||
|
|> follow_redirect(conn, ~p"/posts/#{post}") |
||||||
|
|
||||||
|
assert html =~ "Post updated successfully" |
||||||
|
assert html =~ "some updated body" |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,23 @@ |
|||||||
|
defmodule Chirp.TimelineFixtures do |
||||||
|
@moduledoc """ |
||||||
|
This module defines test helpers for creating |
||||||
|
entities via the `Chirp.Timeline` context. |
||||||
|
""" |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Generate a post. |
||||||
|
""" |
||||||
|
def post_fixture(attrs \\ %{}) do |
||||||
|
{:ok, post} = |
||||||
|
attrs |
||||||
|
|> Enum.into(%{ |
||||||
|
body: "some body", |
||||||
|
likes_count: 42, |
||||||
|
reposts_count: 42, |
||||||
|
username: "some username" |
||||||
|
}) |
||||||
|
|> Chirp.Timeline.create_post() |
||||||
|
|
||||||
|
post |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue