OSDN Git Service

GIFMAGAZINE のサムネイル表示に対応
[opentween/open-tween.git] / OpenTween.Tests / TweetFormatterTest.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // for more details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 using System;
23 using System.Collections.Generic;
24 using System.Linq;
25 using System.Text;
26 using OpenTween.Api;
27 using Xunit;
28 using Xunit.Extensions;
29
30 namespace OpenTween
31 {
32     public class TweetFormatterTest
33     {
34         [Fact]
35         public void FormatUrlEntity_Test()
36         {
37             var text = "http://t.co/KYi7vMZzRt";
38             var entities = new[]
39             {
40                 new TwitterEntityUrl
41                 {
42                     Indices = new[] { 0, 22 },
43                     DisplayUrl = "twitter.com",
44                     ExpandedUrl = "http://twitter.com/",
45                     Url = "http://t.co/KYi7vMZzRt",
46                 },
47             };
48
49             var expected = "<a href=\"http://t.co/KYi7vMZzRt\" title=\"http://twitter.com/\">twitter.com</a>";
50             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
51         }
52
53         [Fact]
54         public void FormatHashtagEntity_Test()
55         {
56             var text = "#OpenTween";
57             var entities = new[]
58             {
59                 new TwitterEntityHashtag
60                 {
61                     Indices = new[] { 0, 10 },
62                     Text = "OpenTween",
63                 },
64             };
65
66             var expected = "<a class=\"hashtag\" href=\"https://twitter.com/search?q=%23OpenTween\">#OpenTween</a>";
67             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
68         }
69
70         [Fact]
71         public void FormatMentionEntity_Test()
72         {
73             var text = "@TwitterAPI";
74             var entities = new[]
75             {
76                 new TwitterEntityMention
77                 {
78                     Indices = new[] { 0, 11 },
79                     Id = 6253282L,
80                     Name = "Twitter API",
81                     ScreenName = "twitterapi",
82                 },
83             };
84
85             var expected = "<a class=\"mention\" href=\"https://twitter.com/twitterapi\">@TwitterAPI</a>";
86             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
87         }
88
89         [Fact]
90         public void FormatMediaEntity_Test()
91         {
92             var text = "http://t.co/h5dCr4ftN4";
93             var entities = new[]
94             {
95                 new TwitterEntityMedia
96                 {
97                     Indices = new[] { 0, 22 },
98                     Sizes = new TwitterMediaSizes
99                     {
100                         Large = new TwitterMediaSizes.Size { Resize = "fit", Height = 329, Width = 1024 },
101                         Medium = new TwitterMediaSizes.Size { Resize = "fit", Height = 204, Width = 600 },
102                         Small = new TwitterMediaSizes.Size { Resize = "fit", Height = 116, Width = 340 },
103                         Thumb = new TwitterMediaSizes.Size { Resize = "crop", Height = 150, Width = 150 },
104                     },
105                     Type = "photo",
106                     Id = 426404550379986940L,
107                     MediaUrl = "http://pbs.twimg.com/media/BerkrewCYAAV4Kf.png",
108                     MediaUrlHttps = "https://pbs.twimg.com/media/BerkrewCYAAV4Kf.png",
109                     Url = "http://t.co/h5dCr4ftN4",
110                     DisplayUrl = "pic.twitter.com/h5dCr4ftN4",
111                     ExpandedUrl = "http://twitter.com/kim_upsilon/status/426404550371598337/photo/1",
112                 },
113             };
114
115             var expected = "<a href=\"http://t.co/h5dCr4ftN4\" title=\"http://twitter.com/kim_upsilon/status/426404550371598337/photo/1\">pic.twitter.com/h5dCr4ftN4</a>";
116             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
117         }
118
119         [Fact]
120         public void AutoLinkHtml_EntityNullTest()
121         {
122             var text = "てすとてすとー";
123             TwitterEntities entities = null;
124
125             var expected = "てすとてすとー";
126             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
127         }
128
129         [Fact]
130         public void AutoLinkHtml_EntityNullTest2()
131         {
132             var text = "てすとてすとー";
133             TwitterEntities entities = new TwitterEntities
134             {
135                 Urls = null,
136                 Hashtags = null,
137                 UserMentions = null,
138                 Media = null,
139             };
140
141             var expected = "てすとてすとー";
142             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
143         }
144
145         [Fact]
146         public void AutoLinkHtml_EntityNullTest3()
147         {
148             var text = "てすとてすとー";
149             IEnumerable<TwitterEntity> entities = null;
150
151             var expected = "てすとてすとー";
152             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
153         }
154
155         [Fact]
156         public void AutoLinkHtml_EntityNullTest4()
157         {
158             var text = "てすとてすとー";
159             IEnumerable<TwitterEntity> entities = new TwitterEntity[] { null };
160
161             var expected = "てすとてすとー";
162             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
163         }
164
165         [Fact]
166         public void AutoLinkHtml_EscapeTest()
167         {
168             // Twitter APIの中途半端なエスケープの対象とならない「"」や「'」に対するエスケープ処理を施す
169             var text = "\"\'@twitterapi\'\"";
170             var entities = new[]
171             {
172                 new TwitterEntityMention
173                 {
174                     Indices = new[] { 2, 13 },
175                     Id = 6253282L,
176                     Name = "Twitter API",
177                     ScreenName = "twitterapi",
178                 },
179             };
180
181             var expected = "&quot;&#39;<a class=\"mention\" href=\"https://twitter.com/twitterapi\">@twitterapi</a>&#39;&quot;";
182             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
183         }
184
185         [Fact]
186         public void AutoLinkHtml_EscapeTest2()
187         {
188             // 「<」や「>」についてはエスケープされた状態でAPIからテキストが返されるため、二重エスケープとならないように考慮する
189             var text = "&lt;b&gt; @twitterapi &lt;/b&gt;";
190             var entities = new[]
191             {
192                 new TwitterEntityMention
193                 {
194                     Indices = new[] { 10, 21 },
195                     Id = 6253282L,
196                     Name = "Twitter API",
197                     ScreenName = "twitterapi",
198                 },
199             };
200
201             var expected = "&lt;b&gt; <a class=\"mention\" href=\"https://twitter.com/twitterapi\">@twitterapi</a> &lt;/b&gt;";
202             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
203         }
204
205         [Fact]
206         public void AutoLinkHtml_EscapeTest3()
207         {
208             // 万が一「<」や「>」がエスケープされていない状態のテキストを受け取っても適切にエスケープが施されるようにする
209             var text = "<b> @twitterapi </b>";
210             var entities = new[]
211             {
212                 new TwitterEntityMention
213                 {
214                     Indices = new[] { 4, 15 },
215                     Id = 6253282L,
216                     Name = "Twitter API",
217                     ScreenName = "twitterapi",
218                 },
219             };
220
221             var expected = "&lt;b&gt; <a class=\"mention\" href=\"https://twitter.com/twitterapi\">@twitterapi</a> &lt;/b&gt;";
222             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
223         }
224
225         [Fact]
226         public void AutoLinkHtml_EscapeUrlTest()
227         {
228             // 日本語ハッシュタグのリンク先URLを適切にエスケープする
229             var text = "#ぜんぶ雪のせいだ";
230             var entities = new[]
231             {
232                 new TwitterEntityHashtag
233                 {
234                     Indices = new[] { 0, 9 },
235                     Text = "ぜんぶ雪のせいだ",
236                 },
237             };
238
239             var expected = "<a class=\"hashtag\" href=\"https://twitter.com/search?q=%23%E3%81%9C%E3%82%93%E3%81%B6%E9%9B%AA%E3%81%AE%E3%81%9B%E3%81%84%E3%81%A0\">#ぜんぶ雪のせいだ</a>";
240             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
241         }
242
243         [Fact]
244         public void AutoLinkHtml_SurrogatePairTest()
245         {
246             // UTF-16 で 4 バイトで表される文字を含むツイート
247             // 参照: https://sourceforge.jp/ticket/browse.php?group_id=6526&tid=33079
248             var text = "🐬🐬 @irucame 🐬🐬";
249             var entities = new[]
250             {
251                 new TwitterEntityMention
252                 {
253                     Indices = new[] { 3, 11 },
254                     Id = 89942943L,
255                     ScreenName = "irucame",
256                 },
257             };
258
259             var expected = "🐬🐬 <a class=\"mention\" href=\"https://twitter.com/irucame\">@irucame</a> 🐬🐬";
260             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
261         }
262
263         [Fact]
264         public void AutoLinkHtml_SurrogatePairTest2()
265         {
266             // 現時点では存在しないものの、ハッシュタグなどエンティティ内にサロゲートペアが含まれる場合も考慮する
267             var text = "🐬🐬 #🐬🐬 🐬🐬 #🐬🐬 🐬🐬";
268             var entities = new[]
269             {
270                 new TwitterEntityHashtag
271                 {
272                     Indices = new[] { 3, 6 },
273                     Text = "🐬🐬",
274                 },
275                 new TwitterEntityHashtag
276                 {
277                     Indices = new[] { 10, 13 },
278                     Text = "🐬🐬",
279                 },
280             };
281
282             var expected = "🐬🐬 <a class=\"hashtag\" href=\"https://twitter.com/search?q=%23%F0%9F%90%AC%F0%9F%90%AC\">#🐬🐬</a> " +
283                 "🐬🐬 <a class=\"hashtag\" href=\"https://twitter.com/search?q=%23%F0%9F%90%AC%F0%9F%90%AC\">#🐬🐬</a> 🐬🐬";
284             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
285         }
286
287         [Fact]
288         public void AutoLinkHtml_CompositeCharacterTest()
289         {
290             // 合成文字 é ( \u00e9 ) を含むツイート
291             // 参照: https://dev.twitter.com/issues/251
292             var text = "Caf\u00e9 #test";
293             var entities = new[]
294             {
295                 new TwitterEntityHashtag
296                 {
297                     Indices = new[] { 5, 10 },
298                     Text = "test",
299                 },
300             };
301
302             var expected = "Caf\u00e9 <a class=\"hashtag\" href=\"https://twitter.com/search?q=%23test\">#test</a>";
303             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
304         }
305
306         [Fact]
307         public void AutoLinkHtml_CombiningCharacterSequenceTest()
308         {
309             // 結合文字列 é ( e + \u0301 ) を含むツイート
310             // 参照: https://dev.twitter.com/issues/251
311             var text = "Cafe\u0301 #test";
312             var entities = new[]
313             {
314                 new TwitterEntityHashtag
315                 {
316                     Indices = new[] { 6, 11 },
317                     Text = "test",
318                 },
319             };
320
321             var expected = "Cafe\u0301 <a class=\"hashtag\" href=\"https://twitter.com/search?q=%23test\">#test</a>";
322             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
323         }
324
325         [Fact]
326         public void AutoLinkHtml_BreakLineTest()
327         {
328             var text = "てすと\nてすと\nてすと";
329             TwitterEntities entities = null;
330
331             var expected = "てすと<br>てすと<br>てすと";
332             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
333         }
334
335         [Fact]
336         public void AutoLinkHtml_OverlappedEntitiesTest()
337         {
338             // extended_entities で追加される、区間が重複したエンティティを考慮
339             // 参照: https://dev.twitter.com/docs/api/multiple-media-extended-entities
340
341             var text = "\"I hope you'll keep...building bonds of friendship that will enrich your lives &amp; enrich our world\" \u2014FLOTUS in China, http://t.co/fxmuQN9JL9";
342             var entities = new[]
343             {
344                 new TwitterEntityMedia
345                 {
346                     DisplayUrl = "pic.twitter.com/fxmuQN9JL9",
347                     ExpandedUrl = "http://twitter.com/FLOTUS/status/449660889793581056/photo/1",
348                     Indices = new[] { 121, 143 },
349                     MediaUrlHttps = "https://pbs.twimg.com/media/Bj2EH6yIQAEYvxu.jpg",
350                     Url = "http://t.co/fxmuQN9JL9",
351                 },
352                 new TwitterEntityMedia
353                 {
354                     DisplayUrl = "pic.twitter.com/fxmuQN9JL9",
355                     ExpandedUrl = "http://twitter.com/FLOTUS/status/449660889793581056/photo/1",
356                     Indices = new[] { 121, 143 },
357                     MediaUrlHttps = "https://pbs.twimg.com/media/Bj2EHxAIIAE8dtg.jpg",
358                     Url = "http://t.co/fxmuQN9JL9",
359                 },
360                 new TwitterEntityMedia
361                 {
362                     DisplayUrl = "pic.twitter.com/fxmuQN9JL9",
363                     ExpandedUrl = "http://twitter.com/FLOTUS/status/449660889793581056/photo/1",
364                     Indices = new[] { 121, 143 },
365                     MediaUrlHttps = "https://pbs.twimg.com/media/Bj2EH3pIYAE4LQn.jpg",
366                     Url = "http://t.co/fxmuQN9JL9",
367                 },
368                 new TwitterEntityMedia
369                 {
370                     DisplayUrl = "pic.twitter.com/fxmuQN9JL9",
371                     ExpandedUrl = "http://twitter.com/FLOTUS/status/449660889793581056/photo/1",
372                     Indices = new[] { 121, 143 },
373                     MediaUrlHttps = "https://pbs.twimg.com/media/Bj2EL3DIEAAzGAX.jpg",
374                     Url = "http://t.co/fxmuQN9JL9",
375                 },
376             };
377
378             var expected = "&quot;I hope you&#39;ll keep...building bonds of friendship that will enrich your lives &amp; enrich our world&quot; \u2014FLOTUS in China, " +
379                 "<a href=\"http://t.co/fxmuQN9JL9\" title=\"http://twitter.com/FLOTUS/status/449660889793581056/photo/1\">pic.twitter.com/fxmuQN9JL9</a>";
380             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
381         }
382     }
383 }