t_html.ml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. (*
  2. * _ _ ____ _
  3. * _| || |_/ ___| ___ _ __ _ __ ___ | |
  4. * |_ .. _\___ \ / _ \ '_ \| '_ \ / _ \| |
  5. * |_ _|___) | __/ |_) | |_) | (_) |_|
  6. * |_||_| |____/ \___| .__/| .__/ \___/(_)
  7. * |_| |_|
  8. *
  9. * Personal Social Web.
  10. *
  11. * Copyright (C) The #Seppo contributors. All rights reserved.
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation, either version 3 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. *)
  26. open Seppo_lib
  27. module Form = struct
  28. let test_of_channel () =
  29. let ic = "data/cgi_" ^ "2022-04-05T125146.post" |> open_in in
  30. let fv = ic |> Html.Form.of_channel 141 in
  31. ic |> close_in;
  32. (match fv with
  33. | [ (k0, [ v0 ]); (k1, [ v1 ]); (k2, [ v2 ]); (k3, [ v3 ]) ] ->
  34. k0 |> Assrt.equals_string __LOC__ "login";
  35. v0 |> Assrt.equals_string __LOC__ "demo";
  36. k1 |> Assrt.equals_string __LOC__ "password";
  37. v1 |> Assrt.equals_string __LOC__ "demodemodemo";
  38. k2 |> Assrt.equals_string __LOC__ "token";
  39. v2
  40. |> Assrt.equals_string __LOC__
  41. "ff13e7eaf9541ca2ba30fd44e864c3ff014d2bc9";
  42. k3 |> Assrt.equals_string __LOC__ "returnurl";
  43. v3
  44. |> Assrt.equals_string __LOC__ "https://demo.mro.name/shaarligo/o/p/";
  45. assert true
  46. | _ -> failwith __LOC__);
  47. (* match
  48. fv
  49. |> Http.Form.filter_sort_keys
  50. [ "login"; "password"; "token"; "returnurl" ]
  51. with
  52. | [ (k0, [ v0 ]); (k1, [ v1 ]); (k2, [ v2 ]); (k3, [ v3 ]) ] ->
  53. k0 |> Assrt.equals_string __LOC__ "login";
  54. v0 |> Assrt.equals_string __LOC__ "demo";
  55. k1 |> Assrt.equals_string __LOC__ "password";
  56. v1 |> Assrt.equals_string __LOC__ "demodemodemo";
  57. k2 |> Assrt.equals_string __LOC__ "returnurl";
  58. v2
  59. |> Assrt.equals_string __LOC__ "https://demo.mro.name/shaarligo/o/p/";
  60. k3 |> Assrt.equals_string __LOC__ "token";
  61. v3
  62. |> Assrt.equals_string __LOC__
  63. "ff13e7eaf9541ca2ba30fd44e864c3ff014d2bc9";
  64. assert true
  65. | _ -> failwith __LOC__ *);
  66. assert true
  67. let test_to_html () =
  68. let defs = [
  69. ("ka", (Ok "va", "text", [("autofocus",""); ("pattern", {|^\S+$|})]));
  70. ] in
  71. (match List.assoc_opt "ka" defs with
  72. | Some (Ok v,_,_) -> v
  73. | _ -> "foo")
  74. |> Assrt.equals_string __LOC__ "va";
  75. assert true
  76. let test_validate () =
  77. Logr.info (fun m -> m "%s.%s" "http" "test_validate");
  78. (match Html.Form.string
  79. ("uid","text",["required","required"; "pattern","^[a-z]+$"])
  80. ["uid",["hu1"]] with
  81. | Error ("uid", "pattern mismatch") -> ()
  82. | _ -> failwith __LOC__);
  83. (match Html.Form.validate "uid" "text" (Ok "hu1") ("pattern","^[a-z]+$") with
  84. | Error ("uid", "pattern mismatch") -> ()
  85. | _ -> failwith __LOC__);
  86. (match Html.Form.validate "uid" "text" (Ok "abcd") ("minlength","4") with
  87. | Ok "abcd" -> ()
  88. | _ -> failwith __LOC__);
  89. (match Html.Form.validate "uid" "text" (Ok "abcd") ("minlength","5") with
  90. | Error ("uid","shorter than minlength") -> ()
  91. | _ -> failwith __LOC__);
  92. (match Html.Form.validate "uid" "text" (Ok "abcd") ("minlength","_") with
  93. | Error ("uid","invalid minlength") -> ()
  94. | _ -> failwith __LOC__);
  95. ()
  96. let test_from_html () =
  97. let pred ty valu (na,va) =
  98. Result.bind
  99. valu
  100. (fun v ->
  101. match v with
  102. | None -> Ok None
  103. | Some v as vv ->
  104. match ty,na with
  105. | _,"pattern" ->
  106. Logr.debug (fun m -> m " '%s' ~ /%s/" v va);
  107. Ok vv
  108. | _ ->
  109. Logr.debug (fun m -> m " ignored %s='%s'" na va);
  110. Ok vv)
  111. in
  112. let string (name,(ty,preds)) vals =
  113. let v = Option.bind
  114. (List.assoc_opt name vals)
  115. (fun v -> Some (v |> String.concat "")) in
  116. List.fold_left (pred ty) (Ok v) preds in
  117. let _validate defs vals =
  118. Logr.debug (fun m -> m "Form.validate");
  119. let field init (name,(ty,preds)) =
  120. match string (name,(ty,preds)) vals with
  121. | Error _ as inp ->
  122. (match init with
  123. | Error a -> Error (inp :: a)
  124. | Ok a -> Error (inp :: a)
  125. )
  126. | Ok _ as inp ->
  127. (match init with
  128. | Error a -> Error (inp :: a)
  129. | Ok a -> Ok (inp :: a)
  130. )
  131. in
  132. List.fold_left field (Ok []) defs
  133. in
  134. let def0 = ("ka", ("text", [("autofocus",""); ("pattern", {|^\S+$|})])) in
  135. let _defs = [ def0; ] in
  136. let vals = [
  137. ("ka", ["vb"]);
  138. ] in
  139. (* match _validate defs vals with
  140. | Ok res -> List.assoc_opt "ka" res
  141. |> Option.value ~default:(Ok None)
  142. |> Result.get_ok
  143. |> Option.get
  144. |> Assrt.equals_string __LOC__ "vb"
  145. | _ -> failwith __LOC__); *)
  146. let ( let* ) = Result.bind in
  147. let run () =
  148. let* k = string def0 vals in
  149. Ok k in
  150. (match run() with
  151. | Ok (Some v) -> v |> Assrt.equals_string __LOC__ "vb"
  152. | _ -> assert true);
  153. assert true
  154. let test_from_html1 () =
  155. let i0 : Html.Form.input = ("k0", "text", [
  156. ("autofocus", "autofocus");
  157. ("required", "required");
  158. ("pattern", {|^[a-z][0-9]+$|});
  159. ]) in
  160. let i1 = ("k1", "text", [
  161. ("required", "required");
  162. ("minlength", "1");
  163. ("maxlength", "50");
  164. ("pattern", {|^v.$|});
  165. ]) in
  166. let vals : Html.Form.t = [
  167. ("k0", ["v0"]);
  168. ("k1", ["v1"]);
  169. ] in
  170. let ( let* ) = Result.bind in
  171. let run () =
  172. let* v0 = vals |> Html.Form.string i0 in
  173. let* v1 = Html.Form.string i1 vals in
  174. v0 |> Assrt.equals_string __LOC__ "v0";
  175. v1 |> Assrt.equals_string __LOC__ "v1";
  176. Ok () in
  177. (match run() with
  178. | Error (_,e) -> e |> Assrt.equals_string __LOC__ ""
  179. | _ -> ())
  180. end
  181. let test_add_class () =
  182. let l = [
  183. (("","name"),"foo");
  184. (("","type"),"text");
  185. ] in
  186. (match "b" |> Html.add_class l with
  187. | [
  188. (("","name"),"foo");
  189. (("","type"),"text");
  190. (("","class"),"b");
  191. ] -> ()
  192. | _ -> failwith __LOC__);
  193. ();
  194. let l = [
  195. (("","name"),"foo");
  196. (("","class"),"clz");
  197. (("","type"),"text");
  198. ] in
  199. (match "b" |> Html.add_class l with
  200. | [
  201. (("","name"),"foo");
  202. (("","class"),"b clz");
  203. (("","type"),"text");
  204. ] -> ()
  205. | _ -> failwith __LOC__);
  206. ();
  207. let l = [
  208. (("","name"),"foo");
  209. (("","class"),"a b clz");
  210. (("","type"),"text");
  211. ] in
  212. (match "b" |> Html.add_class l with
  213. | [
  214. (("","name"),"foo");
  215. (("","class"),"a b clz");
  216. (("","type"),"text");
  217. ] -> ()
  218. | _ -> failwith __LOC__);
  219. ()
  220. let soup_test () =
  221. let open Soup in (* https://aantron.github.io/lambdasoup/ *)
  222. let mention a =
  223. let local = a |> texts |> String.concat "" in
  224. (* check txt starting with @ and not having a host *)
  225. let href = R.attribute "href" a |> Uri.of_string in
  226. let host = href |> Uri.host_with_default ~default:"-" in
  227. let txt = local ^ "@" ^ host in
  228. txt |> create_text
  229. in
  230. let proc n =
  231. let p = "div" |> create_element in
  232. append_child p n;
  233. p
  234. |> select "a[href]"
  235. |> iter (fun a ->
  236. print_endline (R.attribute "href" a);
  237. a |> mention |> replace a
  238. );
  239. p
  240. in
  241. {|Hello, world!|} |> parse |> proc |> to_string |> Assrt.equals_string __LOC__ {|<div>Hello, world!</div>|};
  242. {|Hello, <a href="https://example.com/user/12345">@world</a>!|} |> parse |> proc |> to_string |> Assrt.equals_string __LOC__ {|<div>Hello, @world@example.com!</div>|};
  243. ()
  244. let test_to_plain () =
  245. Logr.info (fun m -> m "%s" "test_to_plain");
  246. let a (x,me,ha) =
  247. let prt l = l |> List.iter (fun (s,href) -> Format.asprintf "%s -> %a" s Uri.pp_hum href |> prerr_endline) in
  248. me |> prt;
  249. ha |> prt;
  250. x in
  251. let load_note_content fn =
  252. let fn = "data/ap/inbox/create/note/" ^ fn in
  253. fn
  254. |> File.in_channel
  255. (fun ic ->
  256. match ic |> Ezjsonm.from_channel |> As2_vocab.Activitypub.Decode.obj with
  257. | Error _ -> failwith "failed to load note"
  258. | Ok o -> match o with
  259. | `Create { obj = `Note obj; _ } ->
  260. let _,co = obj.content_map |> List.hd in
  261. co
  262. | _ -> failwith "strange type")
  263. in
  264. {|Hello, world!|} |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|Hello, world!|};
  265. {|Hello, <a href="https://example.com">@world</a>!|} |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|Hello, @world@example.com!|};
  266. {|Hello, <a href="https://example.com">world</a>!|} |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|Hello, world[1]!
  267. [1]: https://example.com|};
  268. {|Hello, <a href="https://example.com">example.com</a>!|} |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|Hello, https://example.com!|};
  269. "note-PZkn02gIUSk.json" |> load_note_content |> Assrt.equals_string __LOC__ {|<p><span class="h-card" translate="no"><a href="https://mastodon.social/@johnleonard" class="u-url mention">@<span>johnleonard</span></a></span> cat&#39;s out of the bag. Stop wasting effort.</p>|};
  270. "note-PZkn02gIUSk.json" |> load_note_content |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|@johnleonard@mastodon.social cat's out of the bag. Stop wasting effort.|};
  271. "note-Hjcb9bqwCgk.json" |> load_note_content |> Assrt.equals_string __LOC__ {|<p><span class="h-card" translate="no"><a href="https://floss.social/@wschenk" class="u-url mention">@<span>wschenk</span></a></span> <span class="h-card" translate="no"><a href="https://mstdn.social/@geoglyphentropy" class="u-url mention">@<span>geoglyphentropy</span></a></span> <span class="h-card" translate="no"><a href="https://mstdn.social/@nus" class="u-url mention">@<span>nus</span></a></span> <span class="h-card" translate="no"><a href="https://tooot.im/@DavidKafri" class="u-url mention">@<span>DavidKafri</span></a></span> <span class="h-card" translate="no"><a href="https://me.dm/@thetechtutor" class="u-url mention">@<span>thetechtutor</span></a></span> After the Goat refused to explain what military action in response to the <a href="https://mastodon.social/tags/alaqsaflood" class="mention hashtag" rel="tag">#<span>alaqsaflood</span></a> would&#39;ve been moral he lost all priveleges with me, although he eventually admitted that nothing would meet his standards. Eventually he blocked me, although he seems to have somehow replied to me.</p><p>I&#39;m inferring that this has something to with the Goat, but have no idea. Guess I&#39;ll never know what it was about.</p>|};
  272. "note-Hjcb9bqwCgk.json" |> load_note_content |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|@wschenk@floss.social @geoglyphentropy@mstdn.social @nus@mstdn.social @DavidKafri@tooot.im @thetechtutor@me.dm After the Goat refused to explain what military action in response to the #alaqsaflood[1] would've been moral he lost all priveleges with me, although he eventually admitted that nothing would meet his standards. Eventually he blocked me, although he seems to have somehow replied to me.
  273. I'm inferring that this has something to with the Goat, but have no idea. Guess I'll never know what it was about.
  274. [1]: https://mastodon.social/tags/alaqsaflood|};
  275. (* *)
  276. "note-OZcAekXDY1A.json" |> load_note_content |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|Trotz Mücken abends draußen sitzen: 6 Tipps gegen die Mückenplage - Utopia.de
  277. https://utopia.de/ratgeber/trotz-muecken-abends-draussen-sitzen-6-tipps-gegen-die-mueckenplage_372661/
  278. Ich weis ja nicht warum man mich mit Teebaum-Öl gequält hat. »Um #Mücke[1] n zu vertreiben, muss der erste Griff nicht zum #Mückenspray[2] gehen. Mücken sind sehr geruchsempfindlich, weshalb du viele natürliche #Gerüche[3] gegen sie einsetzen kannst.«
  279. #Shaarli[4]💫 📱
  280. https://ripf.de/m/b26xarb
  281. [1]: https://social.wohlfarth.name/tags/M%C3%BCcke
  282. [2]: https://social.wohlfarth.name/tags/M%C3%BCckenspray
  283. [3]: https://social.wohlfarth.name/tags/Ger%C3%BCche
  284. [4]: https://social.wohlfarth.name/tags/Shaarli|};
  285. ();
  286. "note-Gyuo6v3wVRY.json" |> load_note_content |> Html.to_plain |> a |> Assrt.equals_string __LOC__ {|@kura@noc.social Yeah it's fustrating, but I also just don't care enough to go to iPlayer/Pay for something to watch the olympics
  287. Like they are cool events, but they are just so hostile to watch (I assume football is like this as well) that it does not cross over the effort/reward.
  288. I dunno how this is not considered a larger existential problem for them. Then again, if the overall press vibe is right they have bigger existential crisis-es ongoing|};
  289. ()
  290. let test_to_plain_2 () =
  291. Logr.info (fun m -> m "%s" "test_to_plain_2");
  292. (*
  293. test/data/ap/inbox/create/note/note-OZcAekXDY1A.json
  294. *)
  295. ()
  296. let () =
  297. Logr.info (fun m -> m "html_test");
  298. Unix.chdir "../../../test/";
  299. Form.test_of_channel ();
  300. Form.test_to_html ();
  301. Form.test_validate ();
  302. Form.test_from_html ();
  303. Form.test_from_html1 ();
  304. test_add_class ();
  305. soup_test ();
  306. test_to_plain ();
  307. test_to_plain_2 ();
  308. ()