diff --git a/exporter/applications/forms/parties.py b/exporter/applications/forms/parties.py index ce5c46f0b0..28a9ea23ce 100644 --- a/exporter/applications/forms/parties.py +++ b/exporter/applications/forms/parties.py @@ -132,8 +132,10 @@ def new_party_form_group(request, application, strings, back_url, clearance_opti return FormGroup(forms) -class PartyReuseForm(forms.Form): - title = "Do you want to reuse an existing party?" +class PartyReuseForm(BaseForm): + class Layout: + TITLE = "Do you want to reuse an existing party? - LITE - GOV.UK" + TITLE_AS_LABEL_FOR = "reuse_party" reuse_party = forms.ChoiceField( choices=( @@ -147,19 +149,15 @@ class PartyReuseForm(forms.Form): }, ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.helper = FormHelper() - self.helper.layout = Layout( - HTML.h1(self.title), - "reuse_party", - Submit("submit", "Continue"), - ) + def get_layout_fields(self): + return ("reuse_party",) -class PartySubTypeSelectForm(forms.Form): - title = "Select the type of end user" +class PartySubTypeSelectForm(BaseForm): + """ + This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set + as per the BaseForm. + """ CHOICES = ( Choice("government", PartyForm.Options.GOVERNMENT), @@ -178,12 +176,8 @@ class PartySubTypeSelectForm(forms.Form): ) sub_type_other = forms.CharField(required=False, label="") - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.helper = FormHelper() - self.helper.layout = Layout( - HTML.h1(self.title), + def get_layout_fields(self): + return ( ConditionalRadios( "sub_type", PartyForm.Options.GOVERNMENT, @@ -191,7 +185,6 @@ def __init__(self, *args, **kwargs): PartyForm.Options.INDIVIDUAL, ConditionalRadiosQuestion(PartyForm.Options.OTHER, "sub_type_other"), ), - Submit("submit", "Continue"), ) def clean(self): @@ -203,10 +196,23 @@ def clean(self): return cleaned_data -class PartyNameForm(BaseForm): +class EndUserSubTypeSelectForm(PartySubTypeSelectForm): class Layout: - TITLE = "End user name" - TITLE_AS_LABEL_FOR = "name" + TITLE = "Select the type of end user" + TITLE_AS_LABEL_FOR = "sub_type" + + +class ConsigneeSubTypeSelectForm(PartySubTypeSelectForm): + class Layout: + TITLE = "Select the type of consignee" + TITLE_AS_LABEL_FOR = "sub_type" + + +class PartyNameForm(BaseForm): + """ + This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set + as per the BaseForm. + """ name = forms.CharField( label="", @@ -223,12 +229,30 @@ def get_layout_fields(self): return ("name",) -class PartyWebsiteForm(BaseForm): +class EndUserNameForm(PartyNameForm): class Layout: - TITLE = "End user website address (optional)" - TITLE_AS_LABEL_FOR = "website" + TITLE = "End user name" + TITLE_AS_LABEL_FOR = "name" + - website = forms.CharField(required=False, label="", help_text="Use the format https://www.example.com") +class ConsigneeNameForm(PartyNameForm): + class Layout: + TITLE = "Consignee name" + TITLE_AS_LABEL_FOR = "name" + + +class PartyWebsiteForm(BaseForm): + """ + This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set + as per the BaseForm. + """ + + website = forms.CharField( + required=False, + label="", + help_text="Use the format https://www.example.com", + validators=[MaxLengthValidator(200, f"Website address should be 200 characters or less")], + ) def clean_website(self): website = self.cleaned_data.get("website") @@ -255,9 +279,23 @@ def get_layout_fields(self): return ("website",) -class PartyAddressForm(BaseForm): +class EndUserWebsiteForm(PartyWebsiteForm): class Layout: - TITLE = "End user address" + TITLE = "End user website address (optional)" + TITLE_AS_LABEL_FOR = "website" + + +class ConsigneeWebsiteForm(PartyWebsiteForm): + class Layout: + TITLE = "Consignee website address (optional)" + TITLE_AS_LABEL_FOR = "website" + + +class PartyAddressForm(BaseForm): + """ + This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set + as per the BaseForm. + """ address = forms.CharField( widget=forms.Textarea(attrs={"rows": "10"}), @@ -284,6 +322,18 @@ def get_layout_fields(self): ) +class EndUserAddressForm(PartyAddressForm): + class Layout: + TITLE = "End user address" + TITLE_AS_LABEL_FOR = "address" + + +class ConsigneeAddressForm(PartyAddressForm): + class Layout: + TITLE = "Consignee address" + TITLE_AS_LABEL_FOR = "address" + + class PartySignatoryNameForm(BaseForm): class Layout: TITLE = "Signatory name" @@ -303,10 +353,10 @@ class PartyDocumentsForm(forms.Form): title = "Do you have an end-user document?" text_p1 = """ You will be asked to upload either an - - end-user undertaking (opens in new tab) or - - stockist undertaking (opens in new tab) completed by the end-user or stockist. + + end-user undertaking (opens in new tab) or + + stockist undertaking (opens in new tab) completed by the end-user or stockist. """ text_p2 = "You must include at least one page on company letterhead. This can either be within the end-user document or on a separate document." text_p3 = ( diff --git a/exporter/applications/views/parties/consignees.py b/exporter/applications/views/parties/consignees.py index 2595ae9615..fec0c22f31 100644 --- a/exporter/applications/views/parties/consignees.py +++ b/exporter/applications/views/parties/consignees.py @@ -1,14 +1,44 @@ +import logging + +from django.http import HttpResponseRedirect from django.shortcuts import render, redirect from django.urls import reverse -from django.views.generic import TemplateView +from django.views.generic import TemplateView, FormView + +from core.auth.views import LoginRequiredMixin +from core.wizard.views import BaseSessionWizardView from exporter.applications.forms.parties import new_party_form_group from exporter.applications.helpers.check_your_answers import convert_party -from exporter.applications.services import get_application, post_party, delete_party, validate_party -from exporter.applications.views.parties.base import AddParty, CopyParties, SetParty, DeleteParty, CopyAndSetParty +from exporter.applications.services import validate_party +from exporter.applications.views.parties.base import CopyParties, DeleteParty, CopyAndSetParty +from exporter.applications.forms.parties import ( + PartyReuseForm, + ConsigneeSubTypeSelectForm, + ConsigneeNameForm, + ConsigneeWebsiteForm, + ConsigneeAddressForm, +) +from exporter.applications.services import ( + get_application, + post_party, + delete_party, +) +from exporter.core.helpers import ( + str_to_bool, +) +from exporter.core.constants import ( + SetPartyFormSteps, +) + +from http import HTTPStatus from lite_content.lite_exporter_frontend.applications import ConsigneeForm, ConsigneePage -from core.auth.views import LoginRequiredMixin +from .payloads import SetConsigneePayloadBuilder + +log = logging.getLogger(__name__) + +from core.decorators import expect_status class Consignee(LoginRequiredMixin, TemplateView): @@ -32,26 +62,57 @@ def get(self, request, **kwargs): return redirect(reverse("applications:add_consignee", kwargs={"pk": application_id})) -class AddConsignee(LoginRequiredMixin, AddParty): - def __init__(self): - super().__init__(new_url="applications:set_consignee", copy_url="applications:consignees_copy") +class AddConsignee(LoginRequiredMixin, FormView): + form_class = PartyReuseForm + template_name = "core/form.html" - @property - def back_url(self): - return reverse("applications:task_list", kwargs={"pk": self.kwargs["pk"]}) + "#consignee" + def form_valid(self, form): + reuse_party = str_to_bool(form.cleaned_data.get("reuse_party")) + if reuse_party: + success_url = reverse("applications:consignees_copy", kwargs=self.kwargs) + else: + success_url = reverse("applications:set_consignee", kwargs=self.kwargs) + return HttpResponseRedirect(success_url) -class SetConsignee(LoginRequiredMixin, SetParty): - def __init__(self): - super().__init__( - url="applications:consignee_attach_document", - party_type="consignee", - form=new_party_form_group, - back_url="applications:consignee", - strings=ConsigneeForm, - post_action=post_party, - validate_action=validate_party, - ) + +class SetConsignee(LoginRequiredMixin, BaseSessionWizardView): + party_type = "consignee" + form_list = [ + (SetPartyFormSteps.PARTY_SUB_TYPE, ConsigneeSubTypeSelectForm), + (SetPartyFormSteps.PARTY_NAME, ConsigneeNameForm), + (SetPartyFormSteps.PARTY_WEBSITE, ConsigneeWebsiteForm), + (SetPartyFormSteps.PARTY_ADDRESS, ConsigneeAddressForm), + ] + + def get_form_kwargs(self, step=None): + kwargs = super().get_form_kwargs(step) + if step == SetPartyFormSteps.PARTY_ADDRESS: + kwargs["request"] = self.request + + return kwargs + + def get_payload(self, form_dict): + return SetConsigneePayloadBuilder().build(form_dict) + + @expect_status( + HTTPStatus.CREATED, + "Error adding consignee to application", + "Unexpected error adding consignee to application", + ) + def post_party_with_payload(self, pk, form_dict): + payload = self.get_payload(form_dict) + payload.update({"type": self.party_type}) + + return post_party(self.request, pk, payload) + + def get_success_url(self, party_id): + return reverse("applications:consignee_attach_document", kwargs={"pk": self.kwargs["pk"], "obj_pk": party_id}) + + def done(self, form_list, form_dict, **kwargs): + response, _ = self.post_party_with_payload(self.kwargs["pk"], form_dict) + + return redirect(self.get_success_url(response[self.party_type]["id"])) class RemoveConsignee(LoginRequiredMixin, DeleteParty): diff --git a/exporter/applications/views/parties/end_users.py b/exporter/applications/views/parties/end_users.py index 6ddadbde6f..979e082388 100644 --- a/exporter/applications/views/parties/end_users.py +++ b/exporter/applications/views/parties/end_users.py @@ -13,10 +13,10 @@ from exporter.applications.forms.parties import ( PartyReuseForm, - PartySubTypeSelectForm, - PartyNameForm, - PartyWebsiteForm, - PartyAddressForm, + EndUserSubTypeSelectForm, + EndUserNameForm, + EndUserWebsiteForm, + EndUserAddressForm, PartySignatoryNameForm, PartyDocumentsForm, PartyDocumentUploadForm, @@ -77,11 +77,6 @@ class AddEndUserView(LoginRequiredMixin, FormView): form_class = PartyReuseForm template_name = "core/form.html" - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context["form_title"] = self.form_class.title - return context - def form_valid(self, form): reuse_party = str_to_bool(form.cleaned_data.get("reuse_party")) if reuse_party: @@ -112,10 +107,10 @@ def _post_party_document(request, application_id, party_id, document_type, docum class SetPartyView(LoginRequiredMixin, BaseSessionWizardView): form_list = [ - (SetPartyFormSteps.PARTY_SUB_TYPE, PartySubTypeSelectForm), - (SetPartyFormSteps.PARTY_NAME, PartyNameForm), - (SetPartyFormSteps.PARTY_WEBSITE, PartyWebsiteForm), - (SetPartyFormSteps.PARTY_ADDRESS, PartyAddressForm), + (SetPartyFormSteps.PARTY_SUB_TYPE, EndUserSubTypeSelectForm), + (SetPartyFormSteps.PARTY_NAME, EndUserNameForm), + (SetPartyFormSteps.PARTY_WEBSITE, EndUserWebsiteForm), + (SetPartyFormSteps.PARTY_ADDRESS, EndUserAddressForm), (SetPartyFormSteps.PARTY_SIGNATORY_NAME, PartySignatoryNameForm), (SetPartyFormSteps.PARTY_DOCUMENTS, PartyDocumentsForm), (SetPartyFormSteps.PARTY_DOCUMENT_UPLOAD, PartyDocumentUploadForm), @@ -143,6 +138,7 @@ def get_context_data(self, form, **kwargs): context["title"] = form.Layout.TITLE else: context["title"] = form.title + return context def get_form_kwargs(self, step=None): @@ -287,6 +283,12 @@ def get(self, request, *args, **kwargs): class PartyEditView(LoginRequiredMixin, PartyContextMixin, FormView): + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + + return kwargs + def form_valid(self, form): update_party(self.request, self.application_id, self.party_id, form.cleaned_data) return super().form_valid(form) @@ -296,28 +298,28 @@ def get_success_url(self): class PartySubTypeEditView(PartyEditView): - form_class = PartySubTypeSelectForm + form_class = EndUserSubTypeSelectForm def get_initial(self): return {"sub_type": self.party["sub_type"]["key"], "sub_type_other": self.party["sub_type_other"]} class PartyNameEditView(PartyEditView): - form_class = PartyNameForm + form_class = EndUserNameForm def get_initial(self): return {"name": self.party["name"]} class PartyWebsiteEditView(PartyEditView): - form_class = PartyWebsiteForm + form_class = EndUserWebsiteForm def get_initial(self): return {"website": self.party["website"]} class PartyAddressEditView(PartyEditView): - form_class = PartyAddressForm + form_class = EndUserAddressForm def get_form_kwargs(self): kwargs = super().get_form_kwargs() @@ -408,7 +410,6 @@ def get_form_kwargs(self, step=None): if step == SetPartyFormSteps.PARTY_COMPANY_LETTERHEAD_DOCUMENT_UPLOAD: kwargs["edit"] = self.company_letterhead_document_exists - return kwargs def get_form_initial(self, step): diff --git a/exporter/applications/views/parties/payloads.py b/exporter/applications/views/parties/payloads.py new file mode 100644 index 0000000000..6618396512 --- /dev/null +++ b/exporter/applications/views/parties/payloads.py @@ -0,0 +1,14 @@ +from core.wizard.payloads import MergingPayloadBuilder +from exporter.applications.views.goods.common.payloads import get_cleaned_data +from exporter.core.constants import ( + SetPartyFormSteps, +) + + +class SetConsigneePayloadBuilder(MergingPayloadBuilder): + payload_dict = { + SetPartyFormSteps.PARTY_SUB_TYPE: get_cleaned_data, + SetPartyFormSteps.PARTY_NAME: get_cleaned_data, + SetPartyFormSteps.PARTY_WEBSITE: get_cleaned_data, + SetPartyFormSteps.PARTY_ADDRESS: get_cleaned_data, + } diff --git a/unit_tests/exporter/applications/forms/test_parties.py b/unit_tests/exporter/applications/forms/test_parties.py index e4f4f662d8..723bec4b5d 100644 --- a/unit_tests/exporter/applications/forms/test_parties.py +++ b/unit_tests/exporter/applications/forms/test_parties.py @@ -31,8 +31,26 @@ def test_party_reuse_form(data, valid, errors): ({"sub_type": "other"}, False, {"sub_type_other": ["Enter the type of the party you're adding"]}), ), ) -def test_party_subtype_select_form(data, valid, errors): - form = parties.PartySubTypeSelectForm(data=data) +def test_end_user_subtype_select_form(data, valid, errors): + form = parties.EndUserSubTypeSelectForm(data=data) + + assert form.is_valid() == valid + + if not valid: + assert form.errors == errors + + +@pytest.mark.parametrize( + "data, valid, errors", + ( + ({"sub_type": "government"}, True, None), + ({"sub_type": "other", "sub_type_other": "test_sub_type"}, True, None), + ({"sub_type": ""}, False, {"sub_type": ["Select what type of party you're creating"]}), + ({"sub_type": "other"}, False, {"sub_type_other": ["Enter the type of the party you're adding"]}), + ), +) +def test_consignee_subtype_select_form(data, valid, errors): + form = parties.ConsigneeSubTypeSelectForm(data=data) assert form.is_valid() == valid @@ -57,8 +75,56 @@ def test_party_subtype_select_form(data, valid, errors): ), ), ) -def test_party_name_form(data, valid, errors): - form = parties.PartyNameForm(data=data) +def test_consignee_name_form(data, valid, errors): + form = parties.ConsigneeNameForm(data=data) + + assert form.is_valid() == valid + + if not valid: + assert form.errors == errors + + +@pytest.mark.parametrize( + "data, valid, errors", + ( + ({"name": "test"}, True, None), + ({"name": ""}, False, {"name": ["Enter a name"]}), + ( + {"name": "department of internation trade in collaboration with the department of national trade"}, + False, + {"name": [f"End user name should be 80 characters or less"]}, + ), + ), +) +def test_end_user_name_form(data, valid, errors): + form = parties.EndUserNameForm(data=data) + + assert form.is_valid() == valid + + if not valid: + assert form.errors == errors + + +@pytest.mark.parametrize( + "data, valid, errors", + ( + ({"website": "test"}, False, {"website": ["Enter a valid URL."]}), + ({"website": "https://www.example.com"}, True, None), + ({"website": "www.example.com"}, True, None), + ({"website": "example.com"}, True, None), + ({"website": ""}, True, None), + ( + { + "website": "https://www.example.com/asfhadjksfhadsklfhalskfhjsakfhsdfkshfskfhsdkfhskfjhfkdshfksfhdksfhsdkjfhksfhsakadfshdsmnfbdsfbdsfsbdfdmsbfdfsngdfsbgdfsgdfsbgdfsgbdfsgbdfsgmnbdfsgmnbdfsgmdfsbgdfsgbdfsgbdfsbgdfsbg/" + }, + False, + {"website": ["Website address should be 200 characters or less"]}, + ), + ({}, True, None), + ), +) +def test_end_user_website_form(data, valid, errors): + form = parties.EndUserWebsiteForm(data=data) assert form.is_valid() == valid @@ -74,11 +140,18 @@ def test_party_name_form(data, valid, errors): ({"website": "www.example.com"}, True, None), ({"website": "example.com"}, True, None), ({"website": ""}, True, None), + ( + { + "website": "https://www.example.com/asfhadjksfhadsklfhalskfhjsakfhsdfkshfskfhsdkfhskfjhfkdshfksfhdksfhsdkjfhksfhsakadfshdsmnfbdsfbdsfsbdfdmsbfdfsngdfsbgdfsgdfsbgdfsgbdfsgbdfsgmnbdfsgmnbdfsgmdfsbgdfsgbdfsgbdfsbgdfsbg/" + }, + False, + {"website": ["Website address should be 200 characters or less"]}, + ), ({}, True, None), ), ) -def test_party_website_form(data, valid, errors): - form = parties.PartyWebsiteForm(data=data) +def test_consignee_website_form(data, valid, errors): + form = parties.ConsigneeWebsiteForm(data=data) assert form.is_valid() == valid @@ -98,13 +171,38 @@ def test_party_website_form(data, valid, errors): ), ) @patch("exporter.applications.forms.parties.get_countries") -def test_party_address_form(mock_get_countries, data, valid, errors): +def test_end_user_address_form(mock_get_countries, data, valid, errors): + class Request: + csp_nonce = "test" + + request = Request() + mock_get_countries.return_value = [{"id": "aus", "name": "Austria"}, {"id": "fr", "name": "France"}] + form = parties.EndUserAddressForm(request=request, data=data) + + assert form.is_valid() == valid + mock_get_countries.assert_called_once_with(request) + + if not valid: + assert form.errors == errors + + +@pytest.mark.parametrize( + "data, valid, errors", + ( + ({"address": "1 somewhere", "country": "aus"}, True, None), + ({"address": "", "country": ""}, False, {"address": ["Enter an address"], "country": ["Select the country"]}), + ({"address": "This-is-a-valid-address", "country": "aus"}, True, None), + ({"address": "this\r\nis\r\ninvalid", "country": "aus"}, True, None), + ), +) +@patch("exporter.applications.forms.parties.get_countries") +def test_consignee_address_form(mock_get_countries, data, valid, errors): class Request: csp_nonce = "test" request = Request() mock_get_countries.return_value = [{"id": "aus", "name": "Austria"}, {"id": "fr", "name": "France"}] - form = parties.PartyAddressForm(request=request, data=data) + form = parties.ConsigneeAddressForm(request=request, data=data) assert form.is_valid() == valid mock_get_countries.assert_called_once_with(request) diff --git a/unit_tests/exporter/applications/views/parties/test_consignees.py b/unit_tests/exporter/applications/views/parties/test_consignees.py index faf4a8d951..ff267f7a65 100644 --- a/unit_tests/exporter/applications/views/parties/test_consignees.py +++ b/unit_tests/exporter/applications/views/parties/test_consignees.py @@ -1,9 +1,20 @@ +import logging import pytest + +from bs4 import BeautifulSoup from pytest_django.asserts import assertTemplateUsed from django.urls import reverse from core import client +from exporter.core.constants import SetPartyFormSteps +from exporter.applications.forms.parties import ( + PartyReuseForm, + PartySubTypeSelectForm, + PartyNameForm, + PartyWebsiteForm, + PartyAddressForm, +) @pytest.fixture(autouse=True) @@ -17,11 +28,203 @@ def application_pk(data_standard_case): return data_standard_case["case"]["id"] +@pytest.fixture +def mock_party_create_success(requests_mock, data_standard_case): + party_id = data_standard_case["case"]["data"]["consignee"]["id"] + url = client._build_absolute_uri(f'/applications/{data_standard_case["case"]["id"]}/parties/') + yield requests_mock.post(url=url, status_code=201, json={"consignee": {"id": party_id}}) + + +@pytest.fixture +def mock_party_create_fail(requests_mock, data_standard_case): + url = client._build_absolute_uri(f'/applications/{data_standard_case["case"]["id"]}/parties/') + yield requests_mock.post(url=url, status_code=500, json={}) + + +@pytest.fixture +def add_consignee_url(application_pk): + return reverse("applications:add_consignee", kwargs={"pk": application_pk}) + + +@pytest.fixture +def set_consignee_url(application_pk): + return reverse("applications:set_consignee", kwargs={"pk": application_pk}) + + +@pytest.fixture +def post_to_step(post_to_step_factory, set_consignee_url): + return post_to_step_factory(set_consignee_url) + + @pytest.fixture def application_parties_consignee_summary_url(application_pk): return reverse("applications:consignee", kwargs={"pk": application_pk}) -def test_application_parties_consignee_summary(authorized_client, application_parties_consignee_summary_url): +@pytest.fixture(autouse=True) +def setup( + mock_countries, +): + yield + + +@pytest.mark.parametrize("reuse_party_response, expected_url", ((True, "consignees_copy"), (False, "set_consignee"))) +def test_add_consignee_view( + application_pk, data_standard_case, add_consignee_url, authorized_client, reuse_party_response, expected_url +): + response = authorized_client.get(add_consignee_url) + + assert response.status_code == 200 + assert isinstance(response.context["form"], PartyReuseForm) + + response = authorized_client.post( + add_consignee_url, + data={ + "reuse_party": reuse_party_response, + }, + ) + + assert response.status_code == 302 + assert response.url == reverse(f"applications:{expected_url}", kwargs={"pk": application_pk}) + + +def test_set_consignee_end_to_end_post_success( + set_consignee_url, + authorized_client, + post_to_step, + requests_mock, + data_standard_case, + application_pk, + mock_party_create_success, +): + response = authorized_client.get(set_consignee_url) + + assert response.status_code == 200 + assert not response.context["form"].errors + assert isinstance(response.context["form"], PartySubTypeSelectForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_SUB_TYPE, + {"sub_type": "government"}, + ) + + assert response.status_code == 200 + assert not response.context["form"].errors + content = BeautifulSoup(response.content, "html.parser") + heading_element = content.find("h1", class_="govuk-heading-xl") + + assert heading_element.string == "Consignee name" + + assert isinstance(response.context["form"], PartyNameForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_NAME, + {"name": "test-name"}, + ) + + assert response.status_code == 200 + assert not response.context["form"].errors + + content = BeautifulSoup(response.content, "html.parser") + heading_element = content.find("h1", class_="govuk-heading-xl") + + assert heading_element.string == "Consignee website address (optional)" + + assert isinstance(response.context["form"], PartyWebsiteForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_WEBSITE, + {"website": "https://www.example.com"}, + ) + assert not response.context["form"].errors + + content = BeautifulSoup(response.content, "html.parser") + heading_element = content.find("h1", class_="govuk-heading-xl") + + assert heading_element.string == "Consignee address" + assert isinstance(response.context["form"], PartyAddressForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_ADDRESS, + {"address": "1 somewhere", "country": "US"}, + ) + + assert response.status_code == 302 + + party_id = data_standard_case["case"]["data"]["consignee"]["id"] + + assert response.url == reverse( + "applications:consignee_attach_document", kwargs={"pk": application_pk, "obj_pk": party_id} + ) + + consignee_data = requests_mock.request_history.pop().json() + assert consignee_data == { + "sub_type": "government", + "sub_type_other": "", + "name": "test-name", + "website": "https://www.example.com", + "address": "1 somewhere", + "country": "US", + "type": "consignee", + } + + +def test_set_consignee_end_to_end_post_fail( + set_consignee_url, + authorized_client, + requests_mock, + data_standard_case, + application_pk, + mock_party_create_fail, + caplog, + post_to_step, +): + response = authorized_client.get(set_consignee_url) + + assert response.status_code == 200 + assert not response.context["form"].errors + assert isinstance(response.context["form"], PartySubTypeSelectForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_SUB_TYPE, + {"sub_type": "government"}, + ) + + assert response.status_code == 200 + assert not response.context["form"].errors + assert isinstance(response.context["form"], PartyNameForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_NAME, + {"name": "test-name"}, + ) + + assert response.status_code == 200 + assert not response.context["form"].errors + assert isinstance(response.context["form"], PartyWebsiteForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_WEBSITE, + {"website": "https://www.example.com"}, + ) + assert not response.context["form"].errors + assert isinstance(response.context["form"], PartyAddressForm) + + response = post_to_step( + SetPartyFormSteps.PARTY_ADDRESS, + {"address": "1 somewhere", "country": "US"}, + ) + + assert response.status_code == 200 + assert len(caplog.records) == 1 + log = caplog.records[0] + + assert log.message == "Error adding consignee to application - response was: 500 - {}" + assert log.levelno == logging.ERROR + + +def test_application_parties_consignee_summary( + authorized_client, application_parties_consignee_summary_url, mock_get_application +): response = authorized_client.get(application_parties_consignee_summary_url) assertTemplateUsed(response, "applications/consignee.html") diff --git a/unit_tests/exporter/applications/views/parties/test_end_users.py b/unit_tests/exporter/applications/views/parties/test_end_users.py index c9f35c6f76..20813e6863 100644 --- a/unit_tests/exporter/applications/views/parties/test_end_users.py +++ b/unit_tests/exporter/applications/views/parties/test_end_users.py @@ -126,6 +126,11 @@ def set_end_user(url, authorized_client): response = authorized_client.get(url) assert not response.context["form"].errors + content = BeautifulSoup(response.content, "html.parser") + heading_element = content.find("h1", class_="govuk-heading-xl") + + assert heading_element.string == "Select the type of end user" + response = authorized_client.post( url, data={ @@ -135,6 +140,11 @@ def set_end_user(url, authorized_client): ) assert not response.context["form"].errors + content = BeautifulSoup(response.content, "html.parser") + heading_element = content.find("h1", class_="govuk-heading-xl") + + assert heading_element.string == "End user name" + response = authorized_client.post( url, data={ @@ -144,6 +154,11 @@ def set_end_user(url, authorized_client): ) assert not response.context["form"].errors + content = BeautifulSoup(response.content, "html.parser") + heading_element = content.find("h1", class_="govuk-heading-xl") + + assert heading_element.string == "End user website address (optional)" + response = authorized_client.post( url, data={ @@ -152,6 +167,10 @@ def set_end_user(url, authorized_client): }, ) assert not response.context["form"].errors + content = BeautifulSoup(response.content, "html.parser") + heading_element = content.find("h1", class_="govuk-heading-xl") + + assert heading_element.string == "End user address" response = authorized_client.post( url, @@ -438,5 +457,6 @@ def test_add_end_user_view(authorized_client, data_standard_case): url = reverse("applications:add_end_user", kwargs={"pk": application_id}) response = authorized_client.get(url) soup = BeautifulSoup(response.content, "html.parser") + heading_element = soup.find("h1", class_="govuk-heading-xl") - assert soup.title.string == "Do you want to reuse an existing party? - LITE - GOV.UK" + assert heading_element.string == "Do you want to reuse an existing party? - LITE - GOV.UK"