Files
santeh-ray/items/tests.py
Azimkin 1188cef7be
All checks were successful
Build and Push Docker Image / build (push) Successful in 27s
Make product cards fully clickable
2026-06-16 00:06:21 +02:00

489 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.test import TestCase, override_settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from favorites.models import Favorite
from items.models import Category, Item, ItemImage, Subcategory
from orders.models import Cart, CartItem
@override_settings(MEDIA_URL="/media/")
class CatalogViewTests(TestCase):
def setUp(self):
self.plumbing = Category.objects.create(name="Сантехника", slug="plumbing")
self.decor = Category.objects.create(name="Декор", slug="decor")
self.sinks = Subcategory.objects.create(name="Мойки", slug="sinks", category=self.plumbing)
self.taps = Subcategory.objects.create(name="Смесители", slug="taps", category=self.plumbing)
self.decor_lights = Subcategory.objects.create(name="Подсветка", slug="lights", category=self.decor)
self.sink = Item.objects.create(
name="Кухонная мойка",
slug="kitchen-sink",
category=self.plumbing,
subcategory=self.sinks,
description="Искусственный камень, врезная на столешницу",
price="1600.00",
is_available=True,
)
self.mirror = Item.objects.create(
name="Зеркало Terra",
slug="terra-mirror",
category=self.decor,
subcategory=self.decor_lights,
description="Холодная подсветка, фронтальная",
price="200.00",
is_available=False,
)
def test_catalog_page_shows_items_from_all_categories(self):
response = self.client.get("/catalog/")
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Кухонная мойка")
self.assertContains(response, "Зеркало Terra")
def test_category_page_shows_only_selected_category_items(self):
response = self.client.get("/catalog/plumbing/")
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Кухонная мойка")
self.assertNotContains(response, "Зеркало Terra")
def test_category_page_shows_only_selected_category_subcategories(self):
response = self.client.get("/catalog/plumbing/")
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Мойки")
self.assertContains(response, "Смесители")
self.assertNotContains(response, 'href="/catalog/decor/?subcategory=lights"')
def test_subcategory_page_shows_only_selected_subcategory_items(self):
Item.objects.create(
name="Смеситель Mono",
slug="mono-tap",
category=self.plumbing,
subcategory=self.taps,
description="Однорычажный смеситель",
price="320.00",
is_available=True,
)
response = self.client.get("/catalog/plumbing/?subcategory=sinks")
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Кухонная мойка")
self.assertNotContains(response, "Смеситель Mono")
def test_catalog_search_filters_items_by_name(self):
response = self.client.get("/catalog/", {"q": "мойка"})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Кухонная мойка")
self.assertNotContains(response, "Зеркало Terra")
def test_catalog_search_is_case_insensitive(self):
response = self.client.get("/catalog/", {"q": "МОЙКА"})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Кухонная мойка")
self.assertNotContains(response, "Зеркало Terra")
def test_category_search_filters_items_within_selected_category(self):
Item.objects.create(
name="Смеситель Terra",
slug="terra-tap",
category=self.plumbing,
subcategory=self.taps,
description="Смеситель для ванной",
price="450.00",
is_available=True,
)
response = self.client.get("/catalog/plumbing/", {"q": "terra"})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Смеситель Terra")
self.assertNotContains(response, "Зеркало Terra")
def test_subcategory_search_filters_items_within_selected_subcategory(self):
Item.objects.create(
name="Terra Смеситель",
slug="terra-tap",
category=self.plumbing,
subcategory=self.taps,
description="Смеситель для раковины",
price="450.00",
is_available=True,
)
Item.objects.create(
name="Terra Мойка",
slug="terra-sink",
category=self.plumbing,
subcategory=self.sinks,
description="Кухонная мойка",
price="900.00",
is_available=True,
)
response = self.client.get("/catalog/plumbing/", {"subcategory": "sinks", "q": "terra"})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Terra Мойка")
self.assertNotContains(response, "Terra Смеситель")
def test_min_price_filters_items(self):
response = self.client.get("/catalog/", {"min_price": "300"})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Кухонная мойка")
self.assertNotContains(response, "Зеркало Terra")
def test_max_price_filters_items(self):
response = self.client.get("/catalog/", {"max_price": "300"})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Зеркало Terra")
self.assertNotContains(response, "Кухонная мойка")
def test_price_range_filters_items(self):
Item.objects.create(
name="Смеситель Mono",
slug="mono-tap",
category=self.plumbing,
subcategory=self.taps,
description="Однорычажный смеситель",
price="320.00",
is_available=True,
)
response = self.client.get("/catalog/", {"min_price": "300", "max_price": "500"})
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Смеситель Mono")
self.assertNotContains(response, "Кухонная мойка")
self.assertNotContains(response, "Зеркало Terra")
def test_header_search_redirects_to_selected_category_url(self):
response = self.client.get(
"/catalog/",
{"category": "category:plumbing", "q": "terra", "min_price": "100", "max_price": "500"},
)
self.assertRedirects(response, "/catalog/plumbing/?q=terra&min_price=100&max_price=500")
def test_header_search_redirects_to_selected_subcategory_url(self):
response = self.client.get(
"/catalog/",
{"category": "subcategory:plumbing:sinks", "q": "terra", "min_price": "100", "max_price": "500"},
)
self.assertRedirects(
response,
"/catalog/plumbing/?subcategory=sinks&q=terra&min_price=100&max_price=500",
)
def test_unknown_subcategory_returns_404(self):
response = self.client.get("/catalog/plumbing/?subcategory=unknown")
self.assertEqual(response.status_code, 404)
def test_subcategory_from_another_category_returns_404(self):
response = self.client.get("/catalog/plumbing/?subcategory=lights")
self.assertEqual(response.status_code, 404)
def test_unknown_category_returns_404(self):
response = self.client.get("/catalog/unknown/")
self.assertEqual(response.status_code, 404)
def test_sidebar_shows_all_categories_and_all_items_link(self):
response = self.client.get("/catalog/")
self.assertContains(response, "Все товары")
self.assertContains(response, "Сантехника")
self.assertContains(response, "Декор")
def test_sidebar_links_preserve_active_search_and_price_filters(self):
response = self.client.get("/catalog/plumbing/", {"q": "terra", "min_price": "100", "max_price": "500"})
self.assertContains(response, 'href="/catalog/?q=terra&min_price=100&max_price=500"')
self.assertContains(response, 'href="/catalog/plumbing/?q=terra&min_price=100&max_price=500"')
self.assertContains(
response,
'href="/catalog/plumbing/?subcategory=sinks&q=terra&min_price=100&max_price=500"',
)
def test_catalog_forms_repeat_active_search_and_price_values(self):
response = self.client.get(
"/catalog/plumbing/",
{"subcategory": "sinks", "q": "terra", "min_price": "100", "max_price": "500"},
)
self.assertContains(response, 'name="q" value="terra"', html=False)
self.assertContains(response, 'name="min_price" value="100"', html=False)
self.assertContains(response, 'name="max_price" value="500"', html=False)
def test_catalog_price_filter_renders_range_slider(self):
response = self.client.get("/catalog/")
self.assertContains(response, 'data-price-slider', html=False)
self.assertContains(response, 'type="range"', count=2, html=False)
self.assertContains(response, 'min="200"', html=False)
self.assertContains(response, 'max="1600"', html=False)
self.assertContains(response, 'src="/static/js/catalog-price-slider.js"', html=False)
def test_item_card_uses_primary_image(self):
ItemImage.objects.create(
item=self.sink,
image="items/secondary.jpg",
sort_order=0,
is_primary=False,
)
ItemImage.objects.create(
item=self.sink,
image="items/primary.jpg",
sort_order=10,
is_primary=True,
)
response = self.client.get("/catalog/")
self.assertContains(response, "/media/items/primary.jpg")
self.assertNotContains(response, "/media/items/secondary.jpg")
def test_item_card_shows_subcategory(self):
response = self.client.get("/catalog/")
self.assertContains(response, "Сантехника / Мойки")
def test_item_card_uses_first_ordered_image_without_primary(self):
ItemImage.objects.create(
item=self.sink,
image="items/second.jpg",
sort_order=20,
is_primary=False,
)
ItemImage.objects.create(
item=self.sink,
image="items/first.jpg",
sort_order=10,
is_primary=False,
)
response = self.client.get("/catalog/")
self.assertContains(response, "/media/items/first.jpg")
self.assertNotContains(response, "/media/items/second.jpg")
def test_catalog_item_card_links_to_detail_page(self):
response = self.client.get("/catalog/")
self.assertContains(response, 'href="/catalog/plumbing/kitchen-sink/"')
self.assertContains(
response,
'<a class="item-card-overlay" href="/catalog/plumbing/kitchen-sink/"',
html=False,
)
def test_sidebar_product_cards_include_full_card_links(self):
sale_item = Item.objects.create(
name="Акционная ванна",
slug="sale-bath",
category=self.plumbing,
price="900.00",
discount_price="750.00",
is_available=True,
)
new_item = Item.objects.create(
name="Новый душ",
slug="new-shower",
category=self.plumbing,
price="500.00",
is_available=True,
is_new=True,
)
response = self.client.get("/catalog/")
self.assertContains(
response,
f'<a class="sidebar-mini-card__overlay" href="/catalog/plumbing/{sale_item.slug}/"',
html=False,
)
self.assertContains(
response,
f'<a class="sidebar-mini-card__overlay" href="/catalog/plumbing/{new_item.slug}/"',
html=False,
)
def test_catalog_item_card_shows_favorite_button_for_authenticated_user(self):
user = get_user_model().objects.create_user(
username="ann",
email="ann@example.com",
password="StrongPassword123",
is_active=True,
)
self.client.force_login(user)
response = self.client.get("/catalog/")
self.assertContains(response, 'action="/favorites/toggle/"', html=False)
self.assertContains(response, "Добавить в избранное")
self.assertContains(response, 'name="item_id" value="', html=False)
def test_catalog_item_card_marks_favorited_item(self):
user = get_user_model().objects.create_user(
username="ann2",
email="ann2@example.com",
password="StrongPassword123",
is_active=True,
)
self.client.force_login(user)
Favorite.objects.create(user=user, item=self.sink)
response = self.client.get("/catalog/")
self.assertContains(response, "Удалить из избранного")
self.assertContains(response, "is-active")
def test_detail_page_shows_item_data(self):
ItemImage.objects.create(
item=self.sink,
image="items/sink-primary.jpg",
alt="Главное фото мойки",
sort_order=0,
is_primary=True,
)
response = self.client.get("/catalog/plumbing/kitchen-sink/")
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Кухонная мойка")
self.assertContains(response, "Сантехника / Мойки")
self.assertContains(response, "Искусственный камень")
self.assertContains(response, "1600,00 руб.")
self.assertContains(response, "В наличии")
self.assertContains(response, "/media/items/sink-primary.jpg")
self.assertContains(response, "Главное фото мойки")
def test_detail_page_uses_primary_image_first(self):
ItemImage.objects.create(
item=self.sink,
image="items/secondary.jpg",
sort_order=0,
is_primary=False,
)
ItemImage.objects.create(
item=self.sink,
image="items/primary.jpg",
sort_order=10,
is_primary=True,
)
response = self.client.get("/catalog/plumbing/kitchen-sink/")
self.assertContains(response, '<img src="/media/items/primary.jpg"', html=False)
def test_detail_page_unknown_item_returns_404(self):
response = self.client.get("/catalog/plumbing/unknown/")
self.assertEqual(response.status_code, 404)
def test_detail_page_shows_cart_link_for_item_already_in_cart(self):
user = get_user_model().objects.create_user(
username="cart-user",
email="cart-user@example.com",
password="StrongPassword123",
is_active=True,
)
cart = Cart.objects.create(user=user)
CartItem.objects.create(cart=cart, item=self.sink, quantity=1)
self.client.force_login(user)
response = self.client.get("/catalog/plumbing/kitchen-sink/")
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'href="/orders/cart/"', html=False)
self.assertContains(response, "Перейти в корзину")
def test_detail_page_guest_cart_cta_redirects_to_login(self):
response = self.client.get("/catalog/plumbing/kitchen-sink/")
self.assertEqual(response.status_code, 200)
self.assertContains(
response,
'href="/users/login/?next=/catalog/plumbing/kitchen-sink/"',
html=False,
)
self.assertNotContains(response, "data-guest-cart-detail-button", html=False)
def test_detail_page_guest_buy_now_link_does_not_require_login(self):
response = self.client.get("/catalog/plumbing/kitchen-sink/")
self.assertEqual(response.status_code, 200)
self.assertContains(response, f'href="/orders/buy/{self.sink.id}/"', html=False)
self.assertContains(response, "Купить")
def test_header_cart_link_redirects_guest_to_login(self):
response = self.client.get("/catalog/")
self.assertEqual(response.status_code, 200)
self.assertContains(
response,
'href="/users/login/?next=/orders/cart/"',
html=False,
)
def test_detail_page_wrong_category_returns_404(self):
response = self.client.get("/catalog/decor/kitchen-sink/")
self.assertEqual(response.status_code, 404)
def test_item_subcategory_must_belong_to_item_category(self):
item = Item(
name="Некорректный товар",
slug="invalid-item",
category=self.plumbing,
subcategory=self.decor_lights,
description="",
price="100.00",
is_available=True,
)
with self.assertRaises(ValidationError):
item.full_clean()
def test_admin_subcategories_endpoint_filters_by_category(self):
user = get_user_model().objects.create_superuser(
username="admin",
email="admin@example.com",
password="password",
)
self.client.force_login(user)
response = self.client.get(f"/admin/items/item/subcategories/?category_id={self.plumbing.id}")
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(),
{
"subcategories": [
{"id": self.sinks.id, "name": "Мойки"},
{"id": self.taps.id, "name": "Смесители"},
]
},
)
def test_admin_subcategories_endpoint_returns_empty_without_category(self):
user = get_user_model().objects.create_superuser(
username="admin",
email="admin@example.com",
password="password",
)
self.client.force_login(user)
response = self.client.get("/admin/items/item/subcategories/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"subcategories": []})