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