This commit is contained in:
parent
583ab535e8
commit
62837089b4
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import urllib.parse
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.urls import reverse
|
||||
|
|
@ -152,6 +152,77 @@ class JobTestCase(
|
|||
)
|
||||
|
||||
|
||||
class JobLogViewTestCase(TestCase):
|
||||
user_permissions = (
|
||||
'core.view_job',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.job = Job.objects.create(
|
||||
name='Test Job',
|
||||
job_id=uuid.uuid4(),
|
||||
)
|
||||
cls.job.log_entries = [
|
||||
{
|
||||
'level': 'info',
|
||||
'message': f'log line {i}',
|
||||
'timestamp': datetime(2026, 1, 1, tzinfo=UTC),
|
||||
}
|
||||
for i in range(120)
|
||||
]
|
||||
cls.job.save()
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# UserConfig.set() mutates self.data in place, which can mutate DEFAULT_USER_PREFERENCES
|
||||
# (the signal in users/signals.py initializes data with a shared reference). Assign a
|
||||
# fresh literal instead. Pin per_page so page-boundary assertions don't depend on PAGINATE_COUNT.
|
||||
self.user.config.data = {'pagination': {'per_page': 50}}
|
||||
self.user.config.save()
|
||||
|
||||
def test_log_page_renders_table_inline(self):
|
||||
"""The full page renders the first log page inside an HTMX container."""
|
||||
url = reverse('core:job_log', kwargs={'pk': self.job.pk})
|
||||
response = self.client.get(url)
|
||||
self.assertHttpStatus(response, 200)
|
||||
self.assertContains(response, 'htmx-container')
|
||||
self.assertContains(response, 'log line 0')
|
||||
self.assertContains(response, 'Showing 1-50 of 120')
|
||||
|
||||
def test_log_page_table_is_embedded(self):
|
||||
"""The embedded table never pushes page/per_page into the browser URL."""
|
||||
url = reverse('core:job_log', kwargs={'pk': self.job.pk})
|
||||
response = self.client.get(url)
|
||||
self.assertHttpStatus(response, 200)
|
||||
self.assertNotContains(response, 'hx-push-url="true"')
|
||||
|
||||
def test_log_table_htmx_renders_partial(self):
|
||||
"""An HTMX request returns the paginated table partial."""
|
||||
url = reverse('core:job_log', kwargs={'pk': self.job.pk})
|
||||
response = self.client.get(url, headers={'hx-request': 'true'})
|
||||
self.assertHttpStatus(response, 200)
|
||||
self.assertContains(response, 'log line 0')
|
||||
self.assertContains(response, 'Showing 1-50 of 120')
|
||||
self.assertContains(response, 'Per Page')
|
||||
|
||||
def test_log_table_htmx_page_navigation(self):
|
||||
"""`?page=2` advances the embedded table to the second page."""
|
||||
url = reverse('core:job_log', kwargs={'pk': self.job.pk})
|
||||
response = self.client.get(f'{url}?page=2', headers={'hx-request': 'true'})
|
||||
self.assertHttpStatus(response, 200)
|
||||
self.assertContains(response, 'log line 50')
|
||||
self.assertNotContains(response, 'log line 49')
|
||||
|
||||
def test_log_table_htmx_per_page(self):
|
||||
"""`?per_page=100` widens the embedded table page size."""
|
||||
url = reverse('core:job_log', kwargs={'pk': self.job.pk})
|
||||
response = self.client.get(f'{url}?per_page=100', headers={'hx-request': 'true'})
|
||||
self.assertHttpStatus(response, 200)
|
||||
self.assertContains(response, 'log line 99')
|
||||
self.assertNotContains(response, 'log line 100')
|
||||
|
||||
|
||||
# TODO: Convert to StandardTestCases.Views
|
||||
class ObjectChangeTestCase(TestCase):
|
||||
user_permissions = (
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ from netbox.plugins.utils import get_installed_plugins
|
|||
from netbox.ui import layout
|
||||
from netbox.ui.panels import (
|
||||
CommentsPanel,
|
||||
ContextTablePanel,
|
||||
JSONPanel,
|
||||
ObjectsTablePanel,
|
||||
PluginContentPanel,
|
||||
|
|
@ -269,7 +268,7 @@ class JobLogView(generic.ObjectView):
|
|||
layout = layout.Layout(
|
||||
layout.Row(
|
||||
layout.Column(
|
||||
ContextTablePanel('table', title=_('Log Entries')),
|
||||
TemplatePanel('core/job/log_entries.html', title=_('Log Entries')),
|
||||
PluginContentPanel('left_page'),
|
||||
),
|
||||
),
|
||||
|
|
@ -280,13 +279,27 @@ class JobLogView(generic.ObjectView):
|
|||
),
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
def get_table(self, request, instance):
|
||||
table = JobLogEntryTable(instance.log_entries)
|
||||
table.embedded = True
|
||||
table.htmx_url = reverse('core:job_log', kwargs={'pk': instance.pk})
|
||||
table.configure(request)
|
||||
return table
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
'table': table,
|
||||
'table': self.get_table(request, instance),
|
||||
}
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
if htmx_partial(request):
|
||||
instance = self.get_object(**kwargs)
|
||||
return render(request, 'htmx/table.html', {
|
||||
'object': instance,
|
||||
'table': self.get_table(request, instance),
|
||||
})
|
||||
return super().get(request, **kwargs)
|
||||
|
||||
|
||||
@register_model_view(Job, 'delete')
|
||||
class JobDeleteView(generic.ObjectDeleteView):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{% extends 'ui/panels/_base.html' %}
|
||||
|
||||
{% block panel_content %}
|
||||
{% include 'htmx/table.html' %}
|
||||
{% endblock panel_content %}
|
||||
Loading…
Reference in New Issue