From ea7030d2a1f740755d976b59433f91d8fe9b9ebd Mon Sep 17 00:00:00 2001 From: catborise Date: Mon, 19 Oct 2026 15:54:04 +0300 Subject: [PATCH] fix(instances): the grouped instance list without computes has a table body The grouped list opens one tbody per compute, so with no compute the sortable table had none, and sortable.js threw on page load ("Cannot read properties of undefined (reading 'rows')"). An empty tbody is rendered in that case. --- instances/templates/allinstances_index_grouped.html | 2 ++ instances/tests/test_index.py | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/instances/templates/allinstances_index_grouped.html b/instances/templates/allinstances_index_grouped.html index 874b4dd8..c25c4358 100644 --- a/instances/templates/allinstances_index_grouped.html +++ b/instances/templates/allinstances_index_grouped.html @@ -88,6 +88,8 @@ {% endfor %} {% endif %} + {% empty %} + {# sortable.js needs a table body #} {% endfor %} diff --git a/instances/tests/test_index.py b/instances/tests/test_index.py index 2acebeeb..9543ea60 100644 --- a/instances/tests/test_index.py +++ b/instances/tests/test_index.py @@ -2,11 +2,13 @@ from unittest.mock import PropertyMock, patch +from appsettings.models import AppSettings from computes.models import Compute from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse from instances.models import Instance +from lxml import html class InstancesIndexTests(TestCase): @@ -42,3 +44,10 @@ class InstancesIndexTests(TestCase): self.assertEqual(response.status_code, 200) self.assertNotContains(response, "test4") self.assertEqual(list(response.context["computes"][0].instance_set.all()), []) + + def test_the_grouped_list_without_computes_has_a_table_body(self): + # sortable.js reads the first tbody's rows: without one it throws and leaves the table unsortable + AppSettings.objects.filter(key="VIEW_INSTANCES_LIST_STYLE").update(value="grouped") + self.client.force_login(get_user_model().objects.create_superuser("grouped-admin", "g@example.com", "pw")) + page = html.fromstring(self.client.get(reverse("instances:index")).content) + self.assertEqual(len(page.xpath("//table[@data-sortable]/tbody")), 1)