An IndieWeb engine for a self-hostable website. https://koype.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
14KB

  1. defmodule Koype.Integration.TemplateTest do
  2. use Koype.DataCase
  3. use Koype.Web.ConnCase
  4. use Koype.HttpMock
  5. use Koype.Web.TemplateCase
  6. setup do
  7. setup_site()
  8. :ok
  9. end
  10. @inbuilt_themes ~w(simplest)s
  11. @test_mf2_url "https://v2.jacky.wtf/post/fc7df928-fbcd-43ea-8c7f-5f99fef674be"
  12. def assert_template_used(resp, template_name) do
  13. assert {"x-koype-theme", template_name} in resp.resp_headers
  14. end
  15. def fetch_current_entry(html, url, type \\ "entry") do
  16. assert mf2 = Microformats2.parse(html, url)
  17. entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-#{type}" in entry[:type] end)
  18. refute is_nil(entry_mf2)
  19. entry_mf2
  20. end
  21. for template_name <- ~w(default)s ++ @inbuilt_themes do
  22. describe template_name <> " - renders as expected" do
  23. test "200 ['home'] renders the homepage" do
  24. resp = build_conn() |> template(unquote(template_name)) |> get("/")
  25. assert html_response(
  26. resp,
  27. :ok
  28. )
  29. assert_template_used(resp, unquote(template_name))
  30. end
  31. test "200 ['follow'] renders the following page" do
  32. resp = build_conn() |> template(unquote(template_name)) |> get("/follow")
  33. assert html_response(
  34. resp,
  35. :ok
  36. )
  37. assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
  38. end
  39. test "200 ['stream/page'] renders a pagination page" do
  40. insert_list(6, :entry,
  41. visibility: "public",
  42. post_status: "published",
  43. published_at: Faker.DateTime.backward(3)
  44. )
  45. |> Enum.each(
  46. &Koype.Repo.Entry.json_persist(
  47. &1,
  48. build(:entry_json) |> with_post_type(String.to_atom(&1.type)) |> with_html_content
  49. )
  50. )
  51. resp = build_conn() |> template(unquote(template_name)) |> get("/stream")
  52. assert_template_used(resp, unquote(template_name))
  53. assert html =
  54. html_response(
  55. resp,
  56. :ok
  57. )
  58. feed_url = Koype.Web.Router.Helpers.timeline_url(resp, :sequential)
  59. feed_mf2 = fetch_current_entry(html, feed_url, "feed")
  60. assert entries = feed_mf2[:children]
  61. assert length(entries) == 6
  62. end
  63. test "200 ['stream/empty'] renders an empty stream" do
  64. resp = build_conn() |> template(unquote(template_name)) |> get("/stream")
  65. assert_template_used(resp, unquote(template_name))
  66. assert html = html_response(resp, :ok)
  67. feed_url = Koype.Web.Router.Helpers.timeline_url(resp, :sequential)
  68. feed_mf2 = fetch_current_entry(html, feed_url, "feed") || %{children: nil}
  69. refute feed_mf2[:children]
  70. end
  71. for {post_type, property, json_property} <- [
  72. {:like, :like_of, "like-of"},
  73. {:repost, :repost_of, "repost-of"},
  74. {:reply, :in_reply_to, "in-reply-to"},
  75. {:bookmark, :bookmark_of, "bookmark-of"}
  76. ] do
  77. test "200 entry/view - shows reply header for #{post_type}" do
  78. entry = insert(:entry, type: Atom.to_string(unquote(post_type)))
  79. entry_json =
  80. build(:entry_json)
  81. |> with_html_content
  82. |> Map.put_new_lazy(unquote(json_property), fn ->
  83. Map.merge(
  84. with_post_type(%{}, unquote(post_type))
  85. |> Map.get(unquote(json_property))
  86. |> List.first(),
  87. %{"content" => %{"text" => "This is text."}}
  88. )
  89. |> List.wrap()
  90. end)
  91. {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
  92. entry_url = Koype.Repo.Entry.get_uri(entry)
  93. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  94. assert html =
  95. html_response(
  96. resp,
  97. :ok
  98. )
  99. assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
  100. assert mf2 = Microformats2.parse(html, entry_url)
  101. assert entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
  102. assert response_json = entry_json[unquote(json_property)] |> List.first()
  103. assert response_mf2 = entry_mf2[:properties][unquote(property)] |> List.first()
  104. assert response_author = response_mf2[:properties][:author]
  105. assert response_json["author"]["url"] in response_author
  106. assert response_json["url"] in response_mf2[:properties][:url]
  107. assert "h-cite" in response_mf2[:type]
  108. end
  109. end
  110. test "200 entry/view - shows baseline" do
  111. entry = insert(:entry, name: "", type: "note")
  112. entry_json = build(:entry_json) |> with_html_content
  113. {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
  114. entry_url = Koype.Repo.Entry.get_uri(entry)
  115. insert_list(3, :webmention, target: entry_url)
  116. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  117. assert html =
  118. html_response(
  119. resp,
  120. :ok
  121. )
  122. assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
  123. assert mf2 = Microformats2.parse(html, entry_url)
  124. entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
  125. refute is_nil(entry_mf2)
  126. assert entry_url in entry_mf2[:properties][:url]
  127. assert Koype.Template.format!(entry.published_at) in entry_mf2[:properties][:published]
  128. mf2_name = entry_mf2[:properties][:name]
  129. mf2_content = entry_mf2[:properties][:content]
  130. json_content = entry_json["content"]
  131. expected_mf2_content = %{
  132. html: json_content["html"] |> List.first(),
  133. text: json_content["plain"]
  134. }
  135. assert mf2_name
  136. assert expected_mf2_content in mf2_content
  137. end
  138. test "200 entry/view - shows media + content" do
  139. Logger.configure(level: :debug)
  140. entry = insert(:entry, name: "", type: "photo")
  141. {:ok, entry_json} =
  142. build(:entry_json)
  143. |> with_html_content
  144. |> with_post_type(:single_photo)
  145. |> IndieWeb.Micropub.Entry.process_properties()
  146. {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
  147. entry_url = Koype.Repo.Entry.get_uri(entry)
  148. insert_list(3, :webmention, target: entry_url)
  149. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  150. assert html =
  151. html_response(
  152. resp,
  153. :ok
  154. )
  155. Apex.ap([:html, html])
  156. assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
  157. assert mf2 = Microformats2.parse(html, entry_url)
  158. entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
  159. refute is_nil(entry_mf2)
  160. assert entry_url in entry_mf2[:properties][:url]
  161. assert Koype.Template.format!(entry.published_at) in entry_mf2[:properties][:published]
  162. mf2_name = entry_mf2[:properties][:name]
  163. mf2_content = entry_mf2[:properties][:content]
  164. json_content = entry_json["content"]
  165. expected_mf2_content = %{
  166. html: json_content["html"] |> List.first(),
  167. text: json_content["plain"]
  168. }
  169. assert mf2_name
  170. assert expected_mf2_content in mf2_content
  171. assert Map.has_key?(entry_mf2[:properties], :photo)
  172. end
  173. test "200 entry/view - shows name for articles" do
  174. entry_json = build(:entry_json) |> with_post_type(:article)
  175. {:ok, entry} = Koype.Repo.Entry.update(insert(:entry, type: "article"), entry_json)
  176. entry_url = Koype.Repo.Entry.get_uri(entry)
  177. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  178. assert html =
  179. html_response(
  180. resp,
  181. :ok
  182. )
  183. assert_template_used(resp, unquote(template_name))
  184. entry_mf2 = fetch_current_entry(html, entry_url)
  185. assert %{html: entry.name, text: entry.name} ==
  186. entry_mf2[:properties][:name] |> List.first()
  187. end
  188. test "200 entry/view - shows reactions" do
  189. entry = insert(:entry, name: "")
  190. entry_json = build(:entry_json) |> with_html_content
  191. {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
  192. entry_url = Koype.Repo.Entry.get_uri(entry)
  193. wms = [
  194. insert(:webmention,
  195. author: "https://v2.jacky.wtf",
  196. target: URI.parse(entry_url).path,
  197. source: @test_mf2_url,
  198. type: "like",
  199. moderation_status: "approved"
  200. ),
  201. insert(:webmention,
  202. author: "https://v2.jacky.wtf",
  203. target: URI.parse(entry_url).path,
  204. source: @test_mf2_url <> "?next_one",
  205. type: "like",
  206. moderation_status: "approved"
  207. ),
  208. insert(:webmention,
  209. author: "https://v2.jacky.wtf",
  210. target: URI.parse(entry_url).path,
  211. source: @test_mf2_url <> "?round_two",
  212. type: "like",
  213. moderation_status: "approved"
  214. )
  215. ]
  216. Enum.each(wms, fn wm ->
  217. {:ok, _} = wm |> with_mf2
  218. end)
  219. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  220. assert html =
  221. html_response(
  222. resp,
  223. :ok
  224. )
  225. assert_template_used(resp, unquote(template_name))
  226. entry_mf2 = fetch_current_entry(html, entry_url)
  227. likes = Map.get(entry_mf2[:properties], :like, [])
  228. assert length(likes) == 3
  229. assert Enum.all?(likes, fn like ->
  230. assert "Jacky Alciné" == like[:value]
  231. assert like[:properties][:url] |> List.first() =~ @test_mf2_url
  232. end)
  233. end
  234. test "200 ['entry/view'] shows comments" do
  235. entry = insert(:entry, name: "")
  236. entry_json = build(:entry_json) |> with_html_content
  237. {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
  238. entry_url = Koype.Repo.Entry.get_uri(entry)
  239. wms = [
  240. insert(:webmention,
  241. author: "https://v2.jacky.wtf",
  242. target: URI.parse(entry_url).path,
  243. source: @test_mf2_url,
  244. type: "reply",
  245. moderation_status: "approved"
  246. ),
  247. insert(:webmention,
  248. author: "https://v2.jacky.wtf",
  249. target: URI.parse(entry_url).path,
  250. source: @test_mf2_url <> "?next_one",
  251. type: "reply",
  252. moderation_status: "approved"
  253. ),
  254. insert(:webmention,
  255. author: "https://v2.jacky.wtf",
  256. target: URI.parse(entry_url).path,
  257. source: @test_mf2_url <> "?round_two",
  258. type: "reply",
  259. moderation_status: "approved"
  260. )
  261. ]
  262. Enum.each(wms, fn wm ->
  263. {:ok, _} = wm |> with_mf2
  264. end)
  265. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  266. assert html =
  267. html_response(
  268. resp,
  269. :ok
  270. )
  271. assert_template_used(resp, unquote(template_name))
  272. entry_mf2 = fetch_current_entry(html, entry_url)
  273. assert Koype.Template.format!(entry.published_at) in entry_mf2[:properties][:published]
  274. comments = Map.get(entry_mf2[:properties], :comment, [])
  275. assert length(comments) == 3
  276. assert Enum.all?(comments, fn comment ->
  277. "https://v2.jacky.wtf" =
  278. comment[:properties][:author] |> List.first() |> Map.get(:value)
  279. end)
  280. end
  281. test "200 entry/view - no content in entry" do
  282. entry = insert(:entry)
  283. {:ok, entry_json} =
  284. build(:entry_json)
  285. |> Map.drop(~w(name content summary))
  286. |> IndieWeb.Micropub.Content.process_properties()
  287. Koype.Repo.Entry.json_persist(entry, entry_json)
  288. entry_url = Koype.Repo.Entry.get_uri(entry)
  289. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  290. assert html = html_response(resp, :ok)
  291. assert_template_used(resp, unquote(template_name))
  292. entry_mf2 = fetch_current_entry(html, entry_url)
  293. refute entry_mf2[:properties][:content]
  294. end
  295. test "200 entry/view - text-only content in entry" do
  296. entry = insert(:entry)
  297. {:ok, entry_json} =
  298. build(:entry_json)
  299. |> Map.drop(~w(name content summary))
  300. |> Map.put("content", Faker.Lorem.sentence())
  301. |> IndieWeb.Micropub.Content.process_properties()
  302. {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
  303. entry_url = Koype.Repo.Entry.get_uri(entry)
  304. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  305. assert html = html_response(resp, :ok)
  306. assert_template_used(resp, unquote(template_name))
  307. entry_mf2 = fetch_current_entry(html, entry_url)
  308. assert %{
  309. html: "<p>#{entry_json["content"]["plain"]}</p>",
  310. text: entry_json["content"]["plain"]
  311. } in entry_mf2[:properties][:content]
  312. end
  313. test "410 entry/gone" do
  314. {:ok, entry} =
  315. insert(:entry, name: Faker.Lorem.sentence())
  316. |> Koype.Repo.Entry.delete()
  317. {:ok, _} = Koype.Repo.Entry.json_persist(entry, build(:entry_json))
  318. entry_url = Koype.Repo.Entry.get_uri(entry)
  319. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  320. assert html =
  321. html_response(
  322. resp,
  323. :gone
  324. )
  325. assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
  326. refute html =~ entry.name
  327. end
  328. test "404 entry/not-found" do
  329. entry = insert(:entry, name: Faker.Lorem.sentence())
  330. entry_url = Koype.Repo.Entry.get_uri(entry)
  331. resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
  332. assert html =
  333. html_response(
  334. resp,
  335. :not_found
  336. )
  337. assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
  338. refute html =~ entry.name
  339. end
  340. end
  341. end
  342. end