Files
2026-07-23 16:19:19 +02:00

106 lines
4.7 KiB
Python

import json
from io import BytesIO
from django.contrib.auth.models import User
from django.core.files.base import ContentFile
from django.test import TestCase
from django.urls import reverse
from PIL import Image
from shop.models import Product, ProductImage, ProductVariant
from shop.tests.mixins import CreateProductsMixin
class TestBackofficeHtmxContract(TestCase, CreateProductsMixin):
def setUp(self):
self.superuser = User.objects.create_superuser('vader', 'vader@empire.com', 'ihatesand')
self.client.force_login(self.superuser)
self.product = self.create_product()
def test_product_create_is_a_full_page_not_a_modal_fragment(self):
# ProductCreateView ya no es modal: navegar a la URL de creación siempre
# devuelve la página completa, incluso si la petición lleva HX-Request.
response = self.client.get(reverse('backoffice:product_create'), HTTP_HX_REQUEST='true')
assert response.status_code == 200
body = response.content.decode()
assert '<html' in body
assert 'Nuevo producto' in body
def test_product_create_redirects_to_detail_even_with_hx_request(self):
response = self.client.post(
reverse('backoffice:product_create'),
{'sku': 'HTMX-1', 'name': 'Producto htmx', 'description': '', 'stock': '0'},
HTTP_HX_REQUEST='true',
)
product = Product.objects.get(sku='HTMX-1')
assert response.status_code == 302
assert response.url == reverse('backoffice:product_detail', args=[product.pk])
assert 'HX-Trigger' not in response
def test_htmx_variant_create_triggers_refresh(self):
size_m = self.create_attribute_value('Talla', 'M')
response = self.client.post(
reverse('backoffice:product_variant_create', args=[self.product.pk]),
{'sku': 'HTMX-V1', 'stock': '5', 'attribute_values': [size_m.pk]},
HTTP_HX_REQUEST='true',
)
assert response.status_code == 204
trigger = json.loads(response['HX-Trigger'])
assert trigger['backoffice:modal-close'] is True
assert trigger['backoffice:list-changed'] is True
assert ProductVariant.objects.filter(sku='HTMX-V1').exists()
def test_htmx_variant_delete_stays_and_refreshes(self):
size_m = self.create_attribute_value('Talla', 'M')
variant = self.create_product_variant(self.product, sku='HTMX-V2', attribute_values=[size_m])
response = self.client.post(
reverse('backoffice:product_variant_delete', args=[variant.pk]), HTTP_HX_REQUEST='true'
)
assert response.status_code == 204
assert 'HX-Redirect' not in response
trigger = json.loads(response['HX-Trigger'])
assert trigger['backoffice:list-changed'] is True
assert not ProductVariant.objects.filter(pk=variant.pk).exists()
def test_htmx_image_upload_triggers_refresh(self):
image = Image.new('RGB', (64, 64), '#ACACAC')
buffer = BytesIO()
image.save(fp=buffer, format='WEBP')
file = ContentFile(buffer.getvalue(), name='test.webp')
response = self.client.post(
reverse('backoffice:product_image_create', args=[self.product.pk]),
{'original': file},
format='multipart',
HTTP_HX_REQUEST='true',
)
assert response.status_code == 204
trigger = json.loads(response['HX-Trigger'])
assert trigger == {'backoffice:modal-close': True, 'backoffice:list-changed': True}
assert ProductImage.objects.filter(product=self.product).exists()
def test_htmx_product_delete_uses_hx_redirect_instead_of_trigger(self):
product = self.create_product(sku='HTMX-DEL')
response = self.client.post(reverse('backoffice:product_delete', args=[product.pk]), HTTP_HX_REQUEST='true')
assert response.status_code == 200
assert response['HX-Redirect'] == reverse('backoffice:product_list')
assert 'HX-Trigger' not in response
assert not Product.objects.filter(pk=product.pk).exists()
def test_htmx_list_self_refresh_returns_fragment(self):
response = self.client.get(reverse('backoffice:product_list'), HTTP_HX_REQUEST='true')
assert response.status_code == 200
body = response.content.decode()
assert '<html' not in body
assert 'id="backoffice-list"' in body
def test_htmx_product_detail_self_refresh_returns_fragment(self):
response = self.client.get(reverse('backoffice:product_detail', args=[self.product.pk]), HTTP_HX_REQUEST='true')
assert response.status_code == 200
body = response.content.decode()
assert '<html' not in body
assert 'id="backoffice-detail"' in body