Skip to content

Commit 209d431

Browse files
authored
Add max length limitation to open questions and raise error for when travel fields are empty and travel is selected (#4501)
1 parent 10e021c commit 209d431

File tree

5 files changed

+161
-31
lines changed

5 files changed

+161
-31
lines changed

backend/api/grants/mutations.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
11
from dataclasses import asdict
22
from enum import Enum
3-
from typing import Annotated, Union, Optional
4-
from participants.models import Participant
3+
from typing import Annotated, Optional, Union
54

6-
from privacy_policy.record import record_privacy_policy_acceptance
75
import strawberry
8-
from strawberry.types import Info
96
from django.db import transaction
10-
from api.grants.types import (
11-
AgeGroup,
12-
Grant,
13-
GrantType,
14-
Occupation,
15-
)
7+
from strawberry.types import Info
8+
9+
from api.grants.types import AgeGroup, Grant, GrantType, Occupation
1610
from api.permissions import IsAuthenticated
1711
from api.types import BaseErrorType
1812
from conferences.models.conference import Conference
19-
from grants.tasks import (
20-
notify_new_grant_reply_slack,
21-
)
2213
from grants.models import Grant as GrantModel
23-
from users.models import User
24-
from grants.tasks import get_name
14+
from grants.tasks import get_name, notify_new_grant_reply_slack
2515
from notifications.models import EmailTemplate, EmailTemplateIdentifier
16+
from participants.models import Participant
17+
from privacy_policy.record import record_privacy_policy_acceptance
18+
from users.models import User
2619

2720

2821
@strawberry.type
@@ -76,6 +69,11 @@ def validate(self, conference: Conference, user: User) -> GrantErrors:
7669
"departure_country": 100,
7770
"nationality": 100,
7871
"departure_city": 100,
72+
"why": 1000,
73+
"python_usage": 700,
74+
"been_to_other_events": 500,
75+
"community_contribution": 900,
76+
"notes": 350,
7977
}
8078
for field, max_length in max_length_fields.items():
8179
value = getattr(self, field, "")
@@ -86,13 +84,17 @@ def validate(self, conference: Conference, user: User) -> GrantErrors:
8684
f"{field}: Cannot be more than {max_length} chars",
8785
)
8886

89-
non_empty_fields = (
87+
non_empty_fields = [
9088
"full_name",
9189
"python_usage",
9290
"been_to_other_events",
9391
"why",
9492
"grant_type",
95-
)
93+
]
94+
if self.needs_funds_for_travel:
95+
non_empty_fields.extend(
96+
["departure_country", "departure_city", "nationality"]
97+
)
9698

9799
for field in non_empty_fields:
98100
value = getattr(self, field, "")

backend/api/grants/tests/test_send_grant.py

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
from privacy_policy.models import PrivacyPolicyAcceptanceRecord
2-
from conferences.tests.factories import ConferenceFactory
3-
from grants.tests.factories import GrantFactory
41
import pytest
5-
from participants.models import Participant
2+
3+
from conferences.tests.factories import ConferenceFactory
64
from grants.models import Grant
5+
from grants.tests.factories import GrantFactory
76
from notifications.models import EmailTemplateIdentifier
87
from notifications.tests.factories import EmailTemplateFactory
9-
8+
from participants.models import Participant
9+
from privacy_policy.models import PrivacyPolicyAcceptanceRecord
1010

1111
pytestmark = pytest.mark.django_db
1212

@@ -91,7 +91,9 @@ def _send_grant(client, conference, conference_code=None, **kwargs):
9191
return response
9292

9393

94-
def test_send_grant(graphql_client, user, mocker, django_capture_on_commit_callbacks, sent_emails):
94+
def test_send_grant(
95+
graphql_client, user, mocker, django_capture_on_commit_callbacks, sent_emails
96+
):
9597
graphql_client.force_login(user)
9698
conference = ConferenceFactory(active_grants=True)
9799
EmailTemplateFactory(
@@ -120,13 +122,16 @@ def test_send_grant(graphql_client, user, mocker, django_capture_on_commit_callb
120122
# Verify that the correct email template was used and email was sent
121123
emails_sent = sent_emails()
122124
assert emails_sent.count() == 1
123-
125+
124126
sent_email = emails_sent.first()
125-
assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_application_confirmation
127+
assert (
128+
sent_email.email_template.identifier
129+
== EmailTemplateIdentifier.grant_application_confirmation
130+
)
126131
assert sent_email.email_template.conference == conference
127132
assert sent_email.recipient == user
128133
assert sent_email.recipient_email == user.email
129-
134+
130135
# Verify placeholders were processed correctly
131136
assert sent_email.placeholders["user_name"] == user.full_name
132137

@@ -238,6 +243,11 @@ def test_cannot_send_grant_outside_allowed_values(
238243
departureCountry="Very long location" * 50,
239244
nationality="Freedonia" * 50,
240245
departureCity="Emerald City " * 50,
246+
why="Very long why" * 100,
247+
pythonUsage="Very long python usage" * 100,
248+
beenToOtherEvents="Very long been to other events" * 100,
249+
communityContribution="Very long community contribution" * 100,
250+
notes="Very long notes" * 100,
241251
)
242252

243253
assert response["data"]["sendGrant"]["__typename"] == "GrantErrors"
@@ -253,6 +263,21 @@ def test_cannot_send_grant_outside_allowed_values(
253263
assert response["data"]["sendGrant"]["errors"]["validationDepartureCity"] == [
254264
"departure_city: Cannot be more than 100 chars"
255265
]
266+
assert response["data"]["sendGrant"]["errors"]["validationWhy"] == [
267+
"why: Cannot be more than 1000 chars"
268+
]
269+
assert response["data"]["sendGrant"]["errors"]["validationPythonUsage"] == [
270+
"python_usage: Cannot be more than 700 chars"
271+
]
272+
assert response["data"]["sendGrant"]["errors"]["validationBeenToOtherEvents"] == [
273+
"been_to_other_events: Cannot be more than 500 chars"
274+
]
275+
assert response["data"]["sendGrant"]["errors"][
276+
"validationCommunityContribution"
277+
] == ["community_contribution: Cannot be more than 900 chars"]
278+
assert response["data"]["sendGrant"]["errors"]["validationNotes"] == [
279+
"notes: Cannot be more than 350 chars"
280+
]
256281

257282

258283
def test_cannot_send_grant_with_empty_values(
@@ -279,6 +304,36 @@ def test_cannot_send_grant_with_empty_values(
279304
]
280305

281306

307+
def test_cannot_send_grant_with_empty_values_if_needs_funds_for_travel(
308+
graphql_client,
309+
user,
310+
):
311+
graphql_client.force_login(user)
312+
conference = ConferenceFactory(
313+
active_grants=True,
314+
)
315+
316+
response = _send_grant(
317+
graphql_client,
318+
conference,
319+
needsFundsForTravel=True,
320+
departureCountry="",
321+
departureCity="",
322+
nationality="",
323+
)
324+
325+
assert response["data"]["sendGrant"]["__typename"] == "GrantErrors"
326+
assert response["data"]["sendGrant"]["errors"]["validationDepartureCountry"] == [
327+
"departure_country: Cannot be empty"
328+
]
329+
assert response["data"]["sendGrant"]["errors"]["validationDepartureCity"] == [
330+
"departure_city: Cannot be empty"
331+
]
332+
assert response["data"]["sendGrant"]["errors"]["validationNationality"] == [
333+
"nationality: Cannot be empty"
334+
]
335+
336+
282337
def test_submit_grant_with_existing_participant(graphql_client, user):
283338
graphql_client.force_login(user)
284339
conference = ConferenceFactory(

backend/api/grants/tests/test_update_grant.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def test_cannot_update_a_grant_as_unlogged_user(graphql_client):
193193
assert resp["errors"][0]["message"] == "User not logged in"
194194

195195

196-
def test_cannot_update_submission_with_lang_outside_allowed_values(
196+
def test_cannot_update_grant_outside_allowed_values(
197197
graphql_client,
198198
user,
199199
):
@@ -211,15 +211,24 @@ def test_cannot_update_submission_with_lang_outside_allowed_values(
211211
graphql_client,
212212
grant=grant,
213213
name="Marcotte" * 50,
214+
fullName="Marcotte" * 50,
214215
departureCountry="Very long location" * 50,
215216
nationality="Freedonia" * 50,
216217
departureCity="Emerald City " * 50,
218+
why="Very long why" * 100,
219+
pythonUsage="Very long python usage" * 100,
220+
beenToOtherEvents="Very long been to other events" * 100,
221+
communityContribution="Very long community contribution" * 100,
222+
notes="Very long notes" * 100,
217223
)
218224

219225
assert response["data"]["updateGrant"]["__typename"] == "GrantErrors"
220226
assert response["data"]["updateGrant"]["errors"]["validationName"] == [
221227
"name: Cannot be more than 300 chars"
222228
]
229+
assert response["data"]["updateGrant"]["errors"]["validationFullName"] == [
230+
"full_name: Cannot be more than 300 chars"
231+
]
223232
assert response["data"]["updateGrant"]["errors"]["validationDepartureCountry"] == [
224233
"departure_country: Cannot be more than 100 chars"
225234
]
@@ -229,3 +238,53 @@ def test_cannot_update_submission_with_lang_outside_allowed_values(
229238
assert response["data"]["updateGrant"]["errors"]["validationDepartureCity"] == [
230239
"departure_city: Cannot be more than 100 chars"
231240
]
241+
assert response["data"]["updateGrant"]["errors"]["validationWhy"] == [
242+
"why: Cannot be more than 1000 chars"
243+
]
244+
assert response["data"]["updateGrant"]["errors"]["validationPythonUsage"] == [
245+
"python_usage: Cannot be more than 700 chars"
246+
]
247+
assert response["data"]["updateGrant"]["errors"]["validationBeenToOtherEvents"] == [
248+
"been_to_other_events: Cannot be more than 500 chars"
249+
]
250+
assert response["data"]["updateGrant"]["errors"][
251+
"validationCommunityContribution"
252+
] == ["community_contribution: Cannot be more than 900 chars"]
253+
assert response["data"]["updateGrant"]["errors"]["validationNotes"] == [
254+
"notes: Cannot be more than 350 chars"
255+
]
256+
257+
258+
def test_cannot_update_grant_with_empty_values_if_needs_funds_for_travel(
259+
graphql_client,
260+
user,
261+
):
262+
graphql_client.force_login(user)
263+
conference = ConferenceFactory(
264+
active_grants=True,
265+
)
266+
267+
grant = GrantFactory(
268+
user_id=user.id,
269+
conference=conference,
270+
)
271+
272+
response = _update_grant(
273+
graphql_client,
274+
grant=grant,
275+
needsFundsForTravel=True,
276+
departureCountry="",
277+
departureCity="",
278+
nationality="",
279+
)
280+
281+
assert response["data"]["updateGrant"]["__typename"] == "GrantErrors"
282+
assert response["data"]["updateGrant"]["errors"]["validationDepartureCountry"] == [
283+
"departure_country: Cannot be empty"
284+
]
285+
assert response["data"]["updateGrant"]["errors"]["validationDepartureCity"] == [
286+
"departure_city: Cannot be empty"
287+
]
288+
assert response["data"]["updateGrant"]["errors"]["validationNationality"] == [
289+
"nationality: Cannot be empty"
290+
]

backend/pycon/settings/test.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
from .base import env
21
from .base import * # noqa
2+
from .base import INSTALLED_APPS, MIDDLEWARE, env
33

44
IS_RUNNING_TESTS = True
55

6+
# Disable Django Debug Toolbar in tests
7+
ENABLE_DJANGO_DEBUG_TOOLBAR = False
8+
if "debug_toolbar" in INSTALLED_APPS:
9+
INSTALLED_APPS.remove("debug_toolbar")
10+
if "debug_toolbar.middleware.DebugToolbarMiddleware" in MIDDLEWARE:
11+
MIDDLEWARE.remove("debug_toolbar.middleware.DebugToolbarMiddleware")
12+
613
SECRET_KEY = "this-key-should-only-be-used-for-tests"
714
HASHID_DEFAULT_SECRET_SALT = "only-for-tests"
815

frontend/src/components/grant-form/index.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ export const GrantForm = ({
544544
required={true}
545545
placeholder={inputPlaceholderText}
546546
errors={getErrors("why")}
547+
maxLength={1000}
547548
/>
548549
</InputWrapper>
549550
</Grid>
@@ -586,7 +587,7 @@ export const GrantForm = ({
586587
{formState.values.needsFundsForTravel === "true" && (
587588
<>
588589
<InputWrapper
589-
required={false}
590+
required={true}
590591
title={
591592
<FormattedMessage id="grants.form.fields.departureCountry" />
592593
}
@@ -615,7 +616,7 @@ export const GrantForm = ({
615616
</InputWrapper>
616617

617618
<InputWrapper
618-
required={false}
619+
required={true}
619620
title={
620621
<FormattedMessage id="grants.form.fields.departureCity" />
621622
}
@@ -658,6 +659,7 @@ export const GrantForm = ({
658659
required={true}
659660
placeholder={inputPlaceholderText}
660661
errors={getErrors("pythonUsage")}
662+
maxLength={700}
661663
/>
662664
</InputWrapper>
663665

@@ -676,6 +678,7 @@ export const GrantForm = ({
676678
required={true}
677679
errors={getErrors("beenToOtherEvents")}
678680
placeholder={inputPlaceholderText}
681+
maxLength={500}
679682
/>
680683
</InputWrapper>
681684

@@ -694,6 +697,7 @@ export const GrantForm = ({
694697
required={false}
695698
errors={getErrors("communityContribution")}
696699
placeholder={inputPlaceholderText}
700+
maxLength={900}
697701
/>
698702
</InputWrapper>
699703
</Grid>
@@ -739,6 +743,7 @@ export const GrantForm = ({
739743
{...textarea("notes")}
740744
errors={getErrors("notes")}
741745
placeholder={inputPlaceholderText}
746+
maxLength={350}
742747
/>
743748
</InputWrapper>
744749
</Grid>
@@ -753,7 +758,9 @@ export const GrantForm = ({
753758
photoRequired={false}
754759
getParticipantValidationError={(field) =>
755760
getErrors(
756-
`validationSpeaker${field[0].toUpperCase()}${field.substring(1)}` as any,
761+
`validationSpeaker${field[0].toUpperCase()}${field.substring(
762+
1,
763+
)}` as any,
757764
)
758765
}
759766
showPhotoField={false}

0 commit comments

Comments
 (0)