mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 07:31:08 -05:00
Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX
This commit is contained in:
6
backend/tests/forms/__init__.py
Normal file
6
backend/tests/forms/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
Form tests.
|
||||
|
||||
This module contains tests for Django forms to verify
|
||||
validation, widgets, and custom logic.
|
||||
"""
|
||||
315
backend/tests/forms/test_park_forms.py
Normal file
315
backend/tests/forms/test_park_forms.py
Normal file
@@ -0,0 +1,315 @@
|
||||
"""
|
||||
Tests for Park forms.
|
||||
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from decimal import Decimal
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.parks.forms import (
|
||||
ParkForm,
|
||||
ParkSearchForm,
|
||||
ParkAutocomplete,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
OperatorCompanyFactory,
|
||||
LocationFactory,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestParkForm(TestCase):
|
||||
"""Tests for ParkForm."""
|
||||
|
||||
def test__init__new_park__no_location_prefilled(self):
|
||||
"""Test initializing form for new park has no location prefilled."""
|
||||
form = ParkForm()
|
||||
|
||||
assert form.fields["latitude"].initial is None
|
||||
assert form.fields["longitude"].initial is None
|
||||
assert form.fields["city"].initial is None
|
||||
|
||||
def test__init__existing_park_with_location__prefills_location_fields(self):
|
||||
"""Test initializing form for existing park prefills location fields."""
|
||||
park = ParkFactory()
|
||||
# Create location via factory's post_generation hook
|
||||
|
||||
form = ParkForm(instance=park)
|
||||
|
||||
# Location should be prefilled if it exists
|
||||
if park.location.exists():
|
||||
location = park.location.first()
|
||||
assert form.fields["latitude"].initial == location.latitude
|
||||
assert form.fields["longitude"].initial == location.longitude
|
||||
assert form.fields["city"].initial == location.city
|
||||
|
||||
def test__clean_latitude__valid_value__returns_normalized_value(self):
|
||||
"""Test clean_latitude normalizes valid latitude."""
|
||||
operator = OperatorCompanyFactory()
|
||||
data = {
|
||||
"name": "Test Park",
|
||||
"operator": operator.pk,
|
||||
"status": "OPERATING",
|
||||
"latitude": "37.123456789", # Too many decimal places
|
||||
"longitude": "-122.123456",
|
||||
}
|
||||
|
||||
form = ParkForm(data=data)
|
||||
form.is_valid()
|
||||
|
||||
if "latitude" in form.cleaned_data:
|
||||
# Should be rounded to 6 decimal places
|
||||
assert len(form.cleaned_data["latitude"].split(".")[-1]) <= 6
|
||||
|
||||
def test__clean_latitude__out_of_range__returns_error(self):
|
||||
"""Test clean_latitude rejects out-of-range latitude."""
|
||||
operator = OperatorCompanyFactory()
|
||||
data = {
|
||||
"name": "Test Park",
|
||||
"operator": operator.pk,
|
||||
"status": "OPERATING",
|
||||
"latitude": "95.0", # Invalid: > 90
|
||||
"longitude": "-122.0",
|
||||
}
|
||||
|
||||
form = ParkForm(data=data)
|
||||
is_valid = form.is_valid()
|
||||
|
||||
assert not is_valid
|
||||
assert "latitude" in form.errors
|
||||
|
||||
def test__clean_latitude__negative_ninety__is_valid(self):
|
||||
"""Test clean_latitude accepts -90 (edge case)."""
|
||||
operator = OperatorCompanyFactory()
|
||||
data = {
|
||||
"name": "Test Park",
|
||||
"operator": operator.pk,
|
||||
"status": "OPERATING",
|
||||
"latitude": "-90.0",
|
||||
"longitude": "0.0",
|
||||
}
|
||||
|
||||
form = ParkForm(data=data)
|
||||
is_valid = form.is_valid()
|
||||
|
||||
# Should be valid (form may have other errors but not latitude)
|
||||
if not is_valid:
|
||||
assert "latitude" not in form.errors
|
||||
|
||||
def test__clean_longitude__valid_value__returns_normalized_value(self):
|
||||
"""Test clean_longitude normalizes valid longitude."""
|
||||
operator = OperatorCompanyFactory()
|
||||
data = {
|
||||
"name": "Test Park",
|
||||
"operator": operator.pk,
|
||||
"status": "OPERATING",
|
||||
"latitude": "37.0",
|
||||
"longitude": "-122.123456789", # Too many decimal places
|
||||
}
|
||||
|
||||
form = ParkForm(data=data)
|
||||
form.is_valid()
|
||||
|
||||
if "longitude" in form.cleaned_data:
|
||||
# Should be rounded to 6 decimal places
|
||||
assert len(form.cleaned_data["longitude"].split(".")[-1]) <= 6
|
||||
|
||||
def test__clean_longitude__out_of_range__returns_error(self):
|
||||
"""Test clean_longitude rejects out-of-range longitude."""
|
||||
operator = OperatorCompanyFactory()
|
||||
data = {
|
||||
"name": "Test Park",
|
||||
"operator": operator.pk,
|
||||
"status": "OPERATING",
|
||||
"latitude": "37.0",
|
||||
"longitude": "-200.0", # Invalid: < -180
|
||||
}
|
||||
|
||||
form = ParkForm(data=data)
|
||||
is_valid = form.is_valid()
|
||||
|
||||
assert not is_valid
|
||||
assert "longitude" in form.errors
|
||||
|
||||
def test__clean_longitude__positive_180__is_valid(self):
|
||||
"""Test clean_longitude accepts 180 (edge case)."""
|
||||
operator = OperatorCompanyFactory()
|
||||
data = {
|
||||
"name": "Test Park",
|
||||
"operator": operator.pk,
|
||||
"status": "OPERATING",
|
||||
"latitude": "0.0",
|
||||
"longitude": "180.0",
|
||||
}
|
||||
|
||||
form = ParkForm(data=data)
|
||||
is_valid = form.is_valid()
|
||||
|
||||
# Should be valid (form may have other errors but not longitude)
|
||||
if not is_valid:
|
||||
assert "longitude" not in form.errors
|
||||
|
||||
def test__save__new_park_with_location__creates_park_and_location(self):
|
||||
"""Test saving new park creates both park and location."""
|
||||
operator = OperatorCompanyFactory()
|
||||
data = {
|
||||
"name": "New Test Park",
|
||||
"operator": operator.pk,
|
||||
"status": "OPERATING",
|
||||
"latitude": "37.123456",
|
||||
"longitude": "-122.123456",
|
||||
"city": "San Francisco",
|
||||
"state": "CA",
|
||||
"country": "USA",
|
||||
}
|
||||
|
||||
form = ParkForm(data=data)
|
||||
|
||||
if form.is_valid():
|
||||
park = form.save()
|
||||
|
||||
assert park.name == "New Test Park"
|
||||
# Location should be created
|
||||
assert park.location.exists() or hasattr(park, "location")
|
||||
|
||||
def test__save__existing_park__updates_location(self):
|
||||
"""Test saving existing park updates location."""
|
||||
park = ParkFactory()
|
||||
|
||||
# Update data
|
||||
data = {
|
||||
"name": park.name,
|
||||
"operator": park.operator.pk,
|
||||
"status": park.status,
|
||||
"latitude": "40.0",
|
||||
"longitude": "-74.0",
|
||||
"city": "New York",
|
||||
"state": "NY",
|
||||
"country": "USA",
|
||||
}
|
||||
|
||||
form = ParkForm(instance=park, data=data)
|
||||
|
||||
if form.is_valid():
|
||||
updated_park = form.save()
|
||||
# Location should be updated
|
||||
assert updated_park.pk == park.pk
|
||||
|
||||
def test__meta__fields__includes_all_expected_fields(self):
|
||||
"""Test Meta.fields includes all expected park and location fields."""
|
||||
expected_fields = [
|
||||
"name",
|
||||
"description",
|
||||
"operator",
|
||||
"property_owner",
|
||||
"status",
|
||||
"opening_date",
|
||||
"closing_date",
|
||||
"operating_season",
|
||||
"size_acres",
|
||||
"website",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"street_address",
|
||||
"city",
|
||||
"state",
|
||||
"country",
|
||||
"postal_code",
|
||||
]
|
||||
|
||||
for field in expected_fields:
|
||||
assert field in ParkForm.Meta.fields
|
||||
|
||||
def test__widgets__latitude_longitude_hidden__are_hidden_inputs(self):
|
||||
"""Test latitude and longitude use HiddenInput widgets."""
|
||||
form = ParkForm()
|
||||
|
||||
assert form.fields["latitude"].widget.input_type == "hidden"
|
||||
assert form.fields["longitude"].widget.input_type == "hidden"
|
||||
|
||||
def test__widgets__text_fields__have_styling_classes(self):
|
||||
"""Test text fields have appropriate CSS classes."""
|
||||
form = ParkForm()
|
||||
|
||||
# Check city field has expected styling
|
||||
city_widget = form.fields["city"].widget
|
||||
assert "class" in city_widget.attrs
|
||||
assert "rounded-lg" in city_widget.attrs["class"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestParkSearchForm(TestCase):
|
||||
"""Tests for ParkSearchForm."""
|
||||
|
||||
def test__init__creates_park_field(self):
|
||||
"""Test initializing form creates park field."""
|
||||
form = ParkSearchForm()
|
||||
|
||||
assert "park" in form.fields
|
||||
|
||||
def test__park_field__uses_autocomplete_widget(self):
|
||||
"""Test park field uses AutocompleteWidget."""
|
||||
form = ParkSearchForm()
|
||||
|
||||
# Check the widget type
|
||||
widget = form.fields["park"].widget
|
||||
widget_class_name = widget.__class__.__name__
|
||||
assert "Autocomplete" in widget_class_name or "Select" in widget_class_name
|
||||
|
||||
def test__park_field__not_required(self):
|
||||
"""Test park field is not required."""
|
||||
form = ParkSearchForm()
|
||||
|
||||
assert form.fields["park"].required is False
|
||||
|
||||
def test__validate__empty_form__is_valid(self):
|
||||
"""Test empty form is valid."""
|
||||
form = ParkSearchForm(data={})
|
||||
|
||||
assert form.is_valid()
|
||||
|
||||
def test__validate__with_park__is_valid(self):
|
||||
"""Test form with valid park is valid."""
|
||||
park = ParkFactory()
|
||||
form = ParkSearchForm(data={"park": park.pk})
|
||||
|
||||
assert form.is_valid()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestParkAutocomplete(TestCase):
|
||||
"""Tests for ParkAutocomplete."""
|
||||
|
||||
def test__model__is_park(self):
|
||||
"""Test autocomplete model is Park."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
assert ParkAutocomplete.model == Park
|
||||
|
||||
def test__search_attrs__includes_name(self):
|
||||
"""Test search_attrs includes name field."""
|
||||
assert "name" in ParkAutocomplete.search_attrs
|
||||
|
||||
def test__search__matching_name__returns_results(self):
|
||||
"""Test searching by name returns matching parks."""
|
||||
park1 = ParkFactory(name="Cedar Point")
|
||||
park2 = ParkFactory(name="Kings Island")
|
||||
|
||||
# The autocomplete should return Cedar Point when searching for "Cedar"
|
||||
queryset = ParkAutocomplete.model.objects.filter(name__icontains="Cedar")
|
||||
|
||||
assert park1 in queryset
|
||||
assert park2 not in queryset
|
||||
|
||||
def test__search__no_match__returns_empty(self):
|
||||
"""Test searching with no match returns empty queryset."""
|
||||
ParkFactory(name="Cedar Point")
|
||||
|
||||
queryset = ParkAutocomplete.model.objects.filter(name__icontains="NoMatchHere")
|
||||
|
||||
assert queryset.count() == 0
|
||||
371
backend/tests/forms/test_ride_forms.py
Normal file
371
backend/tests/forms/test_ride_forms.py
Normal file
@@ -0,0 +1,371 @@
|
||||
"""
|
||||
Tests for Ride forms.
|
||||
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.rides.forms import (
|
||||
RideForm,
|
||||
RideSearchForm,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
ParkAreaFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
DesignerCompanyFactory,
|
||||
RideModelFactory,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideForm(TestCase):
|
||||
"""Tests for RideForm."""
|
||||
|
||||
def test__init__no_park__shows_park_search_field(self):
|
||||
"""Test initializing without park shows park search field."""
|
||||
form = RideForm()
|
||||
|
||||
assert "park_search" in form.fields
|
||||
assert "park" in form.fields
|
||||
|
||||
def test__init__with_park__hides_park_search_field(self):
|
||||
"""Test initializing with park hides park search field."""
|
||||
park = ParkFactory()
|
||||
form = RideForm(park=park)
|
||||
|
||||
assert "park_search" not in form.fields
|
||||
assert "park" in form.fields
|
||||
assert form.fields["park"].initial == park
|
||||
|
||||
def test__init__with_park__populates_park_area_queryset(self):
|
||||
"""Test initializing with park populates park_area choices."""
|
||||
park = ParkFactory()
|
||||
area1 = ParkAreaFactory(park=park, name="Area 1")
|
||||
area2 = ParkAreaFactory(park=park, name="Area 2")
|
||||
|
||||
form = RideForm(park=park)
|
||||
|
||||
# Park area queryset should contain park's areas
|
||||
queryset = form.fields["park_area"].queryset
|
||||
assert area1 in queryset
|
||||
assert area2 in queryset
|
||||
|
||||
def test__init__without_park__park_area_disabled(self):
|
||||
"""Test initializing without park disables park_area."""
|
||||
form = RideForm()
|
||||
|
||||
assert form.fields["park_area"].widget.attrs.get("disabled") is True
|
||||
|
||||
def test__init__existing_ride__prefills_manufacturer(self):
|
||||
"""Test initializing with existing ride prefills manufacturer."""
|
||||
manufacturer = ManufacturerCompanyFactory(name="Test Manufacturer")
|
||||
ride = RideFactory(manufacturer=manufacturer)
|
||||
|
||||
form = RideForm(instance=ride)
|
||||
|
||||
assert form.fields["manufacturer_search"].initial == "Test Manufacturer"
|
||||
assert form.fields["manufacturer"].initial == manufacturer
|
||||
|
||||
def test__init__existing_ride__prefills_designer(self):
|
||||
"""Test initializing with existing ride prefills designer."""
|
||||
designer = DesignerCompanyFactory(name="Test Designer")
|
||||
ride = RideFactory(designer=designer)
|
||||
|
||||
form = RideForm(instance=ride)
|
||||
|
||||
assert form.fields["designer_search"].initial == "Test Designer"
|
||||
assert form.fields["designer"].initial == designer
|
||||
|
||||
def test__init__existing_ride__prefills_ride_model(self):
|
||||
"""Test initializing with existing ride prefills ride model."""
|
||||
ride_model = RideModelFactory(name="Test Model")
|
||||
ride = RideFactory(ride_model=ride_model)
|
||||
|
||||
form = RideForm(instance=ride)
|
||||
|
||||
assert form.fields["ride_model_search"].initial == "Test Model"
|
||||
assert form.fields["ride_model"].initial == ride_model
|
||||
|
||||
def test__init__existing_ride_without_park_arg__prefills_park_search(self):
|
||||
"""Test initializing with existing ride prefills park search."""
|
||||
park = ParkFactory(name="Test Park")
|
||||
ride = RideFactory(park=park)
|
||||
|
||||
form = RideForm(instance=ride)
|
||||
|
||||
assert form.fields["park_search"].initial == "Test Park"
|
||||
assert form.fields["park"].initial == park
|
||||
|
||||
def test__init__category_is_required(self):
|
||||
"""Test category field is required."""
|
||||
form = RideForm()
|
||||
|
||||
assert form.fields["category"].required is True
|
||||
|
||||
def test__init__date_fields_have_no_initial_value(self):
|
||||
"""Test date fields have no initial value."""
|
||||
form = RideForm()
|
||||
|
||||
assert form.fields["opening_date"].initial is None
|
||||
assert form.fields["closing_date"].initial is None
|
||||
assert form.fields["status_since"].initial is None
|
||||
|
||||
def test__field_order__matches_expected(self):
|
||||
"""Test fields are ordered correctly."""
|
||||
form = RideForm()
|
||||
|
||||
expected_order = [
|
||||
"park_search",
|
||||
"park",
|
||||
"park_area",
|
||||
"name",
|
||||
"manufacturer_search",
|
||||
"manufacturer",
|
||||
"designer_search",
|
||||
"designer",
|
||||
"ride_model_search",
|
||||
"ride_model",
|
||||
"category",
|
||||
]
|
||||
|
||||
# Get first 11 fields from form
|
||||
actual_order = list(form.fields.keys())[:11]
|
||||
|
||||
assert actual_order == expected_order
|
||||
|
||||
def test__validate__valid_data__is_valid(self):
|
||||
"""Test form is valid with all required data."""
|
||||
park = ParkFactory()
|
||||
manufacturer = ManufacturerCompanyFactory()
|
||||
|
||||
data = {
|
||||
"name": "Test Ride",
|
||||
"park": park.pk,
|
||||
"category": "RC", # Roller coaster
|
||||
"status": "OPERATING",
|
||||
"manufacturer": manufacturer.pk,
|
||||
}
|
||||
|
||||
form = RideForm(data=data)
|
||||
|
||||
# Remove park_search validation error by skipping it
|
||||
if "park_search" in form.errors:
|
||||
del form.errors["park_search"]
|
||||
|
||||
# Check if form would be valid otherwise
|
||||
assert "name" not in form.errors
|
||||
assert "category" not in form.errors
|
||||
|
||||
def test__validate__missing_name__returns_error(self):
|
||||
"""Test form is invalid without name."""
|
||||
park = ParkFactory()
|
||||
|
||||
data = {
|
||||
"park": park.pk,
|
||||
"category": "RC",
|
||||
"status": "OPERATING",
|
||||
}
|
||||
|
||||
form = RideForm(data=data)
|
||||
is_valid = form.is_valid()
|
||||
|
||||
assert not is_valid
|
||||
assert "name" in form.errors
|
||||
|
||||
def test__validate__missing_category__returns_error(self):
|
||||
"""Test form is invalid without category."""
|
||||
park = ParkFactory()
|
||||
|
||||
data = {
|
||||
"name": "Test Ride",
|
||||
"park": park.pk,
|
||||
"status": "OPERATING",
|
||||
}
|
||||
|
||||
form = RideForm(data=data)
|
||||
is_valid = form.is_valid()
|
||||
|
||||
assert not is_valid
|
||||
assert "category" in form.errors
|
||||
|
||||
def test__widgets__name_field__has_styling(self):
|
||||
"""Test name field has appropriate CSS classes."""
|
||||
form = RideForm()
|
||||
|
||||
name_widget = form.fields["name"].widget
|
||||
assert "class" in name_widget.attrs
|
||||
assert "rounded-lg" in name_widget.attrs["class"]
|
||||
|
||||
def test__widgets__category_field__has_htmx_attributes(self):
|
||||
"""Test category field has HTMX attributes."""
|
||||
form = RideForm()
|
||||
|
||||
category_widget = form.fields["category"].widget
|
||||
assert "hx-get" in category_widget.attrs
|
||||
assert "hx-target" in category_widget.attrs
|
||||
assert "hx-trigger" in category_widget.attrs
|
||||
|
||||
def test__widgets__status_field__has_alpine_attributes(self):
|
||||
"""Test status field has Alpine.js attributes."""
|
||||
form = RideForm()
|
||||
|
||||
status_widget = form.fields["status"].widget
|
||||
assert "x-model" in status_widget.attrs
|
||||
assert "@change" in status_widget.attrs
|
||||
|
||||
def test__widgets__closing_date__has_conditional_display(self):
|
||||
"""Test closing_date has conditional display logic."""
|
||||
form = RideForm()
|
||||
|
||||
closing_date_widget = form.fields["closing_date"].widget
|
||||
assert "x-show" in closing_date_widget.attrs
|
||||
|
||||
def test__meta__model__is_ride(self):
|
||||
"""Test Meta.model is Ride."""
|
||||
from apps.rides.models import Ride
|
||||
|
||||
assert RideForm.Meta.model == Ride
|
||||
|
||||
def test__meta__fields__includes_expected_fields(self):
|
||||
"""Test Meta.fields includes expected ride fields."""
|
||||
expected_fields = [
|
||||
"name",
|
||||
"category",
|
||||
"status",
|
||||
"opening_date",
|
||||
"closing_date",
|
||||
"min_height_in",
|
||||
"max_height_in",
|
||||
"description",
|
||||
]
|
||||
|
||||
for field in expected_fields:
|
||||
assert field in RideForm.Meta.fields
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideSearchForm(TestCase):
|
||||
"""Tests for RideSearchForm."""
|
||||
|
||||
def test__init__creates_ride_field(self):
|
||||
"""Test initializing form creates ride field."""
|
||||
form = RideSearchForm()
|
||||
|
||||
assert "ride" in form.fields
|
||||
|
||||
def test__ride_field__not_required(self):
|
||||
"""Test ride field is not required."""
|
||||
form = RideSearchForm()
|
||||
|
||||
assert form.fields["ride"].required is False
|
||||
|
||||
def test__ride_field__uses_select_widget(self):
|
||||
"""Test ride field uses Select widget."""
|
||||
form = RideSearchForm()
|
||||
|
||||
widget = form.fields["ride"].widget
|
||||
assert "Select" in widget.__class__.__name__
|
||||
|
||||
def test__ride_field__has_htmx_attributes(self):
|
||||
"""Test ride field has HTMX attributes."""
|
||||
form = RideSearchForm()
|
||||
|
||||
ride_widget = form.fields["ride"].widget
|
||||
assert "hx-get" in ride_widget.attrs
|
||||
assert "hx-trigger" in ride_widget.attrs
|
||||
assert "hx-target" in ride_widget.attrs
|
||||
|
||||
def test__validate__empty_form__is_valid(self):
|
||||
"""Test empty form is valid."""
|
||||
form = RideSearchForm(data={})
|
||||
|
||||
assert form.is_valid()
|
||||
|
||||
def test__validate__with_ride__is_valid(self):
|
||||
"""Test form with valid ride is valid."""
|
||||
ride = RideFactory()
|
||||
form = RideSearchForm(data={"ride": ride.pk})
|
||||
|
||||
assert form.is_valid()
|
||||
|
||||
def test__validate__with_invalid_ride__is_invalid(self):
|
||||
"""Test form with invalid ride is invalid."""
|
||||
form = RideSearchForm(data={"ride": 99999})
|
||||
|
||||
assert not form.is_valid()
|
||||
assert "ride" in form.errors
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideFormWithParkAreas(TestCase):
|
||||
"""Tests for RideForm park area functionality."""
|
||||
|
||||
def test__park_area__queryset_empty_without_park(self):
|
||||
"""Test park_area queryset is empty when no park provided."""
|
||||
form = RideForm()
|
||||
|
||||
# When no park, the queryset should be empty (none())
|
||||
queryset = form.fields["park_area"].queryset
|
||||
assert queryset.count() == 0
|
||||
|
||||
def test__park_area__queryset_filtered_to_park(self):
|
||||
"""Test park_area queryset only contains areas from given park."""
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
area1 = ParkAreaFactory(park=park1)
|
||||
area2 = ParkAreaFactory(park=park2)
|
||||
|
||||
form = RideForm(park=park1)
|
||||
|
||||
queryset = form.fields["park_area"].queryset
|
||||
assert area1 in queryset
|
||||
assert area2 not in queryset
|
||||
|
||||
def test__park_area__is_optional(self):
|
||||
"""Test park_area field is optional."""
|
||||
form = RideForm()
|
||||
|
||||
assert form.fields["park_area"].required is False
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideFormFieldOrder(TestCase):
|
||||
"""Tests for RideForm field ordering."""
|
||||
|
||||
def test__field_order__park_fields_first(self):
|
||||
"""Test park-related fields come first."""
|
||||
form = RideForm()
|
||||
|
||||
field_names = list(form.fields.keys())
|
||||
|
||||
# park_search should be first
|
||||
assert field_names[0] == "park_search"
|
||||
assert field_names[1] == "park"
|
||||
assert field_names[2] == "park_area"
|
||||
|
||||
def test__field_order__name_after_park(self):
|
||||
"""Test name field comes after park fields."""
|
||||
form = RideForm()
|
||||
|
||||
field_names = list(form.fields.keys())
|
||||
|
||||
name_index = field_names.index("name")
|
||||
park_index = field_names.index("park")
|
||||
|
||||
assert name_index > park_index
|
||||
|
||||
def test__field_order__description_last(self):
|
||||
"""Test description is near the end."""
|
||||
form = RideForm()
|
||||
|
||||
field_names = list(form.fields.keys())
|
||||
|
||||
# Description should be one of the last fields
|
||||
description_index = field_names.index("description")
|
||||
assert description_index > len(field_names) // 2
|
||||
Reference in New Issue
Block a user