Skip to content
Merged
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
36 changes: 19 additions & 17 deletions backend/api/grants/mutations.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
from dataclasses import asdict
from enum import Enum
from typing import Annotated, Union, Optional
from participants.models import Participant
from typing import Annotated, Optional, Union

from privacy_policy.record import record_privacy_policy_acceptance
import strawberry
from strawberry.types import Info
from django.db import transaction
from api.grants.types import (
AgeGroup,
Grant,
GrantType,
Occupation,
)
from strawberry.types import Info

from api.grants.types import AgeGroup, Grant, GrantType, Occupation
from api.permissions import IsAuthenticated
from api.types import BaseErrorType
from conferences.models.conference import Conference
from grants.tasks import (
notify_new_grant_reply_slack,
)
from grants.models import Grant as GrantModel
from users.models import User
from grants.tasks import get_name
from grants.tasks import get_name, notify_new_grant_reply_slack
from notifications.models import EmailTemplate, EmailTemplateIdentifier
from participants.models import Participant
from privacy_policy.record import record_privacy_policy_acceptance
from users.models import User


@strawberry.type
Expand Down Expand Up @@ -76,6 +69,11 @@ def validate(self, conference: Conference, user: User) -> GrantErrors:
"departure_country": 100,
"nationality": 100,
"departure_city": 100,
"why": 1000,
"python_usage": 700,
"been_to_other_events": 500,
"community_contribution": 900,
"notes": 350,
}
for field, max_length in max_length_fields.items():
value = getattr(self, field, "")
Expand All @@ -86,13 +84,17 @@ def validate(self, conference: Conference, user: User) -> GrantErrors:
f"{field}: Cannot be more than {max_length} chars",
)

non_empty_fields = (
non_empty_fields = [
"full_name",
"python_usage",
"been_to_other_events",
"why",
"grant_type",
)
]
if self.needs_funds_for_travel:
non_empty_fields.extend(
["departure_country", "departure_city", "nationality"]
)

for field in non_empty_fields:
value = getattr(self, field, "")
Expand Down
73 changes: 64 additions & 9 deletions backend/api/grants/tests/test_send_grant.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from privacy_policy.models import PrivacyPolicyAcceptanceRecord
from conferences.tests.factories import ConferenceFactory
from grants.tests.factories import GrantFactory
import pytest
from participants.models import Participant

from conferences.tests.factories import ConferenceFactory
from grants.models import Grant
from grants.tests.factories import GrantFactory
from notifications.models import EmailTemplateIdentifier
from notifications.tests.factories import EmailTemplateFactory

from participants.models import Participant
from privacy_policy.models import PrivacyPolicyAcceptanceRecord

pytestmark = pytest.mark.django_db

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


def test_send_grant(graphql_client, user, mocker, django_capture_on_commit_callbacks, sent_emails):
def test_send_grant(
graphql_client, user, mocker, django_capture_on_commit_callbacks, sent_emails
):
graphql_client.force_login(user)
conference = ConferenceFactory(active_grants=True)
EmailTemplateFactory(
Expand Down Expand Up @@ -120,13 +122,16 @@ def test_send_grant(graphql_client, user, mocker, django_capture_on_commit_callb
# Verify that the correct email template was used and email was sent
emails_sent = sent_emails()
assert emails_sent.count() == 1

sent_email = emails_sent.first()
assert sent_email.email_template.identifier == EmailTemplateIdentifier.grant_application_confirmation
assert (
sent_email.email_template.identifier
== EmailTemplateIdentifier.grant_application_confirmation
)
assert sent_email.email_template.conference == conference
assert sent_email.recipient == user
assert sent_email.recipient_email == user.email

# Verify placeholders were processed correctly
assert sent_email.placeholders["user_name"] == user.full_name

Expand Down Expand Up @@ -238,6 +243,11 @@ def test_cannot_send_grant_outside_allowed_values(
departureCountry="Very long location" * 50,
nationality="Freedonia" * 50,
departureCity="Emerald City " * 50,
why="Very long why" * 100,
pythonUsage="Very long python usage" * 100,
beenToOtherEvents="Very long been to other events" * 100,
communityContribution="Very long community contribution" * 100,
notes="Very long notes" * 100,
)

assert response["data"]["sendGrant"]["__typename"] == "GrantErrors"
Expand All @@ -253,6 +263,21 @@ def test_cannot_send_grant_outside_allowed_values(
assert response["data"]["sendGrant"]["errors"]["validationDepartureCity"] == [
"departure_city: Cannot be more than 100 chars"
]
assert response["data"]["sendGrant"]["errors"]["validationWhy"] == [
"why: Cannot be more than 1000 chars"
]
assert response["data"]["sendGrant"]["errors"]["validationPythonUsage"] == [
"python_usage: Cannot be more than 700 chars"
]
assert response["data"]["sendGrant"]["errors"]["validationBeenToOtherEvents"] == [
"been_to_other_events: Cannot be more than 500 chars"
]
assert response["data"]["sendGrant"]["errors"][
"validationCommunityContribution"
] == ["community_contribution: Cannot be more than 900 chars"]
assert response["data"]["sendGrant"]["errors"]["validationNotes"] == [
"notes: Cannot be more than 350 chars"
]


def test_cannot_send_grant_with_empty_values(
Expand All @@ -279,6 +304,36 @@ def test_cannot_send_grant_with_empty_values(
]


def test_cannot_send_grant_with_empty_values_if_needs_funds_for_travel(
graphql_client,
user,
):
graphql_client.force_login(user)
conference = ConferenceFactory(
active_grants=True,
)

response = _send_grant(
graphql_client,
conference,
needsFundsForTravel=True,
departureCountry="",
departureCity="",
nationality="",
)

assert response["data"]["sendGrant"]["__typename"] == "GrantErrors"
assert response["data"]["sendGrant"]["errors"]["validationDepartureCountry"] == [
"departure_country: Cannot be empty"
]
assert response["data"]["sendGrant"]["errors"]["validationDepartureCity"] == [
"departure_city: Cannot be empty"
]
assert response["data"]["sendGrant"]["errors"]["validationNationality"] == [
"nationality: Cannot be empty"
]


def test_submit_grant_with_existing_participant(graphql_client, user):
graphql_client.force_login(user)
conference = ConferenceFactory(
Expand Down
61 changes: 60 additions & 1 deletion backend/api/grants/tests/test_update_grant.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def test_cannot_update_a_grant_as_unlogged_user(graphql_client):
assert resp["errors"][0]["message"] == "User not logged in"


def test_cannot_update_submission_with_lang_outside_allowed_values(
def test_cannot_update_grant_outside_allowed_values(
graphql_client,
user,
):
Expand All @@ -211,15 +211,24 @@ def test_cannot_update_submission_with_lang_outside_allowed_values(
graphql_client,
grant=grant,
name="Marcotte" * 50,
fullName="Marcotte" * 50,
departureCountry="Very long location" * 50,
nationality="Freedonia" * 50,
departureCity="Emerald City " * 50,
why="Very long why" * 100,
pythonUsage="Very long python usage" * 100,
beenToOtherEvents="Very long been to other events" * 100,
communityContribution="Very long community contribution" * 100,
notes="Very long notes" * 100,
)

assert response["data"]["updateGrant"]["__typename"] == "GrantErrors"
assert response["data"]["updateGrant"]["errors"]["validationName"] == [
"name: Cannot be more than 300 chars"
]
assert response["data"]["updateGrant"]["errors"]["validationFullName"] == [
"full_name: Cannot be more than 300 chars"
]
assert response["data"]["updateGrant"]["errors"]["validationDepartureCountry"] == [
"departure_country: Cannot be more than 100 chars"
]
Expand All @@ -229,3 +238,53 @@ def test_cannot_update_submission_with_lang_outside_allowed_values(
assert response["data"]["updateGrant"]["errors"]["validationDepartureCity"] == [
"departure_city: Cannot be more than 100 chars"
]
assert response["data"]["updateGrant"]["errors"]["validationWhy"] == [
"why: Cannot be more than 1000 chars"
]
assert response["data"]["updateGrant"]["errors"]["validationPythonUsage"] == [
"python_usage: Cannot be more than 700 chars"
]
assert response["data"]["updateGrant"]["errors"]["validationBeenToOtherEvents"] == [
"been_to_other_events: Cannot be more than 500 chars"
]
assert response["data"]["updateGrant"]["errors"][
"validationCommunityContribution"
] == ["community_contribution: Cannot be more than 900 chars"]
assert response["data"]["updateGrant"]["errors"]["validationNotes"] == [
"notes: Cannot be more than 350 chars"
]


def test_cannot_update_grant_with_empty_values_if_needs_funds_for_travel(
graphql_client,
user,
):
graphql_client.force_login(user)
conference = ConferenceFactory(
active_grants=True,
)

grant = GrantFactory(
user_id=user.id,
conference=conference,
)

response = _update_grant(
graphql_client,
grant=grant,
needsFundsForTravel=True,
departureCountry="",
departureCity="",
nationality="",
)

assert response["data"]["updateGrant"]["__typename"] == "GrantErrors"
assert response["data"]["updateGrant"]["errors"]["validationDepartureCountry"] == [
"departure_country: Cannot be empty"
]
assert response["data"]["updateGrant"]["errors"]["validationDepartureCity"] == [
"departure_city: Cannot be empty"
]
assert response["data"]["updateGrant"]["errors"]["validationNationality"] == [
"nationality: Cannot be empty"
]
9 changes: 8 additions & 1 deletion backend/pycon/settings/test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
from .base import env
from .base import * # noqa
from .base import INSTALLED_APPS, MIDDLEWARE, env

IS_RUNNING_TESTS = True

# Disable Django Debug Toolbar in tests
ENABLE_DJANGO_DEBUG_TOOLBAR = False
if "debug_toolbar" in INSTALLED_APPS:
INSTALLED_APPS.remove("debug_toolbar")
if "debug_toolbar.middleware.DebugToolbarMiddleware" in MIDDLEWARE:
MIDDLEWARE.remove("debug_toolbar.middleware.DebugToolbarMiddleware")

SECRET_KEY = "this-key-should-only-be-used-for-tests"
HASHID_DEFAULT_SECRET_SALT = "only-for-tests"

Expand Down
13 changes: 10 additions & 3 deletions frontend/src/components/grant-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ export const GrantForm = ({
required={true}
placeholder={inputPlaceholderText}
errors={getErrors("why")}
maxLength={1000}
/>
</InputWrapper>
</Grid>
Expand Down Expand Up @@ -586,7 +587,7 @@ export const GrantForm = ({
{formState.values.needsFundsForTravel === "true" && (
<>
<InputWrapper
required={false}
required={true}
title={
<FormattedMessage id="grants.form.fields.departureCountry" />
}
Expand Down Expand Up @@ -615,7 +616,7 @@ export const GrantForm = ({
</InputWrapper>

<InputWrapper
required={false}
required={true}
title={
<FormattedMessage id="grants.form.fields.departureCity" />
}
Expand Down Expand Up @@ -658,6 +659,7 @@ export const GrantForm = ({
required={true}
placeholder={inputPlaceholderText}
errors={getErrors("pythonUsage")}
maxLength={700}
/>
</InputWrapper>

Expand All @@ -676,6 +678,7 @@ export const GrantForm = ({
required={true}
errors={getErrors("beenToOtherEvents")}
placeholder={inputPlaceholderText}
maxLength={500}
/>
</InputWrapper>

Expand All @@ -694,6 +697,7 @@ export const GrantForm = ({
required={false}
errors={getErrors("communityContribution")}
placeholder={inputPlaceholderText}
maxLength={900}
/>
</InputWrapper>
</Grid>
Expand Down Expand Up @@ -739,6 +743,7 @@ export const GrantForm = ({
{...textarea("notes")}
errors={getErrors("notes")}
placeholder={inputPlaceholderText}
maxLength={350}
/>
</InputWrapper>
</Grid>
Expand All @@ -753,7 +758,9 @@ export const GrantForm = ({
photoRequired={false}
getParticipantValidationError={(field) =>
getErrors(
`validationSpeaker${field[0].toUpperCase()}${field.substring(1)}` as any,
`validationSpeaker${field[0].toUpperCase()}${field.substring(
1,
)}` as any,
)
}
showPhotoField={false}
Expand Down
Loading