From ebb6d5a5a571b47845509508edbfe46bdd883576 Mon Sep 17 00:00:00 2001 From: Pablo Moreno Date: Thu, 16 Jan 2025 10:34:47 +0100 Subject: [PATCH] feat: improved coverage --- shop/tests/mixins.py | 2 + users/forms/register.py | 1 + web/forms.py | 3 +- web/tests/images/trolley.png | Bin 0 -> 4154 bytes web/tests/test_login.py | 15 +++++ web/tests/test_manifest.py | 15 ++++- web/tests/test_orders.py | 119 +++++++++++++++++++++++++++++++++++ web/tests/test_register.py | 36 ++++++++++- web/tests/test_wishlist.py | 10 +++ web/views/web.py | 2 + 10 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 web/tests/images/trolley.png create mode 100644 web/tests/test_orders.py diff --git a/shop/tests/mixins.py b/shop/tests/mixins.py index af7bac0..d5f1cf2 100644 --- a/shop/tests/mixins.py +++ b/shop/tests/mixins.py @@ -12,6 +12,7 @@ class CreateProductsMixin: name="Producto 1", description="Descripción", price=Decimal("10.00"), + is_shipping=False, ) -> Product: tax, created = Tax.objects.get_or_create( code="IVA", @@ -21,6 +22,7 @@ class CreateProductsMixin: sku=sku, name=name, description=description, + is_shipping_method=is_shipping, ) ProductPrice.objects.create( price=price, diff --git a/users/forms/register.py b/users/forms/register.py index d31067a..e197683 100644 --- a/users/forms/register.py +++ b/users/forms/register.py @@ -10,6 +10,7 @@ class RegisterForm(BaseUserCreationForm, SetPasswordMixin): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field_name in self.fields.keys(): + self.fields[field_name].required = True self.fields[field_name].widget.attrs.update( { "class": "my-2 bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg " diff --git a/web/forms.py b/web/forms.py index 9827bc5..ee9ba4f 100644 --- a/web/forms.py +++ b/web/forms.py @@ -8,13 +8,12 @@ class CartItemForm(forms.Form): """ - product = forms.IntegerField(widget=widgets.HiddenInput) quantity = forms.IntegerField() class CreateOrderForm(forms.Form): - email = forms.CharField(max_length=254) + email = forms.CharField(max_length=254, required=True) shipping_address_full_name = forms.CharField(max_length=128, required=True) shipping_address = forms.CharField(max_length=128, required=True) diff --git a/web/tests/images/trolley.png b/web/tests/images/trolley.png new file mode 100644 index 0000000000000000000000000000000000000000..8205a3341678016deea9e10677b8a5749d6bd507 GIT binary patch literal 4154 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 literal 0 HcmV?d00001 diff --git a/web/tests/test_login.py b/web/tests/test_login.py index 0f0a7a4..4d39cb9 100644 --- a/web/tests/test_login.py +++ b/web/tests/test_login.py @@ -96,3 +96,18 @@ class TestLogin(APITestCase): response = self.client.get(reverse("web:logout")) assert response.status_code == status.HTTP_302_FOUND assert response.url == reverse("web:index") + + def test_login_then_get_my_account(self): + response = self.client.post( + reverse("web:login"), + { + "username": self.user.username, + "password": self.password, + }, + ) + + assert response.status_code == status.HTTP_302_FOUND + assert response.url == reverse("web:index") + + response = self.client.get(reverse("web:my_account")) + assert response.status_code == status.HTTP_200_OK diff --git a/web/tests/test_manifest.py b/web/tests/test_manifest.py index c843974..93db4a5 100644 --- a/web/tests/test_manifest.py +++ b/web/tests/test_manifest.py @@ -1,13 +1,24 @@ +import os from django.test import TestCase from django.urls import reverse +from django.conf import settings from web.models import WebSettings +from django.core.files.uploadedfile import SimpleUploadedFile class TestManifest(TestCase): def setUp(self): - WebSettings.objects.create() + self.settings = WebSettings.load() - def test_index_view(self): + with open(os.path.join(settings.BASE_DIR, 'web', 'tests', 'images', 'trolley.png'), 'rb') as f: + file_128 = SimpleUploadedFile('logo_128.png', f.read()) + self.settings.logo_128 = file_128 + file_240 = SimpleUploadedFile('logo_240.png', f.read()) + self.settings.logo_240 = file_240 + self.settings.logo = file_240 + self.settings.save() + + def test_manifest_view(self): response = self.client.get(reverse("web:manifest_json")) assert response.status_code == 200 diff --git a/web/tests/test_orders.py b/web/tests/test_orders.py new file mode 100644 index 0000000..e23a008 --- /dev/null +++ b/web/tests/test_orders.py @@ -0,0 +1,119 @@ +from decimal import Decimal + +from django.contrib.auth.models import User +from django.test import TestCase +from django.urls import reverse + +from shop.models import Cart, CartItem, Order, CustomerAddress, ShippingMethod, ShopSettings +from shop.tests.mixins import CreateProductsMixin + + +class TestOrders(TestCase, CreateProductsMixin): + def setUp(self): + self.product = self.create_product() + self.user = User.objects.create_user("anakin", "anakin@skywalker.com", "ihatesand") + self.cart_for_user = self.create_cart(user=self.user) + self.anonymous_cart = self.create_cart() + self.address = CustomerAddress.objects.create( + vat_id="11111111H", + full_name="Vader", + address="Tatooine", + address_town="Mos Eisley", + address_zip="00001", + address_state="Tatooine", + address_country="Mos Eisley", + address_phone="00000000", + address_type=CustomerAddress.Types.BILLING, + email=self.user.email, + default=True, + ) + self.shipping_method = self.create_shipping_method() + self.settings = ShopSettings.load() + self.settings.shared_secret = "sq7HjrUOBfKmC576ILgskD5srU870gJ7" # Debug secret + self.settings.save() + + def create_cart(self, user=None): + cart = Cart.objects.create( + user=user, + ) + CartItem.objects.create( + cart=cart, + product=self.product, + quantity=1, + ) + return cart + + def create_shipping_method(self): + product = self.create_product(sku="SM1", name="SM1", price=Decimal("1.00")) + method = ShippingMethod.objects.create(name="SM 1", shipping_product=product) + return method + + def test_logged_user_orders_page(self): + self.client.force_login(self.user) + response = self.client.get(reverse("web:orders")) + assert response.status_code == 200 + + def test_logged_user_create_get_order_page(self): + self.client.force_login(self.user) + response = self.client.get(reverse("web:cart_detail")) + assert response.status_code == 200 + + # Crear pedido + data = { + "email": self.user.email, + "shipping_address_full_name": self.address.full_name, + "shipping_address": self.address.address, + "shipping_address_town": self.address.address_town, + "shipping_address_zip": self.address.address_zip, + "shipping_address_state": self.address.address_state, + "shipping_address_country": self.address.address_country, + "shipping_address_phone": self.address.address_phone, + "same_as_shipping": True, + "billing_address_full_name": self.address.full_name, + "billing_address": self.address.address, + "billing_address_town": self.address.address_town, + "billing_address_zip": self.address.address_zip, + "billing_address_state": self.address.address_state, + "billing_address_country": self.address.address_country, + "billing_address_phone": self.address.address_phone, + "shipping_method": self.shipping_method.pk, + } + + response = self.client.post(reverse("web:cart_detail"), data) + assert response.status_code == 302 + + order = Order.objects.filter(user=self.user).first() + assert order is not None + + response = self.client.get(response.url) + assert response.status_code == 200 + + def test_logged_user_create_get_order_page_invalid_data(self): + self.client.force_login(self.user) + response = self.client.get(reverse("web:cart_detail")) + assert response.status_code == 200 + + # Sin e-mail + data = { + "shipping_address_full_name": self.address.full_name, + "shipping_address": self.address.address, + "shipping_address_town": self.address.address_town, + "shipping_address_zip": self.address.address_zip, + "shipping_address_state": self.address.address_state, + "shipping_address_country": self.address.address_country, + "shipping_address_phone": self.address.address_phone, + "same_as_shipping": True, + "billing_address_full_name": self.address.full_name, + "billing_address": self.address.address, + "billing_address_town": self.address.address_town, + "billing_address_zip": self.address.address_zip, + "billing_address_state": self.address.address_state, + "billing_address_country": self.address.address_country, + "billing_address_phone": self.address.address_phone, + "shipping_method": self.shipping_method.pk, + } + + response = self.client.post(reverse("web:cart_detail"), data) + assert response.status_code == 200 + + assert not Order.objects.filter(user=self.user).exists() diff --git a/web/tests/test_register.py b/web/tests/test_register.py index 1810858..ee7b7f6 100644 --- a/web/tests/test_register.py +++ b/web/tests/test_register.py @@ -1,17 +1,21 @@ from django.contrib.auth import get_user_model from django.shortcuts import reverse -from django.test import TestCase +from django.test import TransactionTestCase User = get_user_model() -class TestRegister(TestCase): +class TestRegister(TransactionTestCase): def setUp(self) -> None: self.password = "theonering" self.email = "sauron@mordor.middleearth" self.first_name = "Sauron" self.last_name = "The Lord of the Rings" + def test_get_register_page(self): + response = self.client.get(reverse("web:register")) + assert response.status_code == 200 + def test_create_user_account(self): response = self.client.post( reverse("web:register"), @@ -84,7 +88,7 @@ class TestRegister(TestCase): { "email": self.email, "password1": self.password, - "password2": "otherpassword", + "password2": self.password, "first_name": self.first_name, "last_name": "", }, @@ -98,3 +102,29 @@ class TestRegister(TestCase): ).exists() assert response.status_code == 400 + + def test_create_user_account_already_existing(self): + assert User.objects.all().count() == 0 + User.objects.create_user( + email=self.email, + username=self.email, + ) + response = self.client.post( + reverse("web:register"), + { + "email": self.email, + "password1": self.password, + "password2": self.password, + "first_name": self.first_name, + "last_name": self.last_name, + }, + ) + + assert not User.objects.filter( + username=self.email, + email=self.email, + first_name=self.first_name, + last_name="", + ).exists() + + assert response.status_code == 400 diff --git a/web/tests/test_wishlist.py b/web/tests/test_wishlist.py index 7a3fa5a..8a2b500 100644 --- a/web/tests/test_wishlist.py +++ b/web/tests/test_wishlist.py @@ -44,6 +44,16 @@ class TestWishlist(TestCase, CreateProductsMixin): user=self.user, product=self.product ).exists() + def test_not_logged_user_create_wishlisted_product(self): + assert WishlistedProduct.objects.count() == 0 + response = self.client.post( + reverse("web:add_to_wishlist"), + { + "product": self.product.pk, + }, + ) + assert response.status_code == 400 + def test_logged_user_delete_wishlisted_product(self): wishlisted = WishlistedProduct.objects.create( user=self.user, product=self.product diff --git a/web/views/web.py b/web/views/web.py index fcda7e2..0ab8b78 100644 --- a/web/views/web.py +++ b/web/views/web.py @@ -209,6 +209,8 @@ class OrdersView(TemplateView, FilteredQuerysetMixin, PaginatedQuerysetMixin): page = self.get_paginated_queryset(qs) return { + "title": settings.web_title, + "web_title": settings.web_title, "page": page, "has_next_page": page.has_next(), "has_previous_page": page.has_previous(),