diff --git a/web/forms.py b/web/forms.py index 96489d524..f032d7164 100644 --- a/web/forms.py +++ b/web/forms.py @@ -843,6 +843,7 @@ class EducationalVideoForm(forms.ModelForm): ), help_text="Optional – what this video is about", ) + captcha = CaptchaField(widget=TailwindCaptchaTextInput) class Meta: model = EducationalVideo diff --git a/web/forms_additional.py b/web/forms_additional.py index 53fc35bd4..7ecdfcdef 100644 --- a/web/forms_additional.py +++ b/web/forms_additional.py @@ -1,8 +1,10 @@ +from captcha.fields import CaptchaField from django import forms from django.contrib.auth.models import User from .models import BlogComment, Course, CourseMaterial, Review, StudyGroup from .widgets import ( + TailwindCaptchaTextInput, TailwindCheckboxInput, TailwindEmailInput, TailwindFileInput, @@ -56,6 +58,7 @@ class LearningInquiryForm(forms.Form): ], widget=TailwindSelect(), ) + captcha = CaptchaField(widget=TailwindCaptchaTextInput) class TeachingInquiryForm(forms.Form): diff --git a/web/tests/test_educationalvideosupload.py b/web/tests/test_educationalvideosupload.py index 5fedb2e7a..33e745afc 100644 --- a/web/tests/test_educationalvideosupload.py +++ b/web/tests/test_educationalvideosupload.py @@ -1,4 +1,5 @@ import json +from unittest.mock import patch from django.contrib.auth import get_user_model from django.test import TestCase @@ -11,6 +12,10 @@ class EducationalVideoUploadTests(TestCase): def setUp(self): + self.captcha_patcher = patch("captcha.fields.CaptchaField.clean", return_value="PASSED") + self.captcha_patcher.start() + self.addCleanup(self.captcha_patcher.stop) + # create a user and two categories self.user = User.objects.create_user(username="tester", password="password") self.math = Subject.objects.create(name="Math", slug="math", order=1) @@ -32,6 +37,8 @@ def test_post_upload_authenticated(self): "video_url": "https://youtu.be/dQw4w9WgXcQ", "description": "A great test", "category": self.math.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data) self.assertRedirects(resp, reverse("educational_videos_list")) @@ -47,6 +54,8 @@ def test_post_upload_anonymous(self): "video_url": "https://youtu.be/dQw4w9WgXcQ", "description": "Anonymous desc", "category": self.bio.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data) self.assertRedirects(resp, reverse("educational_videos_list")) @@ -63,6 +72,8 @@ def test_quick_add_ajax_missing_category(self): "title": "Bad Quick", "description": "", "category": "", + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data, HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(resp.status_code, 400) @@ -78,6 +89,8 @@ def test_quick_add_ajax_success(self): "title": "Good Quick", "description": "Auto desc", "category": self.math.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data, HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(resp.status_code, 200) @@ -93,6 +106,8 @@ def test_youtube_embed_url_validation(self): "video_url": "https://www.youtube.com/embed/dQw4w9WgXcQ", "description": "Test embed URL", "category": self.math.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data) self.assertRedirects(resp, reverse("educational_videos_list")) @@ -106,6 +121,8 @@ def test_youtube_embed_url_no_www_validation(self): "video_url": "https://youtube.com/embed/dQw4w9WgXcQ", "description": "Test embed URL without www", "category": self.math.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data) self.assertRedirects(resp, reverse("educational_videos_list")) @@ -119,6 +136,8 @@ def test_vimeo_video_path_url_validation(self): "video_url": "https://vimeo.com/video/123456789", "description": "Test Vimeo video path URL", "category": self.bio.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data) self.assertRedirects(resp, reverse("educational_videos_list")) @@ -132,6 +151,8 @@ def test_invalid_youtube_embed_short_id(self): "video_url": "https://www.youtube.com/embed/shortid", "description": "Test invalid embed URL", "category": self.math.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data, HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(resp.status_code, 400) @@ -147,6 +168,8 @@ def test_invalid_vimeo_short_id(self): "video_url": "https://vimeo.com/video/1234567", # 7 digits, need 8+ "description": "Test invalid Vimeo URL", "category": self.bio.id, + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } resp = self.client.post(self.upload_url, data, HTTP_X_REQUESTED_WITH="XMLHttpRequest") self.assertEqual(resp.status_code, 400) diff --git a/web/tests/test_forms.py b/web/tests/test_forms.py index 9fb2f2a14..841aa240e 100644 --- a/web/tests/test_forms.py +++ b/web/tests/test_forms.py @@ -473,7 +473,8 @@ def test_invalid_message_form(self): class LearningInquiryFormTests(TestCase): - def test_valid_inquiry_form(self): + @patch("captcha.fields.CaptchaField.clean", return_value="PASSED") + def test_valid_inquiry_form(self, _mock_captcha_clean): form_data = { "name": "John Doe", "email": "john@example.com", @@ -481,11 +482,14 @@ def test_valid_inquiry_form(self): "learning_goals": "I want to learn web development", "preferred_schedule": "Weekends", "experience_level": "beginner", + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } form = LearningInquiryForm(data=form_data) self.assertTrue(form.is_valid()) - def test_invalid_inquiry_form(self): + @patch("captcha.fields.CaptchaField.clean", return_value="PASSED") + def test_invalid_inquiry_form(self, _mock_captcha_clean): form_data = { "name": "", # Name is required "email": "invalid-email", # Invalid email @@ -493,6 +497,8 @@ def test_invalid_inquiry_form(self): "learning_goals": "", # Learning goals are required "preferred_schedule": "", # Schedule is required "experience_level": "invalid_level", # Invalid level + "captcha_0": "dummy-hash", + "captcha_1": "PASSED", } form = LearningInquiryForm(data=form_data) self.assertFalse(form.is_valid())