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