diff --git a/lib/tilex/posts.ex b/lib/tilex/posts.ex index e1b80a17..e55e1479 100644 --- a/lib/tilex/posts.ex +++ b/lib/tilex/posts.ex @@ -44,6 +44,10 @@ defmodule Tilex.Posts do end end + def delete_post(post) do + Repo.delete(post) + end + def published(query \\ Post) do from(p in query, where: not is_nil(p.published_at) and p.published_at <= fragment("now()")) end diff --git a/lib/tilex_web/controllers/post_controller.ex b/lib/tilex_web/controllers/post_controller.ex index cfac74fb..8fd1d099 100644 --- a/lib/tilex_web/controllers/post_controller.ex +++ b/lib/tilex_web/controllers/post_controller.ex @@ -11,12 +11,12 @@ defmodule TilexWeb.PostController do alias Tilex.Posts plug(:load_channels when action in [:new, :create, :edit, :update]) - plug(:extract_slug when action in [:show, :edit, :update]) + plug(:extract_slug when action in [:show, :edit, :update, :delete]) plug( Guardian.Plug.EnsureAuthenticated, [error_handler: __MODULE__] - when action in ~w(new create edit update)a + when action in ~w(new create edit update delete)a ) @behaviour Guardian.Plug.ErrorHandler @@ -196,6 +196,30 @@ defmodule TilexWeb.PostController do end end + def delete(conn, _params) do + current_user = Guardian.Plug.current_resource(conn) + + post = + case current_user.admin do + false -> assoc(current_user, :posts) + true -> Post + end + |> Repo.get_by!(slug: conn.assigns.slug) + |> Repo.preload([:developer]) + + case Posts.delete_post(post) do + {:ok, _post} -> + conn + |> put_flash(:info, "Post deleted successfully") + |> redirect(to: Routes.developer_path(conn, :show, post.developer)) + + {:error, _changeset} -> + conn + |> put_flash(:error, "Failed to delete post") + |> redirect(to: Routes.post_path(conn, :show, post)) + end + end + defp load_channels(conn, _) do query = Channel diff --git a/lib/tilex_web/templates/shared/post.html.eex b/lib/tilex_web/templates/shared/post.html.eex index 0b058cfc..6d1f4c94 100644 --- a/lib/tilex_web/templates/shared/post.html.eex +++ b/lib/tilex_web/templates/shared/post.html.eex @@ -45,6 +45,9 @@
  • <%= link("edit", to: Routes.post_path(@conn, :edit, @post), class: "post__permalink") %>
  • +
  • + <%= link("delete", to: Routes.post_path(@conn, :delete, @post), method: :delete, class: "post__delete-link", data: [confirm: "Are you sure you want to delete this post?"]) %> +
  • <% end %>
  • <%= link to: "#", class: "js-like-action post__like-link", id: @post.slug do %> diff --git a/mix.exs b/mix.exs index 52333114..fd681b7f 100644 --- a/mix.exs +++ b/mix.exs @@ -67,7 +67,7 @@ defmodule Tilex.Mixfile do {:tzdata, "~> 1.1.0"}, {:ueberauth_google, "~> 0.5"}, {:usage_rules, "~> 0.1", only: [:dev]}, - {:wallaby, "~>0.30.1", [runtime: false, only: :test]} + {:wallaby, "~>0.30.1", runtime: false, only: :test} ] end diff --git a/mix.lock b/mix.lock index 3fc31787..58a90315 100644 --- a/mix.lock +++ b/mix.lock @@ -14,12 +14,12 @@ "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.16.0", "54592074ebbbb92ee4746c8a8846e5605052f29309d3a873468d76cdf932076f", [:make, :rebar3], [], "hexpm", "7f478d80d66b747344f0ea7708c187645cfcc08b11aa424632f78e25bf05db51"}, "credo": {:hex, :credo, "1.7.13", "126a0697df6b7b71cd18c81bc92335297839a806b6f62b61d417500d1070ff4e", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "47641e6d2bbff1e241e87695b29f617f1a8f912adea34296fb10ecc3d7e9e84f"}, - "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, + "db_connection": {:hex, :db_connection, "2.8.1", "9abdc1e68c34c6163f6fb96a96532272d13ad7ca45262156ae8b7ec6d9dc4bec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a61a3d489b239d76f326e03b98794fb8e45168396c925ef25feb405ed09da8fd"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"}, "earmark": {:hex, :earmark, "1.4.48", "5f41e579d85ef812351211842b6e005f6e0cef111216dea7d4b9d58af4608434", [:mix], [], "hexpm", "a461a0ddfdc5432381c876af1c86c411fd78a25790c75023c7a4c035fdc858f9"}, "ecto": {:hex, :ecto, "3.13.4", "27834b45d58075d4a414833d9581e8b7bb18a8d9f264a21e42f653d500dbeeb5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ad7d1505685dfa7aaf86b133d54f5ad6c42df0b4553741a1ff48796736e88b2"}, - "ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"}, + "ecto_sql": {:hex, :ecto_sql, "3.13.2", "a07d2461d84107b3d037097c822ffdd36ed69d1cf7c0f70e12a3d1decf04e2e1", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "539274ab0ecf1a0078a6a72ef3465629e4d6018a3028095dc90f60a19c371717"}, "esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"}, "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, @@ -32,7 +32,7 @@ "hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"}, "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.4.3", "67b3d9fa8691b727317e0cc96b9b3093be00ee45419ffb221cdeee88e75d1360", [:mix], [{:mochiweb, "~> 2.15 or ~> 3.1", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm", "87748d3c4afe949c7c6eb7150c958c2bcba43fc5b2a02686af30e636b74bccb7"}, - "httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"}, + "httpoison": {:hex, :httpoison, "2.2.3", "a599d4b34004cc60678999445da53b5e653630651d4da3d14675fedc9dd34bd6", [:mix], [{:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "fa0f2e3646d3762fdc73edb532104c8619c7636a6997d20af4003da6cfc53e53"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "igniter": {:hex, :igniter, "0.6.30", "83a466369ebb8fe009e0823c7bf04314dc545122c2d48f896172fc79df33e99d", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "76a14d5b7f850bb03b5243088c3649d54a2e52e34a2aa1104dee23cf50a8bae0"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, @@ -52,7 +52,7 @@ "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "peri": {:hex, :peri, "0.6.0", "0758aa037f862f7a3aa0823cb82195916f61a8071f6eaabcff02103558e61a70", [:mix], [{:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:stream_data, "~> 1.1", [hex: :stream_data, repo: "hexpm", optional: true]}], "hexpm", "b27f118f3317fbc357c4a04b3f3c98561efdd8865edd4ec0e24fd936c7ff36c8"}, "phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.2", "3b83b24ab5a2eb071a20372f740d7118767c272db386831b2e77638c4dcc606d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "3f94d025f59de86be00f5f8c5dd7b5965a3298458d21ab1c328488be3b5fcd59"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.5", "c4ef322acd15a574a8b1a08eff0ee0a85e73096b53ce1403b6563709f15e1cea", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "26ec3208eef407f31b748cadd044045c6fd485fbff168e35963d2f9dfff28d4b"}, "phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.4", "4508e481f791ce62ec6a096e13b061387158cbeefacca68c6c1928e1305e23ed", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "2984aae96994fbc5c61795a73b8fb58153b41ff934019cfb522343d2d3817d59"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.1", "05df733a09887a005ed0d69a7fc619d376aea2730bf64ce52ac51ce716cc1ef0", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "74273843d5a6e4fef0bbc17599f33e3ec63f08e69215623a0cd91eea4288e5a0"}, @@ -63,7 +63,7 @@ "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"}, "plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"}, "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, - "postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"}, + "postgrex": {:hex, :postgrex, "0.21.1", "2c5cc830ec11e7a0067dd4d623c049b3ef807e9507a424985b8dcf921224cd88", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "27d8d21c103c3cc68851b533ff99eef353e6a0ff98dc444ea751de43eb48bdac"}, "ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"}, "req": {:hex, :req, "0.5.15", "662020efb6ea60b9f0e0fac9be88cd7558b53fe51155a2d9899de594f9906ba9", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "a6513a35fad65467893ced9785457e91693352c70b58bbc045b47e5eb2ef0c53"}, "rewrite": {:hex, :rewrite, "1.2.0", "80220eb14010e175b67c939397e1a8cdaa2c32db6e2e0a9d5e23e45c0414ce21", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "a1cd702bbb9d51613ab21091f04a386d750fc6f4516b81900df082d78b2d8c50"}, @@ -75,7 +75,7 @@ "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"}, "telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"}, - "tesla": {:hex, :tesla, "1.12.1", "fe2bf4250868ee72e5d8b8dfa408d13a00747c41b7237b6aa3b9a24057346681", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2391efc6243d37ead43afd0327b520314c7b38232091d4a440c1212626fdd6e7"}, + "tesla": {:hex, :tesla, "1.15.3", "3a2b5c37f09629b8dcf5d028fbafc9143c0099753559d7fe567eaabfbd9b8663", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.21", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "98bb3d4558abc67b92fb7be4cd31bb57ca8d80792de26870d362974b58caeda7"}, "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"}, "tidewave": {:hex, :tidewave, "0.5.0", "8f278d7eb2d0af36ae6d4f73a5872bd066815bd57b57401125187ba901f095a4", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_reload, ">= 1.6.1", [hex: :phoenix_live_reload, repo: "hexpm", optional: true]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "9a1eb5d2f12ff4912328dfbfe652c27fded462c6ed6fd11814ee28d3e9d016b4"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, @@ -85,6 +85,6 @@ "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, "unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"}, "usage_rules": {:hex, :usage_rules, "0.1.25", "bad5b2cbd45da053423051a752f35ae5249e33ec90c83d0f1ac1be3d90ad9bde", [:mix], [{:igniter, ">= 0.6.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "406598fa133a424d0a8b2d21eb86b8c6e345626d822d7247cdb408a5c3dbe66a"}, - "wallaby": {:hex, :wallaby, "0.30.9", "51d60682092c3c428c63b656b818e2258202b9f9a31ec37230659647ae20325b", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "62e3ccb89068b231b50ed046219022020516d44f443eebef93a19db4be95b808"}, + "wallaby": {:hex, :wallaby, "0.30.11", "93b635bb3d01b11b5a80f83a5d9be59e87ccc8c8390136b7c526dd4fc7e485e3", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "407b50972e3827ce77e3b8292c36dcbd6b21b6837cc4f12ee8767e92a72610ac"}, "web_driver_client": {:hex, :web_driver_client, "0.2.0", "63b76cd9eb3b0716ec5467a0f8bead73d3d9612e63f7560d21357f03ad86e31a", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:tesla, "~> 1.3", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "83cc6092bc3e74926d1c8455f0ce927d5d1d36707b74d9a65e38c084aab0350f"}, } diff --git a/test/controllers/post_controller_test.exs b/test/controllers/post_controller_test.exs index 13c9f465..afea16b0 100644 --- a/test/controllers/post_controller_test.exs +++ b/test/controllers/post_controller_test.exs @@ -1,7 +1,10 @@ defmodule Tilex.PostControllerTest do use TilexWeb.ConnCase, async: true - alias Tilex.Factory + alias Tilex.Auth + alias Tilex.Blog.Post + alias Tilex.Factory + alias Tilex.Repo test "lists all entries on index", %{conn: conn} do conn = get(conn, Routes.post_path(conn, :index)) @@ -55,7 +58,7 @@ defmodule Tilex.PostControllerTest do post(conn, Routes.post_path(conn, :create, params)) til = - Tilex.Blog.Post + Post |> Tilex.Repo.all() |> List.first() @@ -79,7 +82,7 @@ defmodule Tilex.PostControllerTest do post(conn, Routes.post_path(conn, :create, params)) til = - Tilex.Blog.Post + Post |> Tilex.Repo.all() |> List.first() @@ -102,7 +105,7 @@ defmodule Tilex.PostControllerTest do post(conn, Routes.post_path(conn, :create, params)) til = - Tilex.Blog.Post + Post |> Tilex.Repo.all() |> List.first() @@ -133,7 +136,7 @@ defmodule Tilex.PostControllerTest do put(conn, Routes.post_path(conn, :update, til.slug, params)) til = - Tilex.Blog.Post + Post |> Tilex.Repo.get(til.id) assert til.title == "New Title" @@ -162,7 +165,7 @@ defmodule Tilex.PostControllerTest do put(conn, Routes.post_path(conn, :update, til.slug, params)) til = - Tilex.Blog.Post + Post |> Tilex.Repo.get(til.id) assert til.title == "New Title" @@ -190,18 +193,82 @@ defmodule Tilex.PostControllerTest do put(conn, Routes.post_path(conn, :update, til.slug, params)) til = - Tilex.Blog.Post + Post |> Tilex.Repo.get(til.id) assert til.title == "New Title" assert til.max_likes == 1 end + + test "author can delete their own post", %{ + conn: conn, + current_user: current_user + } do + post = Factory.insert!(:post, developer: current_user) + + conn = delete(conn, Routes.post_path(conn, :delete, post.slug)) + + assert redirected_to(conn) == Routes.developer_path(conn, :show, current_user) + assert get_flash(conn, :info) == "Post deleted successfully" + assert Repo.all(Post) == [] + end + + test "author cannot delete another developer's post", %{ + conn: conn + } do + other_user = Factory.insert!(:developer, username: "other-dev") + post = Factory.insert!(:post, developer: other_user) + post_id = post.id + + assert_raise Ecto.NoResultsError, fn -> + delete(conn, Routes.post_path(conn, :delete, post.slug)) + end + + assert [%Post{id: ^post_id}] = Repo.all(Post) + end end - defp authenticated_conn(%{conn: conn}) do - current_user = Factory.insert!(:developer, email: "current@example.com", username: "current") - channel = Factory.insert!(:channel, name: "git") + describe "when authenticated as admin" do + setup :authenticated_admin_conn + test "admin can delete any post", %{ + conn: conn, + current_user: _admin + } do + other_user = Factory.insert!(:developer, username: "other-dev") + post = Factory.insert!(:post, developer: other_user) + + conn = delete(conn, Routes.post_path(conn, :delete, post.slug)) + + assert redirected_to(conn) == Routes.developer_path(conn, :show, other_user) + assert get_flash(conn, :info) == "Post deleted successfully" + assert Repo.all(Post) == [] + end + end + + test "unauthenticated user cannot delete post", %{conn: conn} do + post = Factory.insert!(:post) + post_id = post.id + + conn = delete(conn, Routes.post_path(conn, :delete, post.slug)) + + assert html_response(conn, 302) + assert get_flash(conn, :info) == "Authentication required" + assert [%Post{id: ^post_id}] = Repo.all(Post) + end + + defp authenticated_conn(context) do + Factory.insert!(:developer, email: "current@example.com", username: "current") + |> do_authenticated_conn(context) + end + + defp authenticated_admin_conn(context) do + Factory.insert!(:developer, email: "admin@example.com", username: "admin", admin: true) + |> do_authenticated_conn(context) + end + + defp do_authenticated_conn(current_user, %{conn: conn}) do + channel = Factory.insert!(:channel, name: "git") conn = Auth.Guardian.Plug.sign_in(conn, current_user) {:ok, conn: conn, current_user: current_user, channel: channel} end diff --git a/test/features/admin_edits_post_test.exs b/test/features/admin_edits_post_test.exs index 5cb359fc..0d70cdf1 100644 --- a/test/features/admin_edits_post_test.exs +++ b/test/features/admin_edits_post_test.exs @@ -1,10 +1,10 @@ defmodule AdminEditsPostTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false alias Tilex.Integration.Pages.PostForm alias Tilex.Integration.Pages.PostShowPage - test "fills out form and updates post from post show", %{session: session} do + feature "fills out form and updates post from post show", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer, %{username: "luke-skywalker"}) admin = Factory.insert!(:developer, %{admin: true, username: "darth-vader"}) @@ -29,7 +29,7 @@ defmodule AdminEditsPostTest do |> PostShowPage.ensure_page_loaded("Even Awesomer Post!") end - test "enters a title that is too long", %{session: session} do + feature "enters a title that is too long", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) diff --git a/test/features/developer_creates_post_test.exs b/test/features/developer_creates_post_test.exs index 3dda3b45..e7064a14 100644 --- a/test/features/developer_creates_post_test.exs +++ b/test/features/developer_creates_post_test.exs @@ -7,8 +7,7 @@ defmodule DeveloperCreatesPostTest do alias Tilex.Integration.Pages.PostShowPage alias Tilex.Integration.Pages.PostForm - test "fills out form and save", %{session: session} do - Ecto.Adapters.SQL.Sandbox.allow(Tilex.Repo, self(), Process.whereis(Tilex.Notifications)) + feature "fills out form and save", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) @@ -48,8 +47,7 @@ defmodule DeveloperCreatesPostTest do |> Navigation.ensure_heading("TODAY I LEARNED") end - test "fills out form and save & publish", %{session: session} do - Ecto.Adapters.SQL.Sandbox.allow(Tilex.Repo, self(), Process.whereis(Tilex.Notifications)) + feature "fills out form and save & publish", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) @@ -88,7 +86,7 @@ defmodule DeveloperCreatesPostTest do |> Navigation.ensure_heading("TODAY I LEARNED") end - test "cancels submission", %{session: session} do + feature "cancels submission", %{session: session} do developer = Factory.insert!(:developer) session @@ -99,7 +97,7 @@ defmodule DeveloperCreatesPostTest do |> IndexPage.ensure_page_loaded() end - test "fails to enter things", %{session: session} do + feature "fails to enter things", %{session: session} do developer = Factory.insert!(:developer) session @@ -113,7 +111,7 @@ defmodule DeveloperCreatesPostTest do |> CreatePostPage.expect_form_has_error("Channel can't be blank") end - test "enters a title that is too long", %{session: session} do + feature "enters a title that is too long", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) @@ -131,7 +129,7 @@ defmodule DeveloperCreatesPostTest do |> CreatePostPage.expect_form_has_error("Title should be at most 50 character(s)") end - test "enters a body that is too long", %{session: session} do + feature "enters a body that is too long", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) @@ -149,7 +147,7 @@ defmodule DeveloperCreatesPostTest do |> CreatePostPage.expect_form_has_error("Body should be at most 200 word(s)") end - test "enters markdown code into the body", %{session: session} do + feature "enters markdown code into the body", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) @@ -166,7 +164,7 @@ defmodule DeveloperCreatesPostTest do assert find(session, Query.css("strong", text: "bold powerup")) end - test "views parsed markdown preview", %{session: session} do + feature "views parsed markdown preview", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) diff --git a/test/features/developer_deletes_post_test.exs b/test/features/developer_deletes_post_test.exs new file mode 100644 index 00000000..7ccfb1d2 --- /dev/null +++ b/test/features/developer_deletes_post_test.exs @@ -0,0 +1,47 @@ +defmodule DeveloperDeletesPostTest do + use Tilex.IntegrationCase, async: false + + alias Tilex.Blog.Post + alias Tilex.Integration.Pages.PostShowPage + alias Tilex.Repo + + feature "developer can delete their own post", %{session: session} do + developer = Factory.insert!(:developer) + post = Factory.insert!(:post, developer: developer) + + session + |> sign_in(developer) + |> PostShowPage.navigate(post) + |> click_and_confirm(Query.link("delete")) + |> assert_flash(:info, "Post deleted successfully") + |> assert_path(developer_path(TilexWeb.Endpoint, :show, developer)) + + assert Repo.all(Post) == [] + end + + feature "admin can delete other developer's post", %{session: session} do + developer = Factory.insert!(:developer, username: "regular-dev") + admin = Factory.insert!(:developer, username: "admin-user", admin: true) + post = Factory.insert!(:post, developer: developer) + + session + |> sign_in(admin) + |> PostShowPage.navigate(post) + |> click_and_confirm(Query.link("delete")) + |> assert_flash(:info, "Post deleted successfully") + |> assert_path(developer_path(TilexWeb.Endpoint, :show, developer)) + + assert Repo.all(Post) == [] + end + + feature "non-owner cannot see delete button", %{session: session} do + developer = Factory.insert!(:developer, username: "post-owner") + other_developer = Factory.insert!(:developer, username: "other-dev") + post = Factory.insert!(:post, developer: developer) + + session + |> sign_in(other_developer) + |> PostShowPage.navigate(post) + |> refute_has(Query.link("delete")) + end +end diff --git a/test/features/developer_edits_post_test.exs b/test/features/developer_edits_post_test.exs index 644d9b79..d61dcd74 100644 --- a/test/features/developer_edits_post_test.exs +++ b/test/features/developer_edits_post_test.exs @@ -1,10 +1,10 @@ defmodule DeveloperEditsPostTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false alias Tilex.Integration.Pages.PostForm alias Tilex.Integration.Pages.PostShowPage - test "fills out form and updates post from post show", %{session: session} do + feature "fills out form and updates post from post show", %{session: session} do Factory.insert!(:channel, name: "phoenix") developer = Factory.insert!(:developer) diff --git a/test/features/developer_edits_profile_test.exs b/test/features/developer_edits_profile_test.exs index 909e226d..de423b39 100644 --- a/test/features/developer_edits_profile_test.exs +++ b/test/features/developer_edits_profile_test.exs @@ -1,7 +1,7 @@ defmodule DeveloperEditsProfileTest do use Tilex.IntegrationCase, async: false - test "fills out form and updates post from post show", %{session: session} do + feature "fills out form and updates post from post show", %{session: session} do developer = Factory.insert!(:developer, email: "fine@sixdollareggs.com") sign_in(session, developer) diff --git a/test/features/developer_sees_navigation_bar_test.exs b/test/features/developer_sees_navigation_bar_test.exs index f7640555..43785da3 100644 --- a/test/features/developer_sees_navigation_bar_test.exs +++ b/test/features/developer_sees_navigation_bar_test.exs @@ -1,10 +1,10 @@ defmodule DeveloperSeesNavigationBarTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false alias Tilex.Integration.Pages.Navigation describe "when developer is not authenticated" do - test "there is no link on admin navbar", %{session: session} do + feature "there is no link on admin navbar", %{session: session} do link_texts = session |> visit("/") @@ -17,7 +17,7 @@ defmodule DeveloperSeesNavigationBarTest do describe "when developer is authenticated" do setup [:authenticated_developer] - test "there are links on admin navbar", %{session: session} do + feature "there are links on admin navbar", %{session: session} do link_texts = session |> visit("/") diff --git a/test/features/developer_signs_out_test.exs b/test/features/developer_signs_out_test.exs index a5102f94..769e152d 100644 --- a/test/features/developer_signs_out_test.exs +++ b/test/features/developer_signs_out_test.exs @@ -1,7 +1,7 @@ defmodule Features.DeveloperSignsOutTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false - test "signs out and sees a flash message", %{:session => session} do + feature "signs out and sees a flash message", %{:session => session} do developer = Factory.insert!(:developer) session diff --git a/test/features/developer_views_stats_test.exs b/test/features/developer_views_stats_test.exs index f6199771..aaa826c2 100644 --- a/test/features/developer_views_stats_test.exs +++ b/test/features/developer_views_stats_test.exs @@ -1,5 +1,5 @@ defmodule Features.DeveloperViewsStatsTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false def format_stats_date(%Date{year: year, day: day, month: month}) do day_s = String.pad_leading(Integer.to_string(day), 2, "0") @@ -8,7 +8,7 @@ defmodule Features.DeveloperViewsStatsTest do "#{month_s}-#{day_s}-#{year}" end - test "sees total number of posts by channel", %{session: session} do + feature "sees total number of posts by channel", %{session: session} do developer = Factory.insert!(:developer) phoenix_channel = Factory.insert!(:channel, name: "phoenix") other_channel = Factory.insert!(:channel, name: "other") @@ -70,7 +70,7 @@ defmodule Features.DeveloperViewsStatsTest do assert text_without_newlines(other_channel) =~ "#other 3 posts" end - test "does not see sees til activity chart", %{session: session} do + feature "does not see sees til activity chart", %{session: session} do developer = Factory.insert!(:developer) session diff --git a/test/features/robot_sees_sitemap.exs b/test/features/robot_sees_sitemap.exs index 7f32bc7b..b907153b 100644 --- a/test/features/robot_sees_sitemap.exs +++ b/test/features/robot_sees_sitemap.exs @@ -1,7 +1,7 @@ defmodule RobotSeesSitemap do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false - test "And sees posts", %{session: session} do + feature "And sees posts", %{session: session} do post = Factory.insert!(:post, title: "Klaus and Greta") visit(session, "/sitemap.xml") diff --git a/test/features/visitor_searches_posts_test.exs b/test/features/visitor_searches_posts_test.exs index f41eb835..4beec071 100644 --- a/test/features/visitor_searches_posts_test.exs +++ b/test/features/visitor_searches_posts_test.exs @@ -1,5 +1,5 @@ defmodule VisiorSearchesPosts do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false def fill_in_search(session, query) do session @@ -12,7 +12,7 @@ defmodule VisiorSearchesPosts do |> click(Query.button("Search")) end - test "with no found posts", %{session: session} do + feature "with no found posts", %{session: session} do Factory.insert!(:post, title: "elixir is awesome") fill_in_search(session, "ruby on rails") search_result_header = get_text(session, "#search") @@ -20,7 +20,7 @@ defmodule VisiorSearchesPosts do assert search_result_header == "0 posts about ruby on rails" end - test "with 2 found posts", %{session: session} do + feature "with 2 found posts", %{session: session} do ["Elixir Rules", "Because JavaScript", "Hashrocket Rules"] |> Enum.each(&Factory.insert!(:post, title: &1)) @@ -36,7 +36,7 @@ defmodule VisiorSearchesPosts do refute body =~ ~r/Because JavaScript/ end - test "with paginated query results", %{session: session} do + feature "with paginated query results", %{session: session} do max_posts_on_page = Application.get_env(:tilex, :page_size) 1..(max_posts_on_page * 2) diff --git a/test/features/visitor_views_channel_test.exs b/test/features/visitor_views_channel_test.exs index ec03a8a8..7741875b 100644 --- a/test/features/visitor_views_channel_test.exs +++ b/test/features/visitor_views_channel_test.exs @@ -1,7 +1,7 @@ defmodule Features.VisitorViewsChannelTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false - test "sees associated posts", %{session: session} do + feature "sees associated posts", %{session: session} do target_channel = Factory.insert!(:channel, name: "phoenix") other_channel = Factory.insert!(:channel, name: "other") @@ -22,7 +22,7 @@ defmodule Features.VisitorViewsChannelTest do assert page_title(session) == "Phoenix - Today I Learned" end - test "the page has a list of paginated posts", %{session: session} do + feature "the page has a list of paginated posts", %{session: session} do channel = Factory.insert!(:channel, name: "smalltalk") {:ok, inserted_at} = DateTime.from_naive(~N[2019-06-28 16:05:47], "Etc/UTC") diff --git a/test/features/visitor_views_developer_test.exs b/test/features/visitor_views_developer_test.exs index 2c6a8b26..807e5f23 100644 --- a/test/features/visitor_views_developer_test.exs +++ b/test/features/visitor_views_developer_test.exs @@ -1,9 +1,9 @@ defmodule Features.VisitorViewsDeveloper do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false alias TilexWeb.Endpoint - test "and sees the developer's posts", %{session: session} do + feature "and sees the developer's posts", %{session: session} do developer = Factory.insert!(:developer, username: "makinpancakes") post = Factory.insert!(:post, developer: developer) @@ -19,7 +19,7 @@ defmodule Features.VisitorViewsDeveloper do assert page_title(session) == "makinpancakes - Today I Learned" end - test "and sees a prolific developer's posts", %{session: session} do + feature "and sees a prolific developer's posts", %{session: session} do developer = Factory.insert!(:developer, username: "banjocardhush") Factory.insert_list!(:post, 7, developer: developer) @@ -30,7 +30,7 @@ defmodule Features.VisitorViewsDeveloper do assert page_header =~ ~r/7 posts by banjocardhush/ end - test "and sees the developer's twitter when set", %{session: session} do + feature "and sees the developer's twitter when set", %{session: session} do developer = Factory.insert!(:developer, twitter_handle: "makinbaconpancakes") visit(session, developer_path(Endpoint, :show, developer)) diff --git a/test/features/visitor_views_error_page_test.exs b/test/features/visitor_views_error_page_test.exs index 01305209..d146994c 100644 --- a/test/features/visitor_views_error_page_test.exs +++ b/test/features/visitor_views_error_page_test.exs @@ -1,8 +1,8 @@ defmodule VisitorViewsErrorPageTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false describe "pages early rejected by url rule" do - test "shows not found error page", %{session: session} do + feature "shows not found error page", %{session: session} do session = visit(session, "/some-page.php") assert page_title(session) == "Not Found - Today I Learned" @@ -13,7 +13,7 @@ defmodule VisitorViewsErrorPageTest do end describe "channel not found" do - test "shows not found error page", %{session: session} do + feature "shows not found error page", %{session: session} do session = visit(session, "/missing-channel") assert page_title(session) == "Not Found - Today I Learned" diff --git a/test/features/visitor_views_post_test.exs b/test/features/visitor_views_post_test.exs index 940addfc..cce59b13 100644 --- a/test/features/visitor_views_post_test.exs +++ b/test/features/visitor_views_post_test.exs @@ -4,7 +4,7 @@ defmodule VisitorViewsPostTest do alias TilexWeb.Endpoint alias Tilex.Integration.Pages.PostShowPage - test "the page shows a post", %{session: session} do + feature "the page shows a post", %{session: session} do Application.put_env(:tilex, :date_display_tz, "") developer = Factory.insert!(:developer) @@ -45,7 +45,7 @@ defmodule VisitorViewsPostTest do assert path =~ post.slug end - test "the page shows a post with the correct timezone if given", %{session: session} do + feature "the page shows a post with the correct timezone if given", %{session: session} do Application.put_env(:tilex, :date_display_tz, "America/Chicago") developer = Factory.insert!(:developer) @@ -66,7 +66,7 @@ defmodule VisitorViewsPostTest do assert post_date(session) == "February 1, 2018" end - test "and sees marketing copy, if it exists", %{session: session} do + feature "and sees marketing copy, if it exists", %{session: session} do marketing_channel = Factory.insert!(:channel, name: "elixir") post_in_marketing_channel = Factory.insert!(:post, channel: marketing_channel) @@ -80,7 +80,7 @@ defmodule VisitorViewsPostTest do assert copy =~ String.slice(marketing_content, 0, 10) end - test "and sees a special slug", %{session: session} do + feature "and sees a special slug", %{session: session} do post = Factory.insert!(:post, title: "Super Sluggable Title") url = @@ -102,7 +102,7 @@ defmodule VisitorViewsPostTest do assert url =~ "#{post.slug}-alternate-also-cool-title" end - test "and sees a channel specific twitter card and a post specific twitter description", %{ + feature "and sees a channel specific twitter card and a post specific twitter description", %{ session: session } do popular_channel = Factory.insert!(:channel, name: "command-line") @@ -132,7 +132,7 @@ defmodule VisitorViewsPostTest do refute twitter_description =~ "Another sentence" end - test "and sees a post specific twitter description WITHOUT markdown", %{ + feature "and sees a post specific twitter description WITHOUT markdown", %{ session: session } do popular_channel = Factory.insert!(:channel, name: "command-line") @@ -151,7 +151,7 @@ defmodule VisitorViewsPostTest do refute twitter_description =~ "Another sentence" end - test "and clicks 'like' for that post", %{session: session} do + feature "and clicks 'like' for that post", %{session: session} do Tilex.DateTimeMock.start_link([]) developer = Factory.insert!(:developer) post = Factory.insert!(:post, title: "A special post", developer: developer, likes: 1) @@ -184,7 +184,7 @@ defmodule VisitorViewsPostTest do assert post.max_likes == 2 end - test "sees raw markdown version", %{session: session} do + feature "sees raw markdown version", %{session: session} do title = "A special post" body = """ @@ -218,7 +218,7 @@ defmodule VisitorViewsPostTest do """) end - test "via the random url", %{session: session} do + feature "via the random url", %{session: session} do post = Factory.insert!(:post) session @@ -233,7 +233,7 @@ defmodule VisitorViewsPostTest do assert page_title(session) == "#{post.title} - Today I Learned" end - test "via the random by channel url", %{session: session} do + feature "via the random by channel url", %{session: session} do post = Factory.insert!(:post) session diff --git a/test/features/visitor_views_rss_feed_test.exs b/test/features/visitor_views_rss_feed_test.exs index f8357da4..3d4dd607 100644 --- a/test/features/visitor_views_rss_feed_test.exs +++ b/test/features/visitor_views_rss_feed_test.exs @@ -1,12 +1,12 @@ defmodule VisitorViewsRSSFeed do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false - test "via the legacy atom query parameter", %{session: session} do + feature "via the legacy atom query parameter", %{session: session} do visit(session, "/?format=atom") assert current_path(session) == "/rss" end - test "via an alternate RSS query parameter", %{session: session} do + feature "via an alternate RSS query parameter", %{session: session} do visit(session, "/?format=rss") assert current_path(session) == "/rss" end diff --git a/test/features/visitor_views_stats_test.exs b/test/features/visitor_views_stats_test.exs index 5bcf82d7..3e19f6e5 100644 --- a/test/features/visitor_views_stats_test.exs +++ b/test/features/visitor_views_stats_test.exs @@ -1,7 +1,7 @@ defmodule Features.VisitorViewsStatsTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false - test "sees total number of posts by channel", %{session: session} do + feature "sees total number of posts by channel", %{session: session} do target_channel = Factory.insert!(:channel, name: "phoenix") other_channel = Factory.insert!(:channel, name: "other") @@ -24,7 +24,7 @@ defmodule Features.VisitorViewsStatsTest do assert text_without_newlines(phoenix_channel) =~ "#phoenix 1 post" end - test "sees most liked and hottest tils", %{session: session} do + feature "sees most liked and hottest tils", %{session: session} do posts = [ "Controllers", "Views", @@ -64,7 +64,7 @@ defmodule Features.VisitorViewsStatsTest do assert text_without_newlines(insert_mode) =~ "Controllers #phoenix • 1 like" end - test "sees til activity", %{session: session} do + feature "sees til activity", %{session: session} do dt = fn {_y, _m, _d} = date -> {date, {12, 0, 0}} |> NaiveDateTime.from_erl!() @@ -112,7 +112,7 @@ defmodule Features.VisitorViewsStatsTest do ) end - test "sees total number of posts by developer", %{session: session} do + feature "sees total number of posts by developer", %{session: session} do developer = Factory.insert!(:developer, username: "makinpancakes") Factory.insert!(:post, developer: developer) diff --git a/test/features/visitor_visits_homepage_test.exs b/test/features/visitor_visits_homepage_test.exs index e1191baf..c9817ad5 100644 --- a/test/features/visitor_visits_homepage_test.exs +++ b/test/features/visitor_visits_homepage_test.exs @@ -1,13 +1,13 @@ defmodule VisitorVisitsHomepageTest do - use Tilex.IntegrationCase, async: true + use Tilex.IntegrationCase, async: false - test "the page does not have a Create Post link", %{session: session} do + feature "the page does not have a Create Post link", %{session: session} do visit(session, "/") refute has?(session, Query.link("Create Post")) end - test "the page has the appropriate branding", %{session: session} do + feature "the page has the appropriate branding", %{session: session} do header_text = session |> visit("/") @@ -19,7 +19,7 @@ defmodule VisitorVisitsHomepageTest do assert page_title(session) == "Hashrocket - Today I Learned" end - test "the page has a list of posts", %{session: session} do + feature "the page has a list of posts", %{session: session} do channel = Factory.insert!(:channel, name: "smalltalk") Factory.insert!( @@ -44,7 +44,7 @@ defmodule VisitorVisitsHomepageTest do assert post_footer =~ ~r/#smalltalk/i end - test "the page has a list of paginated posts", %{session: session} do + feature "the page has a list of paginated posts", %{session: session} do Factory.insert_list!(:post, 5 + 1) visit(session, "/") diff --git a/test/support/integration_case.ex b/test/support/integration_case.ex index fd856938..85d4961f 100644 --- a/test/support/integration_case.ex +++ b/test/support/integration_case.ex @@ -1,23 +1,28 @@ defmodule Tilex.IntegrationCase do use ExUnit.CaseTemplate + alias Wallaby.Browser + alias Wallaby.Element + alias Wallaby.Query + using do quote do - use Wallaby.DSL - alias Wallaby.Query - alias Wallaby.Element + use Wallaby.Feature - alias Tilex.Blog.Channel - alias Tilex.Factory - alias Tilex.Blog.Post - alias Tilex.Repo - alias TilexWeb.Endpoint import Ecto import Ecto.Changeset import Ecto.Query - - import TilexWeb.Router.Helpers + import Tilex.IntegrationCase import Tilex.WallabyTestHelpers + import TilexWeb.Router.Helpers + + alias Tilex.Blog.Channel + alias Tilex.Blog.Post + alias Tilex.Factory + alias Tilex.Repo + alias TilexWeb.Endpoint + alias Wallaby.Element + alias Wallaby.Query def sign_in(session, developer) do visit(session, "/admin?id=#{developer.id}") @@ -27,13 +32,64 @@ defmodule Tilex.IntegrationCase do developer = Factory.insert!(:developer, admin: true, username: "Rock Teer") [session: sign_in(session, developer), developer: developer] end + + setup do + Ecto.Adapters.SQL.Sandbox.allow(Tilex.Repo, self(), Process.whereis(Tilex.Notifications)) + end end end - setup tags do - Tilex.DataCase.setup_sandbox(tags) - metadata = Phoenix.Ecto.SQL.Sandbox.metadata_for(Tilex.Repo, self()) - {:ok, session} = Wallaby.start_session(metadata: metadata) - {:ok, session: session} + def click_and_confirm(session, query) do + Browser.accept_confirm(session, &Browser.click(&1, query)) + session + end + + def assert_path(session, path) do + retry!(session, fn -> + assert Browser.current_path(session) == path + session + end) + end + + def assert_contains(session, query, expected_text) do + retry!(session, fn -> + texts = session |> Browser.all(query) |> Enum.map(&Element.text/1) + + assert Enum.any?(texts, &String.contains?(&1, expected_text)), + "Unable to find contains text: '#{expected_text}', instead found '#{texts}'" + + session + end) + end + + def assert_texts(session, query, expected_texts) do + retry!(session, fn -> + texts = session |> Browser.all(query) |> Enum.map(&Element.text/1) + + assert texts == expected_texts, + "Unable to find text: '#{expected_texts}', instead found '#{texts}'" + + session + end) + end + + def assert_flash(session, level, message) when level in [:info, :success] do + assert_contains(session, Query.css(".alert-#{level}"), message) + end + + @waits [10, 20, 30, 50, 80, 130] + def retry!(session, func, waits \\ @waits) do + func.() + session + rescue + error -> + case waits do + [] -> + reraise(error, __STACKTRACE__) + + [wait | waits] -> + Process.sleep(wait) + retry!(session, func, waits) + end end end