twitter-text-kow 1.3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +40 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +44 -0
- data/Gemfile +4 -0
- data/LICENSE +188 -0
- data/README.md +193 -0
- data/Rakefile +52 -0
- data/config/README.md +142 -0
- data/config/v1.json +8 -0
- data/config/v2.json +29 -0
- data/config/v3.json +30 -0
- data/lib/assets/tld_lib.yml +1577 -0
- data/lib/twitter-text/autolink.rb +455 -0
- data/lib/twitter-text/configuration.rb +68 -0
- data/lib/twitter-text/deprecation.rb +21 -0
- data/lib/twitter-text/emoji_regex.rb +27 -0
- data/lib/twitter-text/extractor.rb +388 -0
- data/lib/twitter-text/hash_helper.rb +27 -0
- data/lib/twitter-text/hit_highlighter.rb +92 -0
- data/lib/twitter-text/regex.rb +381 -0
- data/lib/twitter-text/rewriter.rb +69 -0
- data/lib/twitter-text/unicode.rb +31 -0
- data/lib/twitter-text/validation.rb +251 -0
- data/lib/twitter-text/weighted_range.rb +24 -0
- data/lib/twitter-text.rb +29 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/autolinking_spec.rb +858 -0
- data/spec/configuration_spec.rb +136 -0
- data/spec/extractor_spec.rb +392 -0
- data/spec/hithighlighter_spec.rb +96 -0
- data/spec/regex_spec.rb +76 -0
- data/spec/rewriter_spec.rb +553 -0
- data/spec/spec_helper.rb +139 -0
- data/spec/test_urls.rb +90 -0
- data/spec/twitter_text_spec.rb +25 -0
- data/spec/unicode_spec.rb +35 -0
- data/spec/validation_spec.rb +87 -0
- data/test/conformance_test.rb +242 -0
- data/twitter-text.gemspec +35 -0
- metadata +228 -0
@@ -0,0 +1,858 @@
|
|
1
|
+
# Copyright 2018 Twitter, Inc.
|
2
|
+
# Licensed under the Apache License, Version 2.0
|
3
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
4
|
+
|
5
|
+
# encoding: utf-8
|
6
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
7
|
+
|
8
|
+
class TestAutolink
|
9
|
+
include Twitter::TwitterText::Autolink
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Twitter::TwitterText::Autolink do
|
13
|
+
def original_text; end
|
14
|
+
def url; end
|
15
|
+
|
16
|
+
describe "auto_link_custom" do
|
17
|
+
before do
|
18
|
+
@autolinked_text = TestAutolink.new.auto_link(original_text) if original_text
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "plain text" do
|
22
|
+
context "plain text with emoji" do
|
23
|
+
def original_text; "gotcha 👍"; end
|
24
|
+
|
25
|
+
it "should be unchanged" do
|
26
|
+
expect(@autolinked_text).to eq("gotcha 👍")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "username autolinking" do
|
32
|
+
context "username preceded by a space" do
|
33
|
+
def original_text; "hello @jacob"; end
|
34
|
+
|
35
|
+
it "should be linked" do
|
36
|
+
expect(@autolinked_text).to link_to_screen_name('jacob')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "username in camelCase" do
|
41
|
+
def original_text() "@jaCob iS cOoL" end
|
42
|
+
|
43
|
+
it "should be linked" do
|
44
|
+
expect(@autolinked_text).to link_to_screen_name('jaCob')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "username at beginning of line" do
|
49
|
+
def original_text; "@jacob you're cool"; end
|
50
|
+
|
51
|
+
it "should be linked" do
|
52
|
+
expect(@autolinked_text).to link_to_screen_name('jacob')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "username preceded by word character" do
|
57
|
+
def original_text; "meet@the beach"; end
|
58
|
+
|
59
|
+
it "should not be linked" do
|
60
|
+
expect(Nokogiri::HTML(@autolinked_text).search('a')).to be_empty
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "username preceded by non-word character" do
|
65
|
+
def original_text; "great.@jacob"; end
|
66
|
+
|
67
|
+
it "should be linked" do
|
68
|
+
expect(@autolinked_text).to link_to_screen_name('jacob')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "username containing non-word characters" do
|
73
|
+
def original_text; "@zach&^$%^"; end
|
74
|
+
|
75
|
+
it "should not be linked" do
|
76
|
+
expect(@autolinked_text).to link_to_screen_name('zach')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "username over twenty characters" do
|
81
|
+
def original_text
|
82
|
+
@twenty_character_username = "zach" * 5
|
83
|
+
"@" + @twenty_character_username + "1"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should not be linked" do
|
87
|
+
expect(@autolinked_text).to link_to_screen_name(@twenty_character_username)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "username followed by japanese" do
|
92
|
+
def original_text; "@jacobの"; end
|
93
|
+
|
94
|
+
it "should be linked" do
|
95
|
+
expect(@autolinked_text).to link_to_screen_name('jacob')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "username preceded by japanese" do
|
100
|
+
def original_text; "あ@matz"; end
|
101
|
+
|
102
|
+
it "should be linked" do
|
103
|
+
expect(@autolinked_text).to link_to_screen_name('matz')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "username surrounded by japanese" do
|
108
|
+
def original_text; "あ@yoshimiの"; end
|
109
|
+
|
110
|
+
it "should be linked" do
|
111
|
+
expect(@autolinked_text).to link_to_screen_name('yoshimi')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "username using full-width at-sign" do
|
116
|
+
def original_text
|
117
|
+
"#{[0xFF20].pack('U')}jacob"
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should be linked" do
|
121
|
+
expect(@autolinked_text).to link_to_screen_name('jacob')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "list path autolinking" do
|
127
|
+
|
128
|
+
context "when List is not available" do
|
129
|
+
it "should not be linked" do
|
130
|
+
@autolinked_text = TestAutolink.new.auto_link_usernames_or_lists("hello @jacob/my-list", :suppress_lists => true)
|
131
|
+
expect(@autolinked_text).to_not link_to_list_path('jacob/my-list')
|
132
|
+
expect(@autolinked_text).to include('my-list')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "slug preceded by a space" do
|
137
|
+
def original_text; "hello @jacob/my-list"; end
|
138
|
+
|
139
|
+
it "should be linked" do
|
140
|
+
expect(@autolinked_text).to link_to_list_path('jacob/my-list')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "username followed by a slash but no list" do
|
145
|
+
def original_text; "hello @jacob/ my-list"; end
|
146
|
+
|
147
|
+
it "should NOT be linked" do
|
148
|
+
expect(@autolinked_text).to_not link_to_list_path('jacob/my-list')
|
149
|
+
expect(@autolinked_text).to link_to_screen_name('jacob')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "empty username followed by a list" do
|
154
|
+
def original_text; "hello @/my-list"; end
|
155
|
+
|
156
|
+
it "should NOT be linked" do
|
157
|
+
expect(Nokogiri::HTML(@autolinked_text).search('a')).to be_empty
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "list slug at beginning of line" do
|
162
|
+
def original_text; "@jacob/my-list"; end
|
163
|
+
|
164
|
+
it "should be linked" do
|
165
|
+
expect(@autolinked_text).to link_to_list_path('jacob/my-list')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "username preceded by alpha-numeric character" do
|
170
|
+
def original_text; "meet@the/beach"; end
|
171
|
+
|
172
|
+
it "should not be linked" do
|
173
|
+
expect(Nokogiri::HTML(@autolinked_text).search('a')).to be_empty
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "username preceded by non-word character" do
|
178
|
+
def original_text; "great.@jacob/my-list"; end
|
179
|
+
|
180
|
+
it "should be linked" do
|
181
|
+
@autolinked_text = TestAutolink.new.auto_link("great.@jacob/my-list")
|
182
|
+
expect(@autolinked_text).to link_to_list_path('jacob/my-list')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context "username containing non-word characters" do
|
187
|
+
def original_text; "@zach/test&^$%^"; end
|
188
|
+
|
189
|
+
it "should be linked" do
|
190
|
+
expect(@autolinked_text).to link_to_list_path('zach/test')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "username over twenty characters" do
|
195
|
+
def original_text
|
196
|
+
@twentyfive_character_list = "jack/" + ("a" * 25)
|
197
|
+
"@#{@twentyfive_character_list}12345"
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should be linked" do
|
201
|
+
expect(@autolinked_text).to link_to_list_path(@twentyfive_character_list)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "hashtag autolinking" do
|
207
|
+
context "with an all numeric hashtag" do
|
208
|
+
def original_text; "#123"; end
|
209
|
+
|
210
|
+
it "should not be linked" do
|
211
|
+
expect(@autolinked_text).to_not have_autolinked_hashtag('#123')
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context "with a hashtag with alphanumeric characters" do
|
216
|
+
def original_text; "#ab1d"; end
|
217
|
+
|
218
|
+
it "should be linked" do
|
219
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#ab1d')
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context "with a hashtag with underscores" do
|
224
|
+
def original_text; "#a_b_c_d"; end
|
225
|
+
|
226
|
+
it "should be linked" do
|
227
|
+
expect(@autolinked_text).to have_autolinked_hashtag(original_text)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "with a hashtag that is preceded by a word character" do
|
232
|
+
def original_text; "ab#cd"; end
|
233
|
+
|
234
|
+
it "should not be linked" do
|
235
|
+
expect(@autolinked_text).to_not have_autolinked_hashtag(original_text)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context "with a page anchor in a url" do
|
240
|
+
def original_text; "Here's my url: http://foobar.com/#home"; end
|
241
|
+
|
242
|
+
it "should not link the hashtag" do
|
243
|
+
expect(@autolinked_text).to_not have_autolinked_hashtag('#home')
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should link the url" do
|
247
|
+
expect(@autolinked_text).to have_autolinked_url('http://foobar.com/#home')
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "with a hashtag that starts with a number but has word characters" do
|
252
|
+
def original_text; "#2ab"; end
|
253
|
+
|
254
|
+
it "should be linked" do
|
255
|
+
expect(@autolinked_text).to have_autolinked_hashtag(original_text)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context "with multiple valid hashtags" do
|
260
|
+
def original_text; "I'm frickin' awesome #ab #cd #ef"; end
|
261
|
+
|
262
|
+
it "links each hashtag" do
|
263
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#ab')
|
264
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#cd')
|
265
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#ef')
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context "with a hashtag preceded by a ." do
|
270
|
+
def original_text; "ok, great.#abc"; end
|
271
|
+
|
272
|
+
it "should be linked" do
|
273
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#abc')
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context "with a hashtag preceded by a &" do
|
278
|
+
def original_text; "&#nbsp;"; end
|
279
|
+
|
280
|
+
it "should not be linked" do
|
281
|
+
expect(@autolinked_text).to_not have_autolinked_hashtag('#nbsp;')
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "with a hashtag that ends in an !" do
|
286
|
+
def original_text; "#great!"; end
|
287
|
+
|
288
|
+
it "should be linked, but should not include the !" do
|
289
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#great')
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context "with a hashtag followed by Japanese" do
|
294
|
+
def original_text; "#twj_devの"; end
|
295
|
+
|
296
|
+
it "should be linked" do
|
297
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#twj_devの')
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
context "with a hashtag preceded by a full-width space" do
|
302
|
+
def original_text; "#{[0x3000].pack('U')}#twj_dev"; end
|
303
|
+
|
304
|
+
it "should be linked" do
|
305
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#twj_dev')
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "with a hashtag followed by a full-width space" do
|
310
|
+
def original_text; "#twj_dev#{[0x3000].pack('U')}"; end
|
311
|
+
|
312
|
+
it "should be linked" do
|
313
|
+
expect(@autolinked_text).to have_autolinked_hashtag('#twj_dev')
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context "with a hashtag using full-width hash" do
|
318
|
+
def original_text; "#{[0xFF03].pack('U')}twj_dev"; end
|
319
|
+
|
320
|
+
it "should be linked" do
|
321
|
+
link = Nokogiri::HTML(@autolinked_text).search('a')
|
322
|
+
expect((link.inner_text.respond_to?(:force_encoding) ? link.inner_text.force_encoding("utf-8") : link.inner_text)).to be == "#{[0xFF03].pack('U')}twj_dev"
|
323
|
+
expect(link.first['href']).to be == 'https://twitter.com/search?q=%23twj_dev'
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
context "with a hashtag containing an accented latin character" do
|
328
|
+
def original_text
|
329
|
+
# the hashtag is #éhashtag
|
330
|
+
"##{[0x00e9].pack('U')}hashtag"
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should be linked" do
|
334
|
+
expect(@autolinked_text).to be == "<a class=\"tweet-url hashtag\" href=\"https://twitter.com/search?q=%23éhashtag\" rel=\"nofollow\" title=\"#éhashtag\">#éhashtag</a>"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
describe "URL autolinking" do
|
341
|
+
def url; "http://www.google.com"; end
|
342
|
+
|
343
|
+
context "when embedded in plain text" do
|
344
|
+
def original_text; "On my search engine #{url} I found good links."; end
|
345
|
+
|
346
|
+
it "should be linked" do
|
347
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context "when surrounded by Japanese;" do
|
352
|
+
def original_text; "いまなにしてる#{url}いまなにしてる"; end
|
353
|
+
|
354
|
+
it "should be linked" do
|
355
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
context "with a path surrounded by parentheses;" do
|
360
|
+
def original_text; "I found a neatness (#{url})"; end
|
361
|
+
|
362
|
+
it "should be linked" do
|
363
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
364
|
+
end
|
365
|
+
|
366
|
+
context "when the URL ends with a slash;" do
|
367
|
+
def url; "http://www.google.com/"; end
|
368
|
+
|
369
|
+
it "should be linked" do
|
370
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
context "when the URL has a path;" do
|
375
|
+
def url; "http://www.google.com/fsdfasdf"; end
|
376
|
+
|
377
|
+
it "should be linked" do
|
378
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context "when path contains parens" do
|
384
|
+
def original_text; "I found a neatness (#{url})"; end
|
385
|
+
|
386
|
+
it "should be linked" do
|
387
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
388
|
+
end
|
389
|
+
|
390
|
+
context "wikipedia" do
|
391
|
+
def url; "http://en.wikipedia.org/wiki/Madonna_(artist)"; end
|
392
|
+
|
393
|
+
it "should be linked" do
|
394
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context "IIS session" do
|
399
|
+
def url; "http://msdn.com/S(deadbeef)/page.htm"; end
|
400
|
+
|
401
|
+
it "should be linked" do
|
402
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
context "unbalanced parens" do
|
407
|
+
def url; "http://example.com/i_has_a_("; end
|
408
|
+
|
409
|
+
it "should be linked" do
|
410
|
+
expect(@autolinked_text).to have_autolinked_url("http://example.com/i_has_a_")
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
context "balanced parens with a double quote inside" do
|
415
|
+
def url; "http://foo.com/foo_(\")_bar" end
|
416
|
+
|
417
|
+
it "should be linked" do
|
418
|
+
expect(@autolinked_text).to have_autolinked_url("http://foo.com/foo_")
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context "balanced parens hiding XSS" do
|
423
|
+
def url; 'http://x.xx.com/("style="color:red"onmouseover="alert(1)' end
|
424
|
+
|
425
|
+
it "should be linked" do
|
426
|
+
expect(@autolinked_text).to have_autolinked_url("http://x.xx.com/")
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
context "when preceded by a :" do
|
432
|
+
def original_text; "Check this out @hoverbird:#{url}"; end
|
433
|
+
|
434
|
+
it "should be linked" do
|
435
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context "with a URL ending in allowed punctuation" do
|
440
|
+
it "does not consume ending punctuation" do
|
441
|
+
matcher = TestAutolink.new
|
442
|
+
%w| ? ! , . : ; ] ) } = \ ' |.each do |char|
|
443
|
+
expect(matcher.auto_link("#{url}#{char}")).to have_autolinked_url(url)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
context "with a URL preceded in forbidden characters" do
|
449
|
+
it "should be linked" do
|
450
|
+
matcher = TestAutolink.new
|
451
|
+
%w| \ ' / ! = |.each do |char|
|
452
|
+
expect(matcher.auto_link("#{char}#{url}")).to have_autolinked_url(url)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
context "when embedded in a link tag" do
|
458
|
+
def original_text; "<link rel='true'>#{url}</link>"; end
|
459
|
+
|
460
|
+
it "should be linked" do
|
461
|
+
expect(@autolinked_text).to have_autolinked_url(url)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
context "with multiple URLs" do
|
466
|
+
def original_text; "http://www.links.org link at start of page, link at end http://www.foo.org"; end
|
467
|
+
|
468
|
+
it "should autolink each one" do
|
469
|
+
expect(@autolinked_text).to have_autolinked_url('http://www.links.org')
|
470
|
+
expect(@autolinked_text).to have_autolinked_url('http://www.foo.org')
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
context "with multiple URLs in different formats" do
|
475
|
+
def original_text; "http://foo.com https://bar.com http://mail.foobar.org"; end
|
476
|
+
|
477
|
+
it "should autolink each one, in the proper order" do
|
478
|
+
expect(@autolinked_text).to have_autolinked_url('http://foo.com')
|
479
|
+
expect(@autolinked_text).to have_autolinked_url('https://bar.com')
|
480
|
+
expect(@autolinked_text).to have_autolinked_url('http://mail.foobar.org')
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
context "with a URL having a long TLD" do
|
485
|
+
def original_text; "Yahoo integriert Facebook http://golem.mobi/0912/71607.html"; end
|
486
|
+
|
487
|
+
it "should autolink it" do
|
488
|
+
expect(@autolinked_text).to have_autolinked_url('http://golem.mobi/0912/71607.html')
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
context "with a url lacking the protocol" do
|
493
|
+
def original_text; "I like www.foobar.com dudes"; end
|
494
|
+
|
495
|
+
it "does not link at all" do
|
496
|
+
link = Nokogiri::HTML(@autolinked_text).search('a')
|
497
|
+
expect(link).to be_empty
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
context "with a @ in a URL" do
|
502
|
+
context "with XSS attack" do
|
503
|
+
def original_text; 'http://x.xx.com/@"style="color:pink"onmouseover=alert(1)//'; end
|
504
|
+
|
505
|
+
it "should not allow XSS follwing @" do
|
506
|
+
expect(@autolinked_text).to have_autolinked_url('http://x.xx.com/')
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
context "with a username not followed by a /" do
|
511
|
+
def original_text; 'http://example.com/@foobar'; end
|
512
|
+
|
513
|
+
it "should link url" do
|
514
|
+
expect(@autolinked_text).to have_autolinked_url('http://example.com/@foobar')
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
context "with a username followed by a /" do
|
519
|
+
def original_text; 'http://example.com/@foobar/'; end
|
520
|
+
|
521
|
+
it "should not link the username but link full url" do
|
522
|
+
expect(@autolinked_text).to have_autolinked_url('http://example.com/@foobar/')
|
523
|
+
expect(@autolinked_text).to_not link_to_screen_name('foobar')
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
context "regex engine quirks" do
|
529
|
+
context "does not spiral out of control on repeated periods" do
|
530
|
+
def original_text; "Test a ton of periods http://example.com/path.........................................."; end
|
531
|
+
|
532
|
+
it "should autolink" do
|
533
|
+
expect(@autolinked_text).to have_autolinked_url('http://example.com/path')
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
context "does not spiral out of control on repeated dashes" do
|
538
|
+
def original_text; "Single char file ext http://www.bestbuy.com/site/Currie+Technologies+-+Ezip+400+Scooter/9885188.p?id=1218189013070&skuId=9885188"; end
|
539
|
+
|
540
|
+
it "should autolink" do
|
541
|
+
expect(@autolinked_text).to have_autolinked_url('http://www.bestbuy.com/site/Currie+Technologies+-+Ezip+400+Scooter/9885188.p?id=1218189013070&skuId=9885188')
|
542
|
+
end
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
end
|
547
|
+
|
548
|
+
describe "Autolink all" do
|
549
|
+
before do
|
550
|
+
@linker = TestAutolink.new
|
551
|
+
end
|
552
|
+
|
553
|
+
it "should allow url/hashtag overlap" do
|
554
|
+
auto_linked = @linker.auto_link("https://twitter.com/#search")
|
555
|
+
expect(auto_linked).to have_autolinked_url('https://twitter.com/#search')
|
556
|
+
end
|
557
|
+
|
558
|
+
it "should not add invalid option in HTML tags" do
|
559
|
+
auto_linked = @linker.auto_link("https://twitter.com/ is a URL, not a hashtag", :hashtag_class => 'hashtag_classname')
|
560
|
+
expect(auto_linked).to have_autolinked_url('https://twitter.com/')
|
561
|
+
expect(auto_linked).to_not include('hashtag_class')
|
562
|
+
expect(auto_linked).to_not include('hashtag_classname')
|
563
|
+
end
|
564
|
+
|
565
|
+
it "should autolink url/hashtag/mention in text with Unicode supplementary characters" do
|
566
|
+
auto_linked = @linker.auto_link("#{[0x10400].pack('U')} #hashtag #{[0x10400].pack('U')} @mention #{[0x10400].pack('U')} http://twitter.com/")
|
567
|
+
expect(auto_linked).to have_autolinked_hashtag('#hashtag')
|
568
|
+
expect(auto_linked).to link_to_screen_name('mention')
|
569
|
+
expect(auto_linked).to have_autolinked_url('http://twitter.com/')
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
end
|
574
|
+
|
575
|
+
describe "autolinking options" do
|
576
|
+
before do
|
577
|
+
@linker = TestAutolink.new
|
578
|
+
end
|
579
|
+
|
580
|
+
it "should show display_url when :url_entities provided" do
|
581
|
+
linked = @linker.auto_link("http://t.co/0JG5Mcq", :url_entities => [{
|
582
|
+
"url" => "http://t.co/0JG5Mcq",
|
583
|
+
"display_url" => "blog.twitter.com/2011/05/twitte…",
|
584
|
+
"expanded_url" => "http://blog.twitter.com/2011/05/twitter-for-mac-update.html",
|
585
|
+
"indices" => [
|
586
|
+
84,
|
587
|
+
103
|
588
|
+
]
|
589
|
+
}])
|
590
|
+
html = Nokogiri::HTML(linked)
|
591
|
+
expect(html.search('a')).to_not be_empty
|
592
|
+
expect(html.search('a[@href="http://t.co/0JG5Mcq"]')).to_not be_empty
|
593
|
+
expect(html.search('span[@class=js-display-url]').inner_text).to be == "blog.twitter.com/2011/05/twitte"
|
594
|
+
expect(html.inner_text).to be == " http://blog.twitter.com/2011/05/twitter-for-mac-update.html …"
|
595
|
+
expect(html.search('span[@style="position:absolute;left:-9999px;"]').size).to be == 4
|
596
|
+
end
|
597
|
+
|
598
|
+
it "should accept invisible_tag_attrs option" do
|
599
|
+
linked = @linker.auto_link("http://t.co/0JG5Mcq",
|
600
|
+
{
|
601
|
+
:url_entities => [{
|
602
|
+
"url" => "http://t.co/0JG5Mcq",
|
603
|
+
"display_url" => "blog.twitter.com/2011/05/twitte…",
|
604
|
+
"expanded_url" => "http://blog.twitter.com/2011/05/twitter-for-mac-update.html",
|
605
|
+
"indices" => [
|
606
|
+
0,
|
607
|
+
19
|
608
|
+
]
|
609
|
+
}],
|
610
|
+
:invisible_tag_attrs => "style='dummy;'"
|
611
|
+
})
|
612
|
+
html = Nokogiri::HTML(linked)
|
613
|
+
expect(html.search('span[@style="dummy;"]').size).to be == 4
|
614
|
+
end
|
615
|
+
|
616
|
+
it "should show display_url if available in entity" do
|
617
|
+
linked = @linker.auto_link_entities("http://t.co/0JG5Mcq",
|
618
|
+
[{
|
619
|
+
:url => "http://t.co/0JG5Mcq",
|
620
|
+
:display_url => "blog.twitter.com/2011/05/twitte…",
|
621
|
+
:expanded_url => "http://blog.twitter.com/2011/05/twitter-for-mac-update.html",
|
622
|
+
:indices => [0, 19]
|
623
|
+
}]
|
624
|
+
)
|
625
|
+
html = Nokogiri::HTML(linked)
|
626
|
+
expect(html.search('a')).to_not be_empty
|
627
|
+
expect(html.search('a[@href="http://t.co/0JG5Mcq"]')).to_not be_empty
|
628
|
+
expect(html.search('span[@class=js-display-url]').inner_text).to be == "blog.twitter.com/2011/05/twitte"
|
629
|
+
expect(html.inner_text).to be == " http://blog.twitter.com/2011/05/twitter-for-mac-update.html …"
|
630
|
+
end
|
631
|
+
|
632
|
+
it "should apply :class as a CSS class" do
|
633
|
+
linked = @linker.auto_link("http://example.com/", :class => 'myclass')
|
634
|
+
expect(linked).to have_autolinked_url('http://example.com/')
|
635
|
+
expect(linked).to match(/myclass/)
|
636
|
+
end
|
637
|
+
|
638
|
+
it "should apply :url_class only on URL" do
|
639
|
+
linked = @linker.auto_link("http://twitter.com")
|
640
|
+
expect(linked).to have_autolinked_url('http://twitter.com')
|
641
|
+
expect(expect(linked)).to_not match(/class/)
|
642
|
+
|
643
|
+
linked = @linker.auto_link("http://twitter.com", :url_class => 'testClass')
|
644
|
+
expect(linked).to have_autolinked_url('http://twitter.com')
|
645
|
+
expect(linked).to match(/class=\"testClass\"/)
|
646
|
+
|
647
|
+
linked = @linker.auto_link("#hash @tw", :url_class => 'testClass')
|
648
|
+
expect(linked).to match(/class=\"tweet-url hashtag\"/)
|
649
|
+
expect(linked).to match(/class=\"tweet-url username\"/)
|
650
|
+
expect(linked).to_not match(/class=\"testClass\"/)
|
651
|
+
end
|
652
|
+
|
653
|
+
it "should add rel=nofollow by default" do
|
654
|
+
linked = @linker.auto_link("http://example.com/")
|
655
|
+
expect(linked).to have_autolinked_url('http://example.com/')
|
656
|
+
expect(linked).to match(/nofollow/)
|
657
|
+
end
|
658
|
+
|
659
|
+
it "should include the '@' symbol in a username when passed :username_include_symbol" do
|
660
|
+
linked = @linker.auto_link("@user", :username_include_symbol => true)
|
661
|
+
expect(linked).to link_to_screen_name('user', '@user')
|
662
|
+
end
|
663
|
+
|
664
|
+
it "should include the '@' symbol in a list when passed :username_include_symbol" do
|
665
|
+
linked = @linker.auto_link("@user/list", :username_include_symbol => true)
|
666
|
+
expect(linked).to link_to_list_path('user/list', '@user/list')
|
667
|
+
end
|
668
|
+
|
669
|
+
it "should not add rel=nofollow when passed :suppress_no_follow" do
|
670
|
+
linked = @linker.auto_link("http://example.com/", :suppress_no_follow => true)
|
671
|
+
expect(linked).to have_autolinked_url('http://example.com/')
|
672
|
+
expect(linked).to_not match(/nofollow/)
|
673
|
+
end
|
674
|
+
|
675
|
+
it "should not add a target attribute by default" do
|
676
|
+
linked = @linker.auto_link("http://example.com/")
|
677
|
+
expect(linked).to have_autolinked_url('http://example.com/')
|
678
|
+
expect(linked).to_not match(/target=/)
|
679
|
+
end
|
680
|
+
|
681
|
+
it "should respect the :target option" do
|
682
|
+
linked = @linker.auto_link("http://example.com/", :target => 'mywindow')
|
683
|
+
expect(linked).to have_autolinked_url('http://example.com/')
|
684
|
+
expect(linked).to match(/target="mywindow"/)
|
685
|
+
end
|
686
|
+
|
687
|
+
it "should customize href by username_url_block option" do
|
688
|
+
linked = @linker.auto_link("@test", :username_url_block => lambda{|a| "dummy"})
|
689
|
+
expect(linked).to have_autolinked_url('dummy', 'test')
|
690
|
+
end
|
691
|
+
|
692
|
+
it "should customize href by list_url_block option" do
|
693
|
+
linked = @linker.auto_link("@test/list", :list_url_block => lambda{|a| "dummy"})
|
694
|
+
expect(linked).to have_autolinked_url('dummy', 'test/list')
|
695
|
+
end
|
696
|
+
|
697
|
+
it "should customize href by hashtag_url_block option" do
|
698
|
+
linked = @linker.auto_link("#hashtag", :hashtag_url_block => lambda{|a| "dummy"})
|
699
|
+
expect(linked).to have_autolinked_url('dummy', '#hashtag')
|
700
|
+
end
|
701
|
+
|
702
|
+
it "should customize href by cashtag_url_block option" do
|
703
|
+
linked = @linker.auto_link("$CASH", :cashtag_url_block => lambda{|a| "dummy"})
|
704
|
+
expect(linked).to have_autolinked_url('dummy', '$CASH')
|
705
|
+
end
|
706
|
+
|
707
|
+
it "should customize href by link_url_block option" do
|
708
|
+
linked = @linker.auto_link("http://example.com/", :link_url_block => lambda{|a| "dummy"})
|
709
|
+
expect(linked).to have_autolinked_url('dummy', 'http://example.com/')
|
710
|
+
end
|
711
|
+
|
712
|
+
it "should modify link attributes by link_attribute_block" do
|
713
|
+
linked = @linker.auto_link("#hash @mention",
|
714
|
+
:link_attribute_block => lambda{|entity, attributes|
|
715
|
+
attributes[:"dummy-hash-attr"] = "test" if entity[:hashtag]
|
716
|
+
}
|
717
|
+
)
|
718
|
+
expect(linked).to match(/<a[^>]+hashtag[^>]+dummy-hash-attr=\"test\"[^>]+>/)
|
719
|
+
expect(linked).to_not match(/<a[^>]+username[^>]+dummy-hash-attr=\"test\"[^>]+>/)
|
720
|
+
expect(linked).to_not match(/link_attribute_block/i)
|
721
|
+
|
722
|
+
linked = @linker.auto_link("@mention http://twitter.com/",
|
723
|
+
:link_attribute_block => lambda{|entity, attributes|
|
724
|
+
attributes["dummy-url-attr"] = entity[:url] if entity[:url]
|
725
|
+
}
|
726
|
+
)
|
727
|
+
expect(linked).to_not match(/<a[^>]+username[^>]+dummy-url-attr=\"http:\/\/twitter.com\/\"[^>]*>/)
|
728
|
+
expect(linked).to match(/<a[^>]+dummy-url-attr=\"http:\/\/twitter.com\/\"/)
|
729
|
+
end
|
730
|
+
|
731
|
+
it "should modify link text by link_text_block" do
|
732
|
+
linked = @linker.auto_link("#hash @mention",
|
733
|
+
:link_text_block => lambda{|entity, text|
|
734
|
+
entity[:hashtag] ? "#replaced" : "pre_#{text}_post"
|
735
|
+
}
|
736
|
+
)
|
737
|
+
expect(linked).to match(/<a[^>]+>#replaced<\/a>/)
|
738
|
+
expect(linked).to match(/<a[^>]+>pre_mention_post<\/a>/)
|
739
|
+
|
740
|
+
linked = @linker.auto_link("#hash @mention", {
|
741
|
+
:link_text_block => lambda{|entity, text|
|
742
|
+
"pre_#{text}_post"
|
743
|
+
},
|
744
|
+
:symbol_tag => "s", :text_with_symbol_tag => "b", :username_include_symbol => true
|
745
|
+
})
|
746
|
+
expect(linked).to match(/<a[^>]+>pre_<s>#<\/s><b>hash<\/b>_post<\/a>/)
|
747
|
+
expect(linked).to match(/<a[^>]+>pre_<s>@<\/s><b>mention<\/b>_post<\/a>/)
|
748
|
+
end
|
749
|
+
|
750
|
+
it "should apply :url_target only to auto-linked URLs" do
|
751
|
+
auto_linked = @linker.auto_link("#hashtag @mention http://test.com/", {:url_target => '_blank'})
|
752
|
+
expect(auto_linked).to have_autolinked_hashtag('#hashtag')
|
753
|
+
expect(auto_linked).to link_to_screen_name('mention')
|
754
|
+
expect(auto_linked).to have_autolinked_url('http://test.com/')
|
755
|
+
expect(auto_linked).to_not match(/<a[^>]+hashtag[^>]+target[^>]+>/)
|
756
|
+
expect(auto_linked).to_not match(/<a[^>]+username[^>]+target[^>]+>/)
|
757
|
+
expect(auto_linked).to match(/<a[^>]+test.com[^>]+target=\"_blank\"[^>]*>/)
|
758
|
+
end
|
759
|
+
|
760
|
+
it "should apply target='_blank' only to auto-linked URLs when :target_blank is set to true" do
|
761
|
+
auto_linked = @linker.auto_link("#hashtag @mention http://test.com/", {:target_blank => true})
|
762
|
+
expect(auto_linked).to have_autolinked_hashtag('#hashtag')
|
763
|
+
expect(auto_linked).to link_to_screen_name('mention')
|
764
|
+
expect(auto_linked).to have_autolinked_url('http://test.com/')
|
765
|
+
expect(auto_linked).to match(/<a[^>]+hashtag[^>]+target=\"_blank\"[^>]*>/)
|
766
|
+
expect(auto_linked).to match(/<a[^>]+username[^>]+target=\"_blank\"[^>]*>/)
|
767
|
+
expect(auto_linked).to match(/<a[^>]+test.com[^>]+target=\"_blank\"[^>]*>/)
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
describe "link_url_with_entity" do
|
772
|
+
before do
|
773
|
+
@linker = TestAutolink.new
|
774
|
+
end
|
775
|
+
|
776
|
+
it "should use display_url and expanded_url" do
|
777
|
+
expect(@linker.send(:link_url_with_entity,
|
778
|
+
{
|
779
|
+
:url => "http://t.co/abcde",
|
780
|
+
:display_url => "twitter.com",
|
781
|
+
:expanded_url => "http://twitter.com/"},
|
782
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'")).to be == "<span class='tco-ellipsis'><span class='invisible'> </span></span><span class='invisible'>http://</span><span class='js-display-url'>twitter.com</span><span class='invisible'>/</span><span class='tco-ellipsis'><span class='invisible'> </span></span>";
|
783
|
+
end
|
784
|
+
|
785
|
+
it "should correctly handle display_url ending with '…'" do
|
786
|
+
expect(@linker.send(:link_url_with_entity,
|
787
|
+
{
|
788
|
+
:url => "http://t.co/abcde",
|
789
|
+
:display_url => "twitter.com…",
|
790
|
+
:expanded_url => "http://twitter.com/abcdefg"},
|
791
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'")).to be == "<span class='tco-ellipsis'><span class='invisible'> </span></span><span class='invisible'>http://</span><span class='js-display-url'>twitter.com</span><span class='invisible'>/abcdefg</span><span class='tco-ellipsis'><span class='invisible'> </span>…</span>";
|
792
|
+
end
|
793
|
+
|
794
|
+
it "should correctly handle display_url starting with '…'" do
|
795
|
+
expect(@linker.send(:link_url_with_entity,
|
796
|
+
{
|
797
|
+
:url => "http://t.co/abcde",
|
798
|
+
:display_url => "…tter.com/abcdefg",
|
799
|
+
:expanded_url => "http://twitter.com/abcdefg"},
|
800
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'")).to be == "<span class='tco-ellipsis'>…<span class='invisible'> </span></span><span class='invisible'>http://twi</span><span class='js-display-url'>tter.com/abcdefg</span><span class='invisible'></span><span class='tco-ellipsis'><span class='invisible'> </span></span>";
|
801
|
+
end
|
802
|
+
|
803
|
+
it "should not create spans if display_url and expanded_url are on different domains" do
|
804
|
+
expect(@linker.send(:link_url_with_entity,
|
805
|
+
{
|
806
|
+
:url => "http://t.co/abcde",
|
807
|
+
:display_url => "pic.twitter.com/xyz",
|
808
|
+
:expanded_url => "http://twitter.com/foo/statuses/123/photo/1"},
|
809
|
+
{:invisible_tag_attrs => "class='invisible'"}).gsub('"', "'")).to be == "pic.twitter.com/xyz"
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
describe "symbol_tag" do
|
814
|
+
before do
|
815
|
+
@linker = TestAutolink.new
|
816
|
+
end
|
817
|
+
it "should put :symbol_tag around symbol" do
|
818
|
+
expect(@linker.auto_link("@mention", {:symbol_tag => 's', :username_include_symbol=>true})).to match(/<s>@<\/s>mention/)
|
819
|
+
expect(@linker.auto_link("#hash", {:symbol_tag => 's'})).to match(/<s>#<\/s>hash/)
|
820
|
+
result = @linker.auto_link("@mention #hash $CASH", {:symbol_tag => 'b', :username_include_symbol=>true})
|
821
|
+
expect(result).to match(/<b>@<\/b>mention/)
|
822
|
+
expect(result).to match(/<b>#<\/b>hash/)
|
823
|
+
expect(result).to match(/<b>\$<\/b>CASH/)
|
824
|
+
end
|
825
|
+
it "should put :text_with_symbol_tag around text" do
|
826
|
+
result = @linker.auto_link("@mention #hash $CASH", {:text_with_symbol_tag => 'b'})
|
827
|
+
expect(result).to match(/<b>mention<\/b>/)
|
828
|
+
expect(result).to match(/<b>hash<\/b>/)
|
829
|
+
expect(result).to match(/<b>CASH<\/b>/)
|
830
|
+
end
|
831
|
+
it "should put :symbol_tag around symbol and :text_with_symbol_tag around text" do
|
832
|
+
result = @linker.auto_link("@mention #hash $CASH", {:symbol_tag => 's', :text_with_symbol_tag => 'b', :username_include_symbol=>true})
|
833
|
+
expect(result).to match(/<s>@<\/s><b>mention<\/b>/)
|
834
|
+
expect(result).to match(/<s>#<\/s><b>hash<\/b>/)
|
835
|
+
expect(result).to match(/<s>\$<\/s><b>CASH<\/b>/)
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
describe "html_escape" do
|
840
|
+
before do
|
841
|
+
@linker = TestAutolink.new
|
842
|
+
end
|
843
|
+
it "should escape html entities properly" do
|
844
|
+
expect(@linker.html_escape("&")).to be == "&"
|
845
|
+
expect(@linker.html_escape(">")).to be == ">"
|
846
|
+
expect(@linker.html_escape("<")).to be == "<"
|
847
|
+
expect(@linker.html_escape("\"")).to be == """
|
848
|
+
expect(@linker.html_escape("'")).to be == "'"
|
849
|
+
expect(@linker.html_escape("&<>\"")).to be == "&<>""
|
850
|
+
expect(@linker.html_escape("<div>")).to be == "<div>"
|
851
|
+
expect(@linker.html_escape("a&b")).to be == "a&b"
|
852
|
+
expect(@linker.html_escape("<a href=\"https://twitter.com\" target=\"_blank\">twitter & friends</a>")).to be == "<a href="https://twitter.com" target="_blank">twitter & friends</a>"
|
853
|
+
expect(@linker.html_escape("&")).to be == "&amp;"
|
854
|
+
expect(@linker.html_escape(nil)).to be == nil
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
end
|