test_embed.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import requests
  2. from base import BaseTestCase, Media
  3. from parameterized import parameterized
  4. class Embed:
  5. container = '.tweet-embed'
  6. footer = '.embed-footer'
  7. tweet_content = '.tweet-content'
  8. tweet_header = '.tweet-header'
  9. fullname = '.fullname'
  10. username = '.username'
  11. avatar = '.avatar'
  12. stats = '.tweet-stats'
  13. quote = '.quote'
  14. error_panel = '.error-panel'
  15. class TweetEmbedTest(BaseTestCase):
  16. """Test tweet embed rendering."""
  17. tweet = 'elonmusk/status/1141367104702038016'
  18. def test_embed_container_visible(self):
  19. self.open_nitter(self.tweet + '/embed')
  20. self.assert_element_visible(Embed.container)
  21. def test_embed_has_footer(self):
  22. self.open_nitter(self.tweet + '/embed')
  23. self.assert_element_visible(Embed.footer)
  24. self.assert_text_visible('Read more on', Embed.footer)
  25. def test_embed_has_tweet_content(self):
  26. self.open_nitter(self.tweet + '/embed')
  27. self.assert_element_visible(Embed.tweet_content)
  28. def test_embed_has_avatar(self):
  29. self.open_nitter(self.tweet + '/embed')
  30. self.assert_element_visible(Embed.avatar)
  31. def test_embed_has_username(self):
  32. self.open_nitter(self.tweet + '/embed')
  33. self.assert_element_visible(Embed.username)
  34. def test_embed_has_stats(self):
  35. self.open_nitter(self.tweet + '/embed')
  36. self.assert_element_visible(Embed.stats)
  37. def test_embed_footer_links_to_tweet(self):
  38. self.open_nitter(self.tweet + '/embed')
  39. href = self.get_attribute(Embed.footer, 'href')
  40. self.assertIn('/elonmusk/status/1141367104702038016', href)
  41. class TweetEmbedMediaTest(BaseTestCase):
  42. """Test embed rendering with various media types."""
  43. def test_embed_with_image(self):
  44. self.open_nitter('mobile_test/status/519364660823207936/embed')
  45. self.assert_element_visible(Embed.container)
  46. self.scroll_to(Media.container)
  47. self.assert_element_visible(Media.image)
  48. def test_embed_with_gif(self):
  49. self.open_nitter('elonmusk/status/1141367104702038016/embed')
  50. self.assert_element_visible(Embed.container)
  51. self.scroll_to(Media.container)
  52. self.assert_element_visible(Media.gif)
  53. def test_embed_with_video(self):
  54. self.open_nitter('d0m96/status/1078373829917974528/embed')
  55. self.assert_element_visible(Embed.container)
  56. self.scroll_to(Media.container)
  57. self.assert_element_visible(Media.video)
  58. def test_embed_with_gallery(self):
  59. self.open_nitter('mobile_test/status/451108446603980803/embed')
  60. self.assert_element_visible(Embed.container)
  61. self.scroll_to(Media.container)
  62. self.assert_element_visible(Media.row)
  63. class TweetEmbedQuoteTest(BaseTestCase):
  64. """Test embed rendering with quoted tweets."""
  65. def test_embed_with_quote_shows_quote(self):
  66. self.open_nitter('elonmusk/status/1138827760107790336/embed')
  67. self.assert_element_visible(Embed.container)
  68. self.assert_element_visible(Embed.quote)
  69. def test_embed_quote_has_content(self):
  70. self.open_nitter('elonmusk/status/1138827760107790336/embed')
  71. quote = self.find_element(Embed.quote)
  72. self.assertIsNotNone(quote.text)
  73. class EmbedErrorTest(BaseTestCase):
  74. """Test embed error handling."""
  75. def test_nonexistent_tweet_shows_error(self):
  76. self.open_nitter('nobody/status/1/embed')
  77. self.assert_element_visible('.tweet-embed.error-embed')
  78. self.assert_text_visible('not found', Embed.error_panel)
  79. def test_protected_account_embed_shows_error(self):
  80. self.open_nitter('mobile_test_7/status/1/embed')
  81. self.assert_element_visible('.tweet-embed.error-embed')
  82. def test_invalid_tweet_id_shows_error(self):
  83. self.open_nitter('jack/status/notanumber/embed')
  84. self.assert_element_visible('.tweet-embed.error-embed')
  85. class OEmbedApiTest(BaseTestCase):
  86. """Test oEmbed API endpoint."""
  87. base_url = 'http://localhost:8080'
  88. tweet_url = 'https://twitter.com/elonmusk/status/1141367104702038016'
  89. def test_oembed_returns_json(self):
  90. resp = requests.get(f'{self.base_url}/api/oembed?url={self.tweet_url}')
  91. self.assertEqual(resp.status_code, 200)
  92. self.assertEqual(resp.headers['Content-Type'], 'application/json')
  93. def test_oembed_has_required_fields(self):
  94. resp = requests.get(f'{self.base_url}/api/oembed?url={self.tweet_url}')
  95. data = resp.json()
  96. self.assertEqual(data['type'], 'rich')
  97. self.assertEqual(data['version'], '1.0')
  98. self.assertIn('html', data)
  99. self.assertIn('author_name', data)
  100. self.assertIn('provider_name', data)
  101. def test_oembed_html_contains_iframe(self):
  102. resp = requests.get(f'{self.base_url}/api/oembed?url={self.tweet_url}')
  103. data = resp.json()
  104. self.assertIn('<iframe', data['html'])
  105. self.assertIn('/embed', data['html'])
  106. def test_oembed_has_cors_header(self):
  107. resp = requests.get(f'{self.base_url}/api/oembed?url={self.tweet_url}')
  108. self.assertEqual(resp.headers.get('Access-Control-Allow-Origin'), '*')
  109. def test_oembed_missing_url_returns_400(self):
  110. resp = requests.get(f'{self.base_url}/api/oembed')
  111. self.assertEqual(resp.status_code, 400)
  112. def test_oembed_invalid_url_returns_400(self):
  113. resp = requests.get(f'{self.base_url}/api/oembed?url=https://example.com')
  114. self.assertEqual(resp.status_code, 400)
  115. def test_oembed_supports_x_com_url(self):
  116. x_url = 'https://x.com/elonmusk/status/1141367104702038016'
  117. resp = requests.get(f'{self.base_url}/api/oembed?url={x_url}')
  118. self.assertEqual(resp.status_code, 200)
  119. def test_oembed_strips_query_params(self):
  120. url_with_params = 'https://twitter.com/elonmusk/status/1141367104702038016?s=20&t=abc'
  121. resp = requests.get(f'{self.base_url}/api/oembed?url={url_with_params}')
  122. self.assertEqual(resp.status_code, 200)
  123. data = resp.json()
  124. self.assertIn('html', data)
  125. def test_oembed_handles_trailing_slash(self):
  126. url_with_slash = 'https://twitter.com/elonmusk/status/1141367104702038016/'
  127. resp = requests.get(f'{self.base_url}/api/oembed?url={url_with_slash}')
  128. self.assertEqual(resp.status_code, 200)
  129. def test_oembed_handles_mobile_url(self):
  130. mobile_url = 'https://mobile.twitter.com/elonmusk/status/1141367104702038016'
  131. resp = requests.get(f'{self.base_url}/api/oembed?url={mobile_url}')
  132. self.assertEqual(resp.status_code, 200)
  133. def test_oembed_rejects_malformed_tweet_id(self):
  134. bad_url = 'https://twitter.com/elonmusk/status/notanumber'
  135. resp = requests.get(f'{self.base_url}/api/oembed?url={bad_url}')
  136. self.assertEqual(resp.status_code, 400)
  137. def test_oembed_maxwidth_param_accepted(self):
  138. resp = requests.get(f'{self.base_url}/api/oembed?url={self.tweet_url}&maxwidth=400')
  139. self.assertEqual(resp.status_code, 200)
  140. def test_oembed_author_url_present(self):
  141. resp = requests.get(f'{self.base_url}/api/oembed?url={self.tweet_url}')
  142. data = resp.json()
  143. self.assertIn('author_url', data)
  144. self.assertIn('elonmusk', data['author_url'])
  145. class VideoEmbedTest(BaseTestCase):
  146. """Test video embed route (/i/videos/tweet/{id})."""
  147. video_tweet_id = '1078373829917974528'
  148. def test_video_embed_has_video_element(self):
  149. self.open_nitter(f'i/videos/tweet/{self.video_tweet_id}')
  150. self.assert_element_visible('video')
  151. def test_video_embed_has_poster(self):
  152. self.open_nitter(f'i/videos/tweet/{self.video_tweet_id}')
  153. poster = self.get_attribute('video', 'poster')
  154. self.assertIsNotNone(poster)
  155. self.assertIn('pic/', poster)
  156. def test_video_embed_nonexistent_returns_error(self):
  157. self.open_nitter('i/videos/tweet/1')
  158. self.assert_element_visible('.error-embed')