text, user_acc}, opts, {buffer, acc, :parsing}, type), + do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "", :parsing}, handler) + defp do_parse({"" <> text, user_acc}, opts, {buffer, acc, :skip}, type), + do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "", :parsing}, type) - defp do_parse({"" <> text, user_acc}, opts, {buffer, acc, :skip}, handler), - do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "", :parsing}, handler) + defp do_parse({"" <> text, user_acc}, opts, {buffer, acc, :skip}, type), + do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "", :parsing}, type) - defp do_parse({"" <> text, user_acc}, opts, {buffer, acc, :skip}, handler), - do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "", :parsing}, handler) + defp do_parse({"" <> text, user_acc}, opts, {buffer, acc, :skip}, type), + do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "", :parsing}, type) - defp do_parse({"<" <> text, user_acc}, opts, {"", acc, :parsing}, handler), - do: do_parse({text, user_acc}, opts, {"<", acc, {:open, 1}}, handler) + defp do_parse({"<" <> text, user_acc}, opts, {"", acc, :parsing}, type), + do: do_parse({text, user_acc}, opts, {"<", acc, {:open, 1}}, type) - defp do_parse({"<" <> text, user_acc}, opts, {"", acc, {:html, level}}, handler) do - do_parse({text, user_acc}, opts, {"<", acc, {:open, level + 1}}, handler) + defp do_parse({"<" <> text, user_acc}, opts, {"", acc, {:html, level}}, type) do + do_parse({text, user_acc}, opts, {"<", acc, {:open, level + 1}}, type) end - defp do_parse({">" <> text, user_acc}, opts, {buffer, acc, {:attrs, level}}, handler), + defp do_parse({">" <> text, user_acc}, opts, {buffer, acc, {:attrs, level}}, type), do: do_parse( {text, user_acc}, opts, {"", acc <> buffer <> ">", {:html, level}}, - handler + type ) - defp do_parse({<text, user_acc}, opts, {buffer, acc, :parsing}, handler), - do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "text, user_acc}, opts, {buffer, acc, :parsing}, type), + do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "
" <> text, user_acc}, opts, {buffer, acc, :skip}, handler), - do: do_parse({text, user_acc}, opts, {"", acc <> buffer <> "
hello world
\n<`em>another @user__test and @user__test google.com paragraph
\n" - - expected = - "hello world
\n<`em>another @user__test and @user__test google.com paragraph
\n" - - assert AutoLinker.link(text, - mention: true, - mention_prefix: "u/", - class: false, - rel: false, - new_window: false - ) == expected - end - - test "metion @user@example.com" do - text = "hey @user@example.com" - - expected = - "hey @user@example.com" - - assert AutoLinker.link(text, - mention: true, - mention_prefix: "https://example.com/user/" - ) == expected - end - end - - describe "hashtag links" do - test "hashtag" do - expected = - " one #2two three #four." - - assert AutoLinker.link(" one #2two three #four.", - hashtag: true, - hashtag_prefix: "https://example.com/tag/" - ) == expected - end - - test "must have non-numbers" do - expected = "#1ok #42 #7" - - assert AutoLinker.link("#1ok #42 #7", - hashtag: true, - hashtag_prefix: "/t/", - class: false, - rel: false, - new_window: false - ) == expected - end - - test "support French" do - text = "#administrateur·rice·s #ingénieur·e·s" - - expected = - "#administrateur·rice·s #ingénieur·e·s" - - assert AutoLinker.link(text, - hashtag: true, - hashtag_prefix: "/t/", - class: false, - rel: false, - new_window: false - ) == expected - end - - test "support Telugu" do - text = "#చక్రం #కకకకక్ #కకకకాక #కకకక్రకకకక" - - expected = - "#చక్రం #కకకకక్ #కకకకాక #కకకక్రకకకక" - - assert AutoLinker.link(text, - hashtag: true, - hashtag_prefix: "/t/", - class: false, - rel: false, - new_window: false - ) == expected - end - - test "do not turn urls with hashes into hashtags" do - text = "google.com#test #test google.com/#test #tag" - - expected = - "google.com#test #test google.com/#test #tag" - - assert AutoLinker.link(text, - scheme: true, - hashtag: true, - class: false, - new_window: false, - rel: false, - hashtag_prefix: "https://example.com/tag/" - ) == expected - end - - test "works with non-latin characters" do - text = "#漢字 #は #тест #ทดสอบ" - - expected = - "#漢字 #は #тест #ทดสอบ" - - assert AutoLinker.link(text, - scheme: true, - class: false, - new_window: false, - rel: false, - hashtag: true, - hashtag_prefix: "https://example.com/tag/" - ) == expected - end - end - - describe "links" do - test "turning urls into links" do - text = "Hey, check out http://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." - - expected = - "Hey, check out youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." - - assert AutoLinker.link(text, scheme: true) == expected - - # no scheme - text = "Hey, check out www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." - assert AutoLinker.link(text, scheme: true) == expected - end - - test "turn urls with schema into urls" do - text = "📌https://google.com" - expected = "📌google.com" - - assert AutoLinker.link(text, scheme: true, class: false, new_window: false, rel: false) == - expected - end - - test "hostname/@user" do - text = "https://example.com/@user" - - expected = - "example.com/@user" - - assert AutoLinker.link(text, scheme: true) == expected - - text = "https://example.com:4000/@user" - - expected = - "example.com:4000/@user" - - assert AutoLinker.link(text, scheme: true) == expected - - text = "https://example.com:4000/@user" - - expected = - "example.com:4000/@user" - - assert AutoLinker.link(text, scheme: true) == expected - - text = "@username" - expected = "@username" - assert AutoLinker.link(text, scheme: true) == expected - - text = "http://www.cs.vu.nl/~ast/intel/" - - expected = - "cs.vu.nl/~ast/intel/" - - assert AutoLinker.link(text, scheme: true) == expected - - text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" - - expected = - "forum.zdoom.org/viewtopic.php?f=44&t=57087" - - assert AutoLinker.link(text, scheme: true) == expected - - text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" - - expected = - "en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" - - assert AutoLinker.link(text, scheme: true) == expected - - text = "https://en.wikipedia.org/wiki/Duff's_device" - - expected = - "en.wikipedia.org/wiki/Duff's_device" - - assert AutoLinker.link(text, scheme: true) == expected - end - end - - describe "non http links" do - test "xmpp" do - text = "xmpp:user@example.com" - - expected = - "xmpp:user@example.com" - - assert AutoLinker.link(text, extra: true, new_window: false, rel: false) == expected - end - - test "email" do - text = "user@example.com" - expected = "user@example.com" - assert AutoLinker.link(text, email: true) == expected - end - - test "magnet" do - text = - "magnet:?xt=urn:btih:a4104a9d2f5615601c429fe8bab8177c47c05c84&dn=ubuntu-18.04.1.0-live-server-amd64.iso&tr=http%3A%2F%2Ftorrent.ubuntu.com%3A6969%2Fannounce&tr=http%3A%2F%2Fipv6.torrent.ubuntu.com%3A6969%2Fannounce" - - expected = - "magnet:?xt=urn:btih:a4104a9d2f5615601c429fe8bab8177c47c05c84&dn=ubuntu-18.04.1.0-live-server-amd64.iso&tr=http%3A%2F%2Ftorrent.ubuntu.com%3A6969%2Fannounce&tr=http%3A%2F%2Fipv6.torrent.ubuntu.com%3A6969%2Fannounce" - - assert AutoLinker.link(text, extra: true, new_window: false, rel: false) == expected - end - - test "dweb" do - text = - "dweb://584faa05d394190ab1a3f0240607f9bf2b7e2bd9968830a11cf77db0cea36a21+v1.0.0/path/to/file.txt" - - expected = - "dweb://584faa05d394190ab1a3f0240607f9bf2b7e2bd9968830a11cf77db0cea36a21+v1.0.0/path/to/file.txt" - - assert AutoLinker.link(text, extra: true, new_window: false, rel: false) == expected - end - end - - describe "TLDs" do - test "parse with scheme" do - text = "https://google.com" - - expected = - "google.com" - - assert AutoLinker.link(text, scheme: true) == expected - end - - test "only existing TLDs with scheme" do - text = "this url https://google.foobar.blah11blah/ has invalid TLD" - - expected = "this url https://google.foobar.blah11blah/ has invalid TLD" - assert AutoLinker.link(text, scheme: true) == expected - - text = "this url https://google.foobar.com/ has valid TLD" - - expected = - "this url google.foobar.com/ has valid TLD" - - assert AutoLinker.link(text, scheme: true) == expected - end - - test "only existing TLDs without scheme" do - text = "this url google.foobar.blah11blah/ has invalid TLD" - expected = "this url google.foobar.blah11blah/ has invalid TLD" - assert AutoLinker.link(text, scheme: false) == expected - - text = "this url google.foobar.com/ has valid TLD" - - expected = - "this url google.foobar.com/ has valid TLD" - - assert AutoLinker.link(text, scheme: false) == expected - end - - test "only existing TLDs with and without scheme" do - text = "this url http://google.foobar.com/ has valid TLD" - - expected = - "this url google.foobar.com/ has valid TLD" - - assert AutoLinker.link(text, scheme: true) == expected - - text = "this url google.foobar.com/ has valid TLD" - - expected = - "this url google.foobar.com/ has valid TLD" - - assert AutoLinker.link(text, scheme: true) == expected - end - end -end diff --git a/test/builder_test.exs b/test/builder_test.exs index e20f6ea..bfcd9bb 100644 --- a/test/builder_test.exs +++ b/test/builder_test.exs @@ -1,39 +1,34 @@ -defmodule AutoLinker.BuilderTest do +defmodule Linkify.BuilderTest do use ExUnit.Case, async: true - doctest AutoLinker.Builder + doctest Linkify.Builder - import AutoLinker.Builder + import Linkify.Builder test "create_link/2" do - expected = - "text" + expected = "text" assert create_link("text", %{}) == expected - expected = "text" - assert create_link("text", %{rel: nil}) == expected + expected = "text" - expected = - "text" + assert create_link("text", %{new_window: true}) == expected + + expected = "text" + assert create_link("text", %{class: "linkified"}) == expected + + expected = "text" assert create_link("text", %{rel: "me"}) == expected - expected = "t..." + expected = "t..." - assert create_link("text", %{truncate: 3, rel: false}) == expected + assert create_link("text", %{truncate: 3}) == expected - expected = "text" - assert create_link("text", %{truncate: 2, rel: false}) == expected + expected = "text" + assert create_link("text", %{truncate: 2}) == expected - expected = "http://text" - assert create_link("http://text", %{rel: false, strip_prefix: false}) == expected - end - - test "create_markdown_links/2" do - expected = - "text" - - assert create_markdown_links("[text](url)", %{}) == expected + expected = "http://text" + assert create_link("http://text", %{strip_prefix: false}) == expected end test "format_hashtag/3" do @@ -53,40 +48,14 @@ defmodule AutoLinker.BuilderTest do assert format_mention(%{href: "url"}, "user@host", nil) == expected end - describe "create_phone_link" do - test "finishes" do - assert create_phone_link([], "", []) == "" - end - - test "handles one link" do - phrase = "my exten is x888. Call me." - - expected = - ~s'my exten is x888. Call me.' - - assert create_phone_link([["x888", ""]], phrase, attributes: [test: "test"]) == expected - end - - test "handles multiple links" do - phrase = "555.555.5555 or (555) 888-8888" - - expected = - ~s'555.555.5555 or ' <> - ~s'(555) 888-8888' - - assert create_phone_link([["555.555.5555", ""], ["(555) 888-8888"]], phrase, []) == expected - end - end - test "create_mention_link/3" do - expected = - "@navi" + expected = "@navi" assert create_mention_link("@navi", "hello @navi", %{mention_prefix: "/u/"}) == expected end test "create_email_link/3" do - expected = "user@example.org" + expected = "user@example.org" assert create_email_link("user@example.org", %{}) == expected assert create_email_link("user@example.org", %{href: "mailto:user@example.org"}) == expected end diff --git a/test/linkify_test.exs b/test/linkify_test.exs new file mode 100644 index 0000000..c50aace --- /dev/null +++ b/test/linkify_test.exs @@ -0,0 +1,404 @@ +defmodule LinkifyTest do + use ExUnit.Case, async: true + doctest Linkify + + test "default link" do + assert Linkify.link("google.com") == + "google.com" + end + + test "does on link existing links" do + text = ~s(google.com) + assert Linkify.link(text) == text + end + + test "all kinds of links" do + text = "hello google.com https://ddg.com user@email.com irc:///mIRC" + + expected = + "hello google.com ddg.com user@email.com irc:///mIRC" + + assert Linkify.link(text, + email: true, + extra: true + ) == expected + end + + test "class attribute" do + assert Linkify.link("google.com", class: "linkified") == + "google.com" + end + + test "rel attribute" do + assert Linkify.link("google.com", rel: "noopener noreferrer") == + "google.com" + end + + test "rel as function" do + text = "google.com" + + expected = "google.com" + + custom_rel = fn url -> + url |> String.split(".") |> List.last() + end + + assert Linkify.link(text, rel: custom_rel) == expected + + text = "google.com" + + expected = "google.com" + + custom_rel = fn _ -> nil end + + assert Linkify.link(text, rel: custom_rel) == expected + end + + test "link_map/2" do + assert Linkify.link_map("google.com", []) == + {"google.com", []} + end + + describe "custom handlers" do + test "mentions handler" do + text = "hello @user, @valid_user and @invalid_user" + valid_users = ["user", "valid_user"] + + handler = fn "@" <> user = mention, buffer, _opts, acc -> + if Enum.member?(valid_users, user) do + link = ~s(#{mention}) + {link, %{acc | mentions: MapSet.put(acc.mentions, {mention, user})}} + else + {buffer, acc} + end + end + + {result_text, %{mentions: mentions}} = + Linkify.link_map(text, %{mentions: MapSet.new()}, + mention: true, + mention_handler: handler + ) + + assert result_text == + "hello @user, @valid_user and @invalid_user" + + assert mentions |> MapSet.to_list() |> Enum.map(&elem(&1, 1)) == valid_users + end + + test "hashtags handler" do + text = "#hello #world" + + handler = fn hashtag, buffer, opts, acc -> + link = Linkify.Builder.create_hashtag_link(hashtag, buffer, opts) + {link, %{acc | tags: MapSet.put(acc.tags, hashtag)}} + end + + {result_text, %{tags: tags}} = + Linkify.link_map(text, %{tags: MapSet.new()}, + hashtag: true, + hashtag_handler: handler, + hashtag_prefix: "https://example.com/user/", + rel: false + ) + + assert result_text == + "#hello #world" + + assert MapSet.to_list(tags) == ["#hello", "#world"] + end + + test "mention handler and hashtag prefix" do + text = + "Hello again, @user.<script></script>\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric" + + handler = fn "@" <> user = mention, _, _, _ -> + ~s(@#{mention}) + end + + expected = + ~s(Hello again, @@user.<script></script>\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric) + + assert Linkify.link(text, + mention: true, + mention_handler: handler, + hashtag: true, + hashtag_prefix: "/tag/", + new_window: true + ) == expected + end + end + + describe "mentions" do + test "simple mentions" do + expected = + ~s{hello @user and @anotherUser.} + + assert Linkify.link("hello @user and @anotherUser.", + mention: true, + mention_prefix: "https://example.com/user/", + new_window: true + ) == expected + end + + test "mentions inside html tags" do + text = + "hello world
\n<`em>another @user__test and @user__test google.com paragraph
\n" + + expected = + "hello world
\n<`em>another @user__test and @user__test google.com paragraph
\n" + + assert Linkify.link(text, mention: true, mention_prefix: "u/") == expected + end + + test "metion @user@example.com" do + text = "hey @user@example.com" + + expected = + "hey @user@example.com" + + assert Linkify.link(text, + mention: true, + mention_prefix: "https://example.com/user/", + new_window: true + ) == expected + end + end + + describe "hashtag links" do + test "hashtag" do + expected = + " one #2two three #four." + + assert Linkify.link(" one #2two three #four.", + hashtag: true, + hashtag_prefix: "https://example.com/tag/", + new_window: true + ) == expected + end + + test "must have non-numbers" do + expected = "#1ok #42 #7" + + assert Linkify.link("#1ok #42 #7", + hashtag: true, + hashtag_prefix: "/t/", + rel: false + ) == expected + end + + test "support French" do + text = "#administrateur·rice·s #ingénieur·e·s" + + expected = + "#administrateur·rice·s #ingénieur·e·s" + + assert Linkify.link(text, + hashtag: true, + hashtag_prefix: "/t/", + rel: false + ) == expected + end + + test "support Telugu" do + text = "#చక్రం #కకకకక్ #కకకకాక #కకకక్రకకకక" + + expected = + "#చక్రం #కకకకక్ #కకకకాక #కకకక్రకకకక" + + assert Linkify.link(text, + hashtag: true, + hashtag_prefix: "/t/", + rel: false + ) == expected + end + + test "do not turn urls with hashes into hashtags" do + text = "google.com#test #test google.com/#test #tag" + + expected = + "google.com#test #test google.com/#test #tag" + + assert Linkify.link(text, + hashtag: true, + rel: false, + hashtag_prefix: "https://example.com/tag/" + ) == expected + end + + test "works with non-latin characters" do + text = "#漢字 #は #тест #ทดสอบ" + + expected = + "#漢字 #は #тест #ทดสอบ" + + assert Linkify.link(text, + rel: false, + hashtag: true, + hashtag_prefix: "https://example.com/tag/" + ) == expected + end + end + + describe "links" do + test "turning urls into links" do + text = "Hey, check out http://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." + + expected = + "Hey, check out youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." + + assert Linkify.link(text, new_window: true) == expected + + # no scheme + text = "Hey, check out www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." + assert Linkify.link(text, new_window: true) == expected + end + + test "turn urls with schema into urls" do + text = "📌https://google.com" + expected = "📌google.com" + + assert Linkify.link(text, rel: false) == expected + end + + test "hostname/@user" do + text = "https://example.com/@user" + + expected = "example.com/@user" + + assert Linkify.link(text, new_window: true) == expected + + text = "https://example.com:4000/@user" + + expected = + "example.com:4000/@user" + + assert Linkify.link(text, new_window: true) == expected + + text = "https://example.com:4000/@user" + + expected = + "example.com:4000/@user" + + assert Linkify.link(text, new_window: true) == expected + + text = "@username" + expected = "@username" + assert Linkify.link(text, new_window: true) == expected + + text = "http://www.cs.vu.nl/~ast/intel/" + + expected = "cs.vu.nl/~ast/intel/" + + assert Linkify.link(text) == expected + + text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" + + expected = + "forum.zdoom.org/viewtopic.php?f=44&t=57087" + + assert Linkify.link(text) == expected + + text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" + + expected = + "en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" + + assert Linkify.link(text) == expected + + text = "https://en.wikipedia.org/wiki/Duff's_device" + + expected = + "en.wikipedia.org/wiki/Duff's_device" + + assert Linkify.link(text) == expected + end + end + + describe "non http links" do + test "xmpp" do + text = "xmpp:user@example.com" + + expected = "xmpp:user@example.com" + + assert Linkify.link(text, extra: true) == expected + end + + test "email" do + text = "user@example.com" + expected = "user@example.com" + assert Linkify.link(text, email: true) == expected + end + + test "magnet" do + text = + "magnet:?xt=urn:btih:a4104a9d2f5615601c429fe8bab8177c47c05c84&dn=ubuntu-18.04.1.0-live-server-amd64.iso&tr=http%3A%2F%2Ftorrent.ubuntu.com%3A6969%2Fannounce&tr=http%3A%2F%2Fipv6.torrent.ubuntu.com%3A6969%2Fannounce" + + expected = + "magnet:?xt=urn:btih:a4104a9d2f5615601c429fe8bab8177c47c05c84&dn=ubuntu-18.04.1.0-live-server-amd64.iso&tr=http%3A%2F%2Ftorrent.ubuntu.com%3A6969%2Fannounce&tr=http%3A%2F%2Fipv6.torrent.ubuntu.com%3A6969%2Fannounce" + + assert Linkify.link(text, extra: true) == expected + end + + test "dweb" do + text = + "dweb://584faa05d394190ab1a3f0240607f9bf2b7e2bd9968830a11cf77db0cea36a21+v1.0.0/path/to/file.txt" + + expected = + "dweb://584faa05d394190ab1a3f0240607f9bf2b7e2bd9968830a11cf77db0cea36a21+v1.0.0/path/to/file.txt" + + assert Linkify.link(text, extra: true) == expected + end + end + + describe "TLDs" do + test "parse with scheme" do + text = "https://google.com" + + expected = "google.com" + + assert Linkify.link(text) == expected + end + + test "only existing TLDs with scheme" do + text = "this url https://google.foobar.blah11blah/ has invalid TLD" + + expected = "this url https://google.foobar.blah11blah/ has invalid TLD" + assert Linkify.link(text) == expected + + text = "this url https://google.foobar.com/ has valid TLD" + + expected = + "this url google.foobar.com/ has valid TLD" + + assert Linkify.link(text) == expected + end + + test "only existing TLDs without scheme" do + text = "this url google.foobar.blah11blah/ has invalid TLD" + assert Linkify.link(text) == text + + text = "this url google.foobar.com/ has valid TLD" + + expected = + "this url google.foobar.com/ has valid TLD" + + assert Linkify.link(text) == expected + end + + test "only existing TLDs with and without scheme" do + text = "this url http://google.foobar.com/ has valid TLD" + + expected = + "this url google.foobar.com/ has valid TLD" + + assert Linkify.link(text) == expected + + text = "this url google.foobar.com/ has valid TLD" + + expected = + "this url google.foobar.com/ has valid TLD" + + assert Linkify.link(text) == expected + end + end +end diff --git a/test/parser_test.exs b/test/parser_test.exs index cb4f8fb..9b74235 100644 --- a/test/parser_test.exs +++ b/test/parser_test.exs @@ -1,8 +1,8 @@ -defmodule AutoLinker.ParserTest do +defmodule Linkify.ParserTest do use ExUnit.Case, async: true - doctest AutoLinker.Parser + doctest Linkify.Parser - import AutoLinker.Parser + import Linkify.Parser describe "url?/2" do test "valid scheme true" do @@ -106,28 +106,10 @@ defmodule AutoLinker.ParserTest do end end - describe "match_phone" do - test "valid" do - valid_phone_nunbers() - |> Enum.each(fn number -> - assert number |> match_phone() |> valid_number?(number) - end) - end - - test "invalid" do - invalid_phone_numbers() - |> Enum.each(fn number -> - assert number |> match_phone() |> is_nil - end) - end - end - describe "parse" do test "handle line breakes" do text = "google.com\r\nssss" - - expected = - "google.com\r\nssss" + expected = "google.com\r\nssss" assert parse(text) == expected end @@ -157,25 +139,20 @@ defmodule AutoLinker.ParserTest do expected = "" - assert parse(text, class: false, rel: false, new_window: false, phone: false) == expected + assert parse(text, class: false, rel: false) == expected text = "Check outgoogle.com
" expected = "" - assert parse(text, class: false, rel: false, new_window: false) == expected - end - - test "excludes html with specified class" do - text = "```Check out