test_tables.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. from django.test import TestCase
  2. from core.events import OBJECT_CREATED
  3. from core.models import ObjectType
  4. from dcim.models import Site
  5. from extras.choices import CustomFieldStatusChoices, CustomFieldTypeChoices
  6. from extras.models import Bookmark, CustomField, EventRule, Notification, Subscription
  7. from extras.tables import *
  8. from utilities.testing import TableTestCases
  9. class CustomFieldTableTestCase(TableTestCases.StandardTableTestCase):
  10. table = CustomFieldTable
  11. class CustomFieldStatusColumnTestCase(TestCase):
  12. """
  13. A field which is not live must be distinguishable at a glance from one which is: deleting a
  14. field with a large amount of stored data reports success while leaving it listed until the purge
  15. job completes (see CustomFieldStatusColumn).
  16. """
  17. @classmethod
  18. def setUpTestData(cls):
  19. for status in (
  20. CustomFieldStatusChoices.STATUS_ACTIVE,
  21. CustomFieldStatusChoices.STATUS_PROVISIONING,
  22. CustomFieldStatusChoices.STATUS_DELETING,
  23. ):
  24. custom_field = CustomField.objects.create(
  25. name=f'field_{status}', type=CustomFieldTypeChoices.TYPE_TEXT
  26. )
  27. # Applied via the queryset to bypass the guard against modifying a pending field
  28. CustomField.objects.filter(pk=custom_field.pk).update(status=status)
  29. def _row(self, status):
  30. table = CustomFieldTable(CustomField.objects.filter(status=status))
  31. return table.rows[0]
  32. def test_status_is_shown_by_default(self):
  33. self.assertIn('status', CustomFieldTable.Meta.default_columns)
  34. def test_active_field_renders_a_green_checkmark(self):
  35. cell = self._row(CustomFieldStatusChoices.STATUS_ACTIVE).get_cell('status')
  36. self.assertInHTML(
  37. '<span class="badge text-bg-green" title="Active"><i class="mdi mdi-check-bold"></i></span>', cell
  38. )
  39. def test_pending_field_renders_an_orange_warning(self):
  40. for status, label in (
  41. (CustomFieldStatusChoices.STATUS_PROVISIONING, 'Provisioning'),
  42. (CustomFieldStatusChoices.STATUS_DELETING, 'Deleting'),
  43. ):
  44. with self.subTest(status=status):
  45. cell = self._row(status).get_cell('status')
  46. self.assertInHTML(
  47. f'<span class="badge text-bg-orange" title="{label}">'
  48. f'<i class="mdi mdi-alert"></i></span>',
  49. cell
  50. )
  51. def test_export_records_the_label(self):
  52. """
  53. The icon carries no text, so an export must fall back to the human-readable status.
  54. """
  55. for status, label in (
  56. (CustomFieldStatusChoices.STATUS_ACTIVE, 'Active'),
  57. (CustomFieldStatusChoices.STATUS_PROVISIONING, 'Provisioning'),
  58. (CustomFieldStatusChoices.STATUS_DELETING, 'Deleting'),
  59. ):
  60. with self.subTest(status=status):
  61. self.assertEqual(self._row(status).get_cell_value('status'), label)
  62. class CustomFieldChoiceSetTableTestCase(TableTestCases.StandardTableTestCase):
  63. table = CustomFieldChoiceSetTable
  64. class CustomLinkTableTestCase(TableTestCases.StandardTableTestCase):
  65. table = CustomLinkTable
  66. class ExportTemplateTableTestCase(TableTestCases.StandardTableTestCase):
  67. table = ExportTemplateTable
  68. class SavedFilterTableTestCase(TableTestCases.StandardTableTestCase):
  69. table = SavedFilterTable
  70. class TableConfigTableTestCase(TableTestCases.StandardTableTestCase):
  71. table = TableConfigTable
  72. class BookmarkTableTestCase(TableTestCases.StandardTableTestCase):
  73. table = BookmarkTable
  74. # The list view for this table lives in account.views (not extras.views),
  75. # so auto-discovery cannot find it. Provide an explicit queryset source.
  76. queryset_sources = [
  77. ('Bookmark.objects.all()', Bookmark.objects.all()),
  78. ]
  79. class NotificationGroupTableTestCase(TableTestCases.StandardTableTestCase):
  80. table = NotificationGroupTable
  81. class NotificationTableTestCase(TableTestCases.StandardTableTestCase):
  82. table = NotificationTable
  83. # The list view for this table lives in account.views (not extras.views),
  84. # so auto-discovery cannot find it. Provide an explicit queryset source.
  85. queryset_sources = [
  86. ('Notification.objects.all()', Notification.objects.all()),
  87. ]
  88. class SubscriptionTableTestCase(TableTestCases.StandardTableTestCase):
  89. table = SubscriptionTable
  90. # The list view for this table lives in account.views (not extras.views),
  91. # so auto-discovery cannot find it. Provide an explicit queryset source.
  92. queryset_sources = [
  93. ('Subscription.objects.all()', Subscription.objects.all()),
  94. ]
  95. class WebhookTableTestCase(TableTestCases.StandardTableTestCase):
  96. table = WebhookTable
  97. class EventRuleTableTestCase(TableTestCases.StandardTableTestCase):
  98. table = EventRuleTable
  99. class EventRuleTableActionTypeRenderingTestCase(TestCase):
  100. """
  101. render_action_type() badges an unregistered action as unavailable; value_action_type() carries
  102. the same label for non-HTML output (e.g. CSV export), without the markup.
  103. """
  104. def test_render_action_type_for_registered_action(self):
  105. rule = EventRule.objects.create(name='Render Test Rule', event_types=[OBJECT_CREATED], action_type='webhook')
  106. rule.object_types.set([ObjectType.objects.get_for_model(Site)])
  107. table = EventRuleTable(EventRule.objects.filter(pk=rule.pk))
  108. self.assertEqual(table.render_action_type(rule), 'Webhook')
  109. self.assertEqual(table.value_action_type(rule), 'Webhook')
  110. def test_render_action_type_for_unregistered_action(self):
  111. rule = EventRule.objects.create(
  112. name='Render Test Unavailable Rule',
  113. event_types=[OBJECT_CREATED],
  114. action_type='someplugin.not_installed_render_test',
  115. )
  116. rule.object_types.set([ObjectType.objects.get_for_model(Site)])
  117. table = EventRuleTable(EventRule.objects.filter(pk=rule.pk))
  118. rendered = table.render_action_type(rule)
  119. self.assertIn('someplugin.not_installed_render_test (unavailable)', rendered)
  120. self.assertIn('badge text-bg-red', rendered)
  121. # The same label, without markup
  122. value = table.value_action_type(rule)
  123. self.assertEqual(value, 'someplugin.not_installed_render_test (unavailable)')
  124. self.assertNotIn('<span', value)
  125. class TagTableTestCase(TableTestCases.StandardTableTestCase):
  126. table = TagTable
  127. class ConfigContextProfileTableTestCase(TableTestCases.StandardTableTestCase):
  128. table = ConfigContextProfileTable
  129. class ConfigContextTableTestCase(TableTestCases.StandardTableTestCase):
  130. table = ConfigContextTable
  131. class ConfigTemplateTableTestCase(TableTestCases.StandardTableTestCase):
  132. table = ConfigTemplateTable
  133. class ImageAttachmentTableTestCase(TableTestCases.StandardTableTestCase):
  134. table = ImageAttachmentTable
  135. class JournalEntryTableTestCase(TableTestCases.StandardTableTestCase):
  136. table = JournalEntryTable