Skip to content

Commit 2194033

Browse files
committed
Add tests to catch error
1 parent 8f729e8 commit 2194033

2 files changed

Lines changed: 155 additions & 1 deletion

File tree

app/tests/algorithms_tests/test_forms.py

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import pytest
55
from actstream.actions import is_following
6+
from bs4 import BeautifulSoup
67
from django.core.validators import MaxValueValidator, MinValueValidator
78
from factory.django import ImageField
89

@@ -22,7 +23,11 @@
2223
AlgorithmPermissionRequest,
2324
Job,
2425
)
25-
from grandchallenge.components.form_fields import INTERFACE_FORM_FIELD_PREFIX
26+
from grandchallenge.cases.widgets import ImageWidgetChoices
27+
from grandchallenge.components.form_fields import (
28+
INTERFACE_FORM_FIELD_PREFIX,
29+
FileWidgetChoices,
30+
)
2631
from grandchallenge.components.models import (
2732
ComponentJob,
2833
ImportStatusChoices,
@@ -411,6 +416,136 @@ def test_create_job_input_field_required_validation(client, socket_kwargs):
411416
}
412417

413418

419+
def extract_form_data_from_response(response):
420+
html = response.content.decode()
421+
soup = BeautifulSoup(html, "html.parser")
422+
423+
data = {}
424+
for tag in soup.find_all(["input", "select", "textarea"]):
425+
name = tag.get("name")
426+
if not name:
427+
continue
428+
429+
if tag.name == "input":
430+
if tag.get("type") in ["checkbox", "radio"]:
431+
if tag.has_attr("checked"):
432+
data[name] = tag.get("value", "on")
433+
else:
434+
data[name] = tag.get("value", "")
435+
elif tag.name == "select":
436+
selected = tag.find("option", selected=True)
437+
if selected:
438+
data[name] = selected.get("value")
439+
else:
440+
first = tag.find("option")
441+
if first:
442+
data[name] = first.get("value")
443+
elif tag.name == "textarea":
444+
data[name] = tag.text
445+
446+
return data
447+
448+
449+
@pytest.mark.parametrize(
450+
"widget_choice",
451+
[
452+
ImageWidgetChoices.UNDEFINED,
453+
ImageWidgetChoices.IMAGE_SEARCH,
454+
ImageWidgetChoices.IMAGE_UPLOAD,
455+
],
456+
)
457+
@pytest.mark.django_db
458+
def test_create_job_image_kind_no_input_after_widget_choice_field_validation(
459+
client, widget_choice
460+
):
461+
alg, creator, input_socket = create_algorithm_with_input(
462+
kind=InterfaceKindChoices.PANIMG_IMAGE
463+
)
464+
prefixed_interface_slug = (
465+
f"{INTERFACE_FORM_FIELD_PREFIX}{input_socket.slug}"
466+
)
467+
468+
response = get_view_for_user(
469+
viewname="cases:select-image-widget",
470+
client=client,
471+
user=creator,
472+
data={
473+
f"widget-choice-{prefixed_interface_slug}": widget_choice.name,
474+
"prefixed-interface-slug": prefixed_interface_slug,
475+
},
476+
)
477+
data = extract_form_data_from_response(response)
478+
data[f"widget-choice-{prefixed_interface_slug}"] = widget_choice.name
479+
480+
response = get_view_for_user(
481+
viewname="algorithms:job-create",
482+
client=client,
483+
reverse_kwargs={
484+
"slug": alg.slug,
485+
"interface_pk": alg.interfaces.first().pk,
486+
},
487+
method=client.post,
488+
data=data,
489+
follow=True,
490+
user=creator,
491+
)
492+
493+
assert response.status_code == 200
494+
assert response.context["form"].errors == {
495+
f"{prefixed_interface_slug}": ["This field is required."],
496+
}
497+
498+
499+
@pytest.mark.parametrize(
500+
"widget_choice",
501+
[
502+
FileWidgetChoices.UNDEFINED,
503+
FileWidgetChoices.FILE_SEARCH,
504+
FileWidgetChoices.FILE_UPLOAD,
505+
],
506+
)
507+
@pytest.mark.django_db
508+
def test_create_job_file_kind_no_input_after_widget_choice_field_validation(
509+
client, widget_choice
510+
):
511+
alg, creator, input_socket = create_algorithm_with_input(
512+
kind=InterfaceKindChoices.PDF
513+
)
514+
prefixed_interface_slug = (
515+
f"{INTERFACE_FORM_FIELD_PREFIX}{input_socket.slug}"
516+
)
517+
518+
response = get_view_for_user(
519+
viewname="components:select-file-widget",
520+
client=client,
521+
user=creator,
522+
data={
523+
f"widget-choice-{prefixed_interface_slug}": widget_choice.name,
524+
"prefixed-interface-slug": prefixed_interface_slug,
525+
},
526+
)
527+
data = extract_form_data_from_response(response)
528+
data[f"widget-choice-{prefixed_interface_slug}"] = widget_choice.name
529+
530+
response = get_view_for_user(
531+
viewname="algorithms:job-create",
532+
client=client,
533+
reverse_kwargs={
534+
"slug": alg.slug,
535+
"interface_pk": alg.interfaces.first().pk,
536+
},
537+
method=client.post,
538+
data=data,
539+
follow=True,
540+
user=creator,
541+
)
542+
543+
assert response.status_code == 200
544+
assert response.context["form"].errors == {
545+
f"{prefixed_interface_slug}": ["This field is required."],
546+
}
547+
548+
414549
def create_algorithm_with_input(**kwargs):
415550
creator = get_algorithm_creator()
416551
VerificationFactory(user=creator, is_verified=True)

app/tests/cases_tests/test_widget.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@ def test_flexible_image_field_validation():
5757
== decompressed_value_for_missing_value
5858
== [None, None]
5959
)
60+
with pytest.raises(ValidationError):
61+
field.clean(parsed_value_for_empty_data)
62+
63+
parsed_value_no_selected_data = field.widget.value_from_datadict(
64+
data=QueryDict(urlencode({prefixed_interface_slug: ""})),
65+
name=prefixed_interface_slug,
66+
files={},
67+
)
68+
decompressed_value_for_no_selected_data = field.widget.decompress(
69+
value=[""]
70+
)
71+
72+
assert (
73+
parsed_value_no_selected_data
74+
== decompressed_value_for_no_selected_data
75+
== [None, None]
76+
)
77+
with pytest.raises(ValidationError):
78+
field.clean(parsed_value_no_selected_data)
6079

6180
parsed_value_for_image_with_permission = field.widget.value_from_datadict(
6281
data=QueryDict(urlencode({prefixed_interface_slug: im1.pk})),

0 commit comments

Comments
 (0)