text_formatter_spec.rb 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. require 'rails_helper'
  2. RSpec.describe TextFormatter do
  3. describe '#to_s' do
  4. let(:preloaded_accounts) { nil }
  5. subject { described_class.new(text, preloaded_accounts: preloaded_accounts).to_s }
  6. context 'given text containing plain text' do
  7. let(:text) { 'text' }
  8. it 'paragraphizes the text' do
  9. is_expected.to eq '<p>text</p>'
  10. end
  11. end
  12. context 'given text containing line feeds' do
  13. let(:text) { "line\nfeed" }
  14. it 'removes line feeds' do
  15. is_expected.not_to include "\n"
  16. end
  17. end
  18. context 'given text containing linkable mentions' do
  19. let(:preloaded_accounts) { [Fabricate(:account, username: 'alice')] }
  20. let(:text) { '@alice' }
  21. it 'creates a mention link' do
  22. is_expected.to include '<a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span>'
  23. end
  24. end
  25. context 'given text containing unlinkable mentions' do
  26. let(:preloaded_accounts) { [] }
  27. let(:text) { '@alice' }
  28. it 'does not create a mention link' do
  29. is_expected.to include '@alice'
  30. end
  31. end
  32. context 'given a stand-alone medium URL' do
  33. let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' }
  34. it 'matches the full URL' do
  35. is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
  36. end
  37. end
  38. context 'given a stand-alone google URL' do
  39. let(:text) { 'http://google.com' }
  40. it 'matches the full URL' do
  41. is_expected.to include 'href="http://google.com"'
  42. end
  43. end
  44. context 'given a stand-alone URL with a newer TLD' do
  45. let(:text) { 'http://example.gay' }
  46. it 'matches the full URL' do
  47. is_expected.to include 'href="http://example.gay"'
  48. end
  49. end
  50. context 'given a stand-alone IDN URL' do
  51. let(:text) { 'https://nic.みんな/' }
  52. it 'matches the full URL' do
  53. is_expected.to include 'href="https://nic.みんな/"'
  54. end
  55. it 'has display URL' do
  56. is_expected.to include '<span class="">nic.みんな/</span>'
  57. end
  58. end
  59. context 'given a URL with a trailing period' do
  60. let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' }
  61. it 'matches the full URL but not the period' do
  62. is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
  63. end
  64. end
  65. context 'given a URL enclosed with parentheses' do
  66. let(:text) { '(http://google.com/)' }
  67. it 'matches the full URL but not the parentheses' do
  68. is_expected.to include 'href="http://google.com/"'
  69. end
  70. end
  71. context 'given a URL with a trailing exclamation point' do
  72. let(:text) { 'http://www.google.com!' }
  73. it 'matches the full URL but not the exclamation point' do
  74. is_expected.to include 'href="http://www.google.com"'
  75. end
  76. end
  77. context 'given a URL with a trailing single quote' do
  78. let(:text) { "http://www.google.com'" }
  79. it 'matches the full URL but not the single quote' do
  80. is_expected.to include 'href="http://www.google.com"'
  81. end
  82. end
  83. context 'given a URL with a trailing angle bracket' do
  84. let(:text) { 'http://www.google.com>' }
  85. it 'matches the full URL but not the angle bracket' do
  86. is_expected.to include 'href="http://www.google.com"'
  87. end
  88. end
  89. context 'given a URL with a query string' do
  90. context 'with escaped unicode character' do
  91. let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
  92. it 'matches the full URL' do
  93. is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;q=autolink"'
  94. end
  95. end
  96. context 'with unicode character' do
  97. let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
  98. it 'matches the full URL' do
  99. is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&amp;q=autolink"'
  100. end
  101. end
  102. context 'with unicode character at the end' do
  103. let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
  104. it 'matches the full URL' do
  105. is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
  106. end
  107. end
  108. context 'with escaped and not escaped unicode characters' do
  109. let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
  110. it 'preserves escaped unicode characters' do
  111. is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;utf81=✓&amp;q=autolink"'
  112. end
  113. end
  114. end
  115. context 'given a URL with parentheses in it' do
  116. let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' }
  117. it 'matches the full URL' do
  118. is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
  119. end
  120. end
  121. context 'given a URL in quotation marks' do
  122. let(:text) { '"https://example.com/"' }
  123. it 'does not match the quotation marks' do
  124. is_expected.to include 'href="https://example.com/"'
  125. end
  126. end
  127. context 'given a URL in angle brackets' do
  128. let(:text) { '<https://example.com/>' }
  129. it 'does not match the angle brackets' do
  130. is_expected.to include 'href="https://example.com/"'
  131. end
  132. end
  133. context 'given a URL with Japanese path string' do
  134. let(:text) { 'https://ja.wikipedia.org/wiki/日本' }
  135. it 'matches the full URL' do
  136. is_expected.to include 'href="https://ja.wikipedia.org/wiki/日本"'
  137. end
  138. end
  139. context 'given a URL with Korean path string' do
  140. let(:text) { 'https://ko.wikipedia.org/wiki/대한민국' }
  141. it 'matches the full URL' do
  142. is_expected.to include 'href="https://ko.wikipedia.org/wiki/대한민국"'
  143. end
  144. end
  145. context 'given a URL with a full-width space' do
  146. let(:text) { 'https://example.com/ abc123' }
  147. it 'does not match the full-width space' do
  148. is_expected.to include 'href="https://example.com/"'
  149. end
  150. end
  151. context 'given a URL in Japanese quotation marks' do
  152. let(:text) { '「[https://example.org/」' }
  153. it 'does not match the quotation marks' do
  154. is_expected.to include 'href="https://example.org/"'
  155. end
  156. end
  157. context 'given a URL with Simplified Chinese path string' do
  158. let(:text) { 'https://baike.baidu.com/item/中华人民共和国' }
  159. it 'matches the full URL' do
  160. is_expected.to include 'href="https://baike.baidu.com/item/中华人民共和国"'
  161. end
  162. end
  163. context 'given a URL with Traditional Chinese path string' do
  164. let(:text) { 'https://zh.wikipedia.org/wiki/臺灣' }
  165. it 'matches the full URL' do
  166. is_expected.to include 'href="https://zh.wikipedia.org/wiki/臺灣"'
  167. end
  168. end
  169. context 'given a URL containing unsafe code (XSS attack, visible part)' do
  170. let(:text) { %q{http://example.com/b<del>b</del>} }
  171. it 'does not include the HTML in the URL' do
  172. is_expected.to include '"http://example.com/b"'
  173. end
  174. it 'escapes the HTML' do
  175. is_expected.to include '&lt;del&gt;b&lt;/del&gt;'
  176. end
  177. end
  178. context 'given a URL containing unsafe code (XSS attack, invisible part)' do
  179. let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
  180. it 'does not include the HTML in the URL' do
  181. is_expected.to include '"http://example.com/blahblahblahblah/a"'
  182. end
  183. it 'escapes the HTML' do
  184. is_expected.to include '&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;'
  185. end
  186. end
  187. context 'given text containing HTML code (script tag)' do
  188. let(:text) { '<script>alert("Hello")</script>' }
  189. it 'escapes the HTML' do
  190. is_expected.to include '<p>&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;</p>'
  191. end
  192. end
  193. context 'given text containing HTML (XSS attack)' do
  194. let(:text) { %q{<img src="javascript:alert('XSS');">} }
  195. it 'escapes the HTML' do
  196. is_expected.to include '<p>&lt;img src=&quot;javascript:alert(&#39;XSS&#39;);&quot;&gt;</p>'
  197. end
  198. end
  199. context 'given an invalid URL' do
  200. let(:text) { 'http://www\.google\.com' }
  201. it 'outputs the raw URL' do
  202. is_expected.to eq '<p>http://www\.google\.com</p>'
  203. end
  204. end
  205. context 'given text containing a hashtag' do
  206. let(:text) { '#hashtag' }
  207. it 'creates a hashtag link' do
  208. is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
  209. end
  210. end
  211. context 'given text containing a hashtag with Unicode chars' do
  212. let(:text) { '#hashtagタグ' }
  213. it 'creates a hashtag link' do
  214. is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
  215. end
  216. end
  217. context 'given text with a stand-alone xmpp: URI' do
  218. let(:text) { 'xmpp:user@instance.com' }
  219. it 'matches the full URI' do
  220. is_expected.to include 'href="xmpp:user@instance.com"'
  221. end
  222. end
  223. context 'given text with an xmpp: URI with a query-string' do
  224. let(:text) { 'please join xmpp:muc@instance.com?join right now' }
  225. it 'matches the full URI' do
  226. is_expected.to include 'href="xmpp:muc@instance.com?join"'
  227. end
  228. end
  229. context 'given text containing a magnet: URI' do
  230. let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' }
  231. it 'matches the full URI' do
  232. is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
  233. end
  234. end
  235. end
  236. end