Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion pms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django import forms
from django.forms import ModelForm

from .models import Booking, Customer
from .models import Booking, Customer, Room


class RoomSearchForm(ModelForm):
Expand Down Expand Up @@ -56,3 +56,15 @@ class Meta:
'total': forms.HiddenInput(),
'state': forms.HiddenInput(),
}


class RoomFilterForm(forms.Form):
search = forms.CharField(
required=False,
label='Buscar habitación',
max_length=100,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Ej: Room 1'
})
)
54 changes: 43 additions & 11 deletions pms/templates/rooms.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,50 @@

{% block content %}
<h1>Habitaciones del hotel</h1>
{% for room in rooms%}
<div class="row card mt-3 mb-3 hover-card bg-tr-250">
<div class="col p-3">
<div class="">
{{room.name}} ({{room.room_type__name}})
</div>
<div>
<a href="{% url 'room_details' pk=room.id%}">Ver detalles</a>

<!-- Search Form -->
<div class="card mb-4 mt-3">
<div class="card-body">
<form method="get" action="{% url 'rooms' %}">
<div class="row">
<div class="col-md-8">
{{ form.search }}
</div>
<div class="col-md-4">
<button type="submit" class="btn btn-primary">Buscar</button>
{% if search_query %}
<a href="{% url 'rooms' %}" class="btn btn-secondary">Limpiar</a>
{% endif %}
</div>
</div>
</form>
{% if search_query %}
<div class="mt-2">
<small class="text-muted">Buscando: "{{ search_query }}" - {{ rooms|length }} resultado(s) encontrado(s)</small>
</div>
{% endif %}
</div>
</div>

<!-- Room List -->
{% if rooms %}
{% for room in rooms%}
<div class="row card mt-3 mb-3 hover-card bg-tr-250">
<div class="col p-3">
<div class="">
{{room.name}} ({{room.room_type__name}})
</div>
<div>
<a href="{% url 'room_details' pk=room.id%}">Ver detalles</a>
</div>

</div>

</div>

</div>
{% endfor %}
{% endfor %}
{% else %}
<div class="alert alert-info">
No se encontraron habitaciones{% if search_query %} que coincidan con "{{ search_query }}"{% endif %}.
</div>
{% endif %}
{% endblock content%}
139 changes: 137 additions & 2 deletions pms/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,138 @@
from django.test import TestCase
from django.test import TestCase, Client
from django.urls import reverse
from .models import Room, Room_type
from .forms import RoomFilterForm

# Create your tests here.

class RoomFilterFormTests(TestCase):
"""Test cases for RoomFilterForm"""

def test_form_field_search_is_optional(self):
"""Test that search field is not required"""
form = RoomFilterForm(data={'search': ''})
self.assertTrue(form.is_valid())

def test_form_with_valid_search_query(self):
"""Test form with valid search query"""
form = RoomFilterForm(data={'search': 'Room 1'})
self.assertTrue(form.is_valid())
self.assertEqual(form.cleaned_data['search'], 'Room 1')


class RoomsViewFilterTests(TestCase):
"""Test cases for RoomsView filtering functionality"""

@classmethod
def setUpTestData(cls):
"""Set up test data for all test methods"""
# Create room types
cls.room_type_individual = Room_type.objects.create(
name='Individual',
price=20.0,
max_guests=1
)
cls.room_type_doble = Room_type.objects.create(
name='Doble',
price=30.0,
max_guests=2
)

# Create test rooms
cls.room_1_1 = Room.objects.create(
name='Room 1.1',
description='Habitación individual 1.1',
room_type=cls.room_type_individual
)
cls.room_1_2 = Room.objects.create(
name='Room 1.2',
description='Habitación individual 1.2',
room_type=cls.room_type_individual
)
cls.room_2_1 = Room.objects.create(
name='Room 2.1',
description='Habitación doble 2.1',
room_type=cls.room_type_doble
)
cls.room_2_2 = Room.objects.create(
name='Room 2.2',
description='Habitación doble 2.2',
room_type=cls.room_type_doble
)
cls.room_3_1 = Room.objects.create(
name='Room 3.1',
description='Habitación individual 3.1',
room_type=cls.room_type_individual
)

def setUp(self):
"""Set up test client"""
self.client = Client()
self.url = reverse('rooms')

def test_rooms_view_without_filter_shows_all_rooms(self):
"""Test that without filter all rooms are displayed"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['rooms']), 5)
self.assertContains(response, 'Room 1.1')
self.assertContains(response, 'Room 2.1')
self.assertContains(response, 'Room 3.1')

def test_rooms_view_with_exact_search(self):
"""Test exact room name search"""
response = self.client.get(self.url, {'search': 'Room 1.1'})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['rooms']), 1)
self.assertContains(response, 'Room 1.1')
self.assertNotContains(response, 'Room 2.1')

def test_rooms_view_with_partial_search(self):
"""Test partial room name search (e.g., 'Room 1' returns 'Room 1.1' and 'Room 1.2')"""
response = self.client.get(self.url, {'search': 'Room 1'})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['rooms']), 2)
self.assertContains(response, 'Room 1.1')
self.assertContains(response, 'Room 1.2')
self.assertNotContains(response, 'Room 2.1')

def test_rooms_view_with_no_results(self):
"""Test search with no matching rooms"""
response = self.client.get(self.url, {'search': 'NonExistent'})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['rooms']), 0)
self.assertContains(response, 'No se encontraron habitaciones')

def test_rooms_view_search_is_case_insensitive(self):
"""Test that search is case insensitive"""
response = self.client.get(self.url, {'search': 'room 1'})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['rooms']), 2)
self.assertContains(response, 'Room 1.1')
self.assertContains(response, 'Room 1.2')

def test_rooms_view_search_with_whitespace(self):
"""Test that search handles whitespace correctly"""
response = self.client.get(self.url, {'search': ' Room 2 '})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['rooms']), 2)
self.assertContains(response, 'Room 2.1')
self.assertContains(response, 'Room 2.2')

def test_rooms_view_empty_search_shows_all_rooms(self):
"""Test that empty search parameter shows all rooms"""
response = self.client.get(self.url, {'search': ''})
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['rooms']), 5)

def test_rooms_view_contains_search_form(self):
"""Test that the view includes the search form"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
self.assertIn('form', response.context)
self.assertIsInstance(response.context['form'], RoomFilterForm)

def test_rooms_view_preserves_search_query_in_context(self):
"""Test that search query is preserved in context"""
response = self.client.get(self.url, {'search': 'Room 1'})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['search_query'], 'Room 1')
18 changes: 15 additions & 3 deletions pms/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,21 @@ def get(self, request, pk):

class RoomsView(View):
def get(self, request):
# renders a list of rooms
rooms = Room.objects.all().values("name", "room_type__name", "id")
# Get search query from GET parameters (received from the search form in the template)
search_query = request.GET.get('search', '').strip()

# Filter rooms by name if search query exists
if search_query:
rooms = Room.objects.filter(name__icontains=search_query).values("name", "room_type__name", "id")
else:
rooms = Room.objects.all().values("name", "room_type__name", "id")

# Initialize form with search query to keep it in the input field
form = RoomFilterForm(initial={'search': search_query})

context = {
'rooms': rooms
'rooms': rooms,
'form': form,
'search_query': search_query
}
return render(request, "rooms.html", context)