From c2ac75b2bcd088960d67c337fcc2e3542d883b47 Mon Sep 17 00:00:00 2001 From: Todd Dembrey <todd.dembrey@torchbox.com> Date: Fri, 10 Aug 2018 12:49:20 +0100 Subject: [PATCH] Fixup the tests for the revisions and address --- opentech/apply/funds/models/forms.py | 6 +-- .../apply/funds/tests/factories/blocks.py | 46 ++++++++++++++++- .../apply/funds/tests/factories/models.py | 44 ++++++++++++++++- opentech/apply/funds/tests/test_models.py | 43 ++++++++++++---- opentech/apply/funds/tests/test_views.py | 39 +++++++++++++++ .../apply/review/tests/factories/blocks.py | 2 +- .../apply/review/tests/factories/models.py | 49 +------------------ opentech/apply/review/tests/test_views.py | 23 +++++---- opentech/apply/stream_forms/blocks.py | 2 +- .../apply/stream_forms/testing/factories.py | 8 +++ 10 files changed, 185 insertions(+), 77 deletions(-) diff --git a/opentech/apply/funds/models/forms.py b/opentech/apply/funds/models/forms.py index 0eaba0798..84154539f 100644 --- a/opentech/apply/funds/models/forms.py +++ b/opentech/apply/funds/models/forms.py @@ -63,6 +63,9 @@ class LabBaseForm(AbstractRelatedForm): class AbstractRelatedReviewForm(Orderable): + class Meta(Orderable.Meta): + abstract = True + form = models.ForeignKey('review.ReviewForm', on_delete=models.PROTECT) panels = [ @@ -73,9 +76,6 @@ class AbstractRelatedReviewForm(Orderable): def fields(self): return self.form.form_fields - class Meta(Orderable.Meta): - abstract = True - def __eq__(self, other): try: return self.fields == other.fields diff --git a/opentech/apply/funds/tests/factories/blocks.py b/opentech/apply/funds/tests/factories/blocks.py index 77cc38d4c..f562cf496 100644 --- a/opentech/apply/funds/tests/factories/blocks.py +++ b/opentech/apply/funds/tests/factories/blocks.py @@ -1,3 +1,4 @@ +import json import random import factory @@ -54,15 +55,58 @@ class ValueFieldBlockFactory(FormFieldBlockFactory): model = blocks.ValueBlock @classmethod - def make_answer(cls, params=dict()): + def make_answer(cls, params=dict(), form=False): return random.randint(0, 1_000_000) +class AddressFieldBlockFactory(FormFieldBlockFactory): + class Meta: + model = blocks.AddressFieldBlock + + @classmethod + def an_adress(cls): + return { + 'country': 'GB', + 'thoroughfare': 'bah', + 'premise': 'baz', + 'locality': { + 'locality_name': 'bish', + 'administrative_area': 'bosh', + 'postal_code': 'SW1 4AQ', + } + } + + @classmethod + def make_answer(cls, params=dict()): + return json.dumps({ + 'country': 'GB', + 'thoroughfare': 'bah', + 'premise': 'baz', + 'localityname': 'bish', + 'administrativearea': 'bosh', + 'postalcode': 'SW1 4AQ', + }) + + @classmethod + def make_form_answer(cls, params=dict()): + return { + 'country': 'GB', + 'thoroughfare': 'bah', + 'premise': 'baz', + 'locality': { + 'locality_name': 'bish', + 'administrative_area': 'bosh', + 'postal_code': 'SW1 4AQ', + } + } + + CustomFormFieldsFactory = StreamFieldUUIDFactory({ 'duration': DurationBlockFactory, 'title': TitleBlockFactory, 'value': ValueFieldBlockFactory, 'email': EmailBlockFactory, + 'address': AddressFieldBlockFactory, 'full_name': FullNameBlockFactory, 'char': CharFieldBlockFactory, 'number': NumberFieldBlockFactory, diff --git a/opentech/apply/funds/tests/factories/models.py b/opentech/apply/funds/tests/factories/models.py index cffdc36f1..591854e89 100644 --- a/opentech/apply/funds/tests/factories/models.py +++ b/opentech/apply/funds/tests/factories/models.py @@ -15,8 +15,11 @@ from opentech.apply.funds.models import ( from opentech.apply.funds.models.forms import ( ApplicationForm, ApplicationBaseForm, + ApplicationBaseReviewForm, LabBaseForm, + LabBaseReviewForm, RoundBaseForm, + RoundBaseReviewForm, ) from opentech.apply.users.tests.factories import StaffFactory, UserFactory from opentech.apply.stream_forms.testing.factories import FormDataFactory @@ -69,14 +72,16 @@ class FundTypeFactory(wagtail_factories.PageFactory): @factory.post_generation def forms(self, create, extracted, **kwargs): if create: - from opentech.apply.review.tests.factories.models import ReviewFormFactory for _ in self.workflow.stages: # Generate a form based on all defined fields on the model ApplicationBaseFormFactory( application=self, **kwargs, ) - ReviewFormFactory(**kwargs) + ApplicationBaseReviewForm( + application=self, + **kwargs, + ) class RequestForPartnersFactory(FundTypeFactory): @@ -128,6 +133,10 @@ class RoundFactory(wagtail_factories.PageFactory): round=self, **kwargs, ) + RoundBaseReviewFormFactory( + round=self, + **kwargs, + ) class SealedRoundFactory(RoundFactory): @@ -167,6 +176,10 @@ class LabFactory(wagtail_factories.PageFactory): lab=self, **kwargs, ) + LabBaseReviewFormFactory( + lab=self, + **kwargs, + ) class LabBaseFormFactory(AbstractRelatedFormFactory): @@ -239,3 +252,30 @@ class ApplicationRevisionFactory(factory.DjangoModelFactory): for_factory=ApplicationSubmissionFactory, clean=True, ) + + +class AbstractReviewFormFactory(factory.DjangoModelFactory): + class Meta: + abstract = True + form = factory.SubFactory('opentech.apply.review.tests.factories.ReviewFormFactory') + + +class ApplicationBaseReviewFormFactory(AbstractReviewFormFactory): + class Meta: + model = ApplicationBaseReviewForm + + application = factory.SubFactory(FundTypeFactory) + + +class RoundBaseReviewFormFactory(AbstractReviewFormFactory): + class Meta: + model = RoundBaseReviewForm + + round = factory.SubFactory(RoundFactory) + + +class LabBaseReviewFormFactory(AbstractReviewFormFactory): + class Meta: + model = LabBaseReviewForm + + lab = factory.SubFactory(LabFactory) diff --git a/opentech/apply/funds/tests/test_models.py b/opentech/apply/funds/tests/test_models.py index 9b21854c4..b3b79b32f 100644 --- a/opentech/apply/funds/tests/test_models.py +++ b/opentech/apply/funds/tests/test_models.py @@ -12,6 +12,7 @@ from django.test import TestCase, override_settings from wagtail.core.models import Site from opentech.apply.funds.models import ApplicationSubmission +from opentech.apply.funds.blocks import EmailBlock, FullNameBlock from opentech.apply.funds.workflow import Request from opentech.apply.utils.testing import make_request @@ -28,6 +29,18 @@ def days_from_today(days): return date.today() + timedelta(days=days) +def flatten_for_form(data, field_name='', number=False): + result = {} + for i, (field, value) in enumerate(data.items()): + if number: + field = f'{field_name}_{i}' + if isinstance(value, dict): + result.update(**flatten_for_form(value, field_name=field, number=True)) + else: + result[field] = value + return result + + class TestFundModel(TestCase): def setUp(self): self.fund = FundTypeFactory(parent=None) @@ -196,16 +209,24 @@ class TestFormSubmission(TestCase): self.round_page = RoundFactory(parent=fund, now=True) self.lab_page = LabFactory(lead=self.round_page.lead) - def submit_form(self, page=None, email=None, name=None, user=AnonymousUser()): - if email is None: - email = self.email - if name is None: - name = self.name - + def submit_form(self, page=None, email=None, name=None, user=AnonymousUser(), ignore_errors=False): page = page or self.round_page fields = page.get_form_fields() - # This needs to match the order of the fields defined on the form factory - data = {k: v for k, v in zip(fields, [1, 'project', 0, email, name])} + + data = { + field: factory.make_form_answer() + for field, factory in zip(fields, CustomFormFieldsFactory.factories.values()) + if hasattr(factory, 'make_form_answer') + } + + data = flatten_for_form(data) + + for field in page.forms.first().fields: + if isinstance(field.block, EmailBlock): + data[field.id] = self.email if email is None else email + if isinstance(field.block, FullNameBlock): + data[field.id] = self.name if name is None else name + request = make_request(user, data, method='post', site=self.site) try: @@ -213,7 +234,9 @@ class TestFormSubmission(TestCase): except AttributeError: response = page.serve(request) - self.assertNotContains(response, 'There where some errors with your form') + if not ignore_errors: + # Check the data we submit is correct + self.assertNotContains(response, 'errors') return response def test_workflow_and_status_assigned(self): @@ -287,7 +310,7 @@ class TestFormSubmission(TestCase): # Lead + applicant self.assertEqual(self.User.objects.count(), 2) - response = self.submit_form(email='', name='', user=user) + response = self.submit_form(email='', name='', user=user, ignore_errors=True) self.assertContains(response, 'This field is required') # Lead + applicant diff --git a/opentech/apply/funds/tests/test_views.py b/opentech/apply/funds/tests/test_views.py index ebacea7f3..19d73c2e0 100644 --- a/opentech/apply/funds/tests/test_views.py +++ b/opentech/apply/funds/tests/test_views.py @@ -1,5 +1,7 @@ from datetime import datetime, timedelta +import json +from addressfield.widgets import AddressWidget from opentech.apply.activity.models import Activity from opentech.apply.funds.tests.factories import ( ApplicationSubmissionFactory, @@ -12,6 +14,7 @@ from opentech.apply.users.tests.factories import UserFactory, StaffFactory, Supe from opentech.apply.utils.testing.tests import BaseViewTestCase from ..models import ApplicationRevision +from .test_models import flatten_for_form class BaseSubmissionViewTestCase(BaseViewTestCase): @@ -130,6 +133,17 @@ class TestApplicantSubmissionView(BaseSubmissionViewTestCase): class TestRevisionsView(BaseSubmissionViewTestCase): user_factory = UserFactory + def prepare_address(self, address, field): + address = json.loads(address) + address['locality'] = { + 'localityname': address.pop('localityname'), + 'administrativearea': address.pop('administrativearea'), + 'postalcode': address.pop('postalcode'), + } + address = flatten_for_form(address, field, number=True) + return address + + def test_create_revisions_on_submit(self): submission = ApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2, user=self.user) old_data = submission.form_data.copy() @@ -137,6 +151,13 @@ class TestRevisionsView(BaseSubmissionViewTestCase): new_title = 'New title' new_data[submission.must_include['title']] = new_title + address_id = submission.must_include['address'] + + new_data.update(**self.prepare_address( + new_data[submission.must_include['address']], + address_id, + )) + self.post_page(submission, {'submit': True, **new_data}, 'edit') submission = self.refresh(submission) @@ -151,7 +172,16 @@ class TestRevisionsView(BaseSubmissionViewTestCase): def test_dont_update_live_revision_on_save(self): submission = ApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2, user=self.user) old_data = submission.form_data.copy() + new_data = submission.raw_data + + address_id = submission.must_include['address'] + + new_data.update(**self.prepare_address( + new_data[submission.must_include['address']], + address_id, + )) + new_data[submission.must_include['title']] = 'New title' self.post_page(submission, {'save': True, **new_data}, 'edit') @@ -166,7 +196,16 @@ class TestRevisionsView(BaseSubmissionViewTestCase): def test_existing_draft_edit_and_submit(self): submission = ApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2, user=self.user) draft_data = submission.raw_data.copy() + + address_id = submission.must_include['address'] + + draft_data.update(**self.prepare_address( + draft_data[submission.must_include['address']], + address_id, + )) + draft_data[submission.must_include['title']] = 'New title' + self.post_page(submission, {'save': True, **draft_data}, 'edit') submission = self.refresh(submission) diff --git a/opentech/apply/review/tests/factories/blocks.py b/opentech/apply/review/tests/factories/blocks.py index 77c8a1612..600291d33 100644 --- a/opentech/apply/review/tests/factories/blocks.py +++ b/opentech/apply/review/tests/factories/blocks.py @@ -37,7 +37,7 @@ class ScoreFieldBlockFactory(FormFieldBlockFactory): ReviewFormFieldsFactory = StreamFieldUUIDFactory({ 'char': CharFieldBlockFactory, 'rich_text': RichTextFieldBlockFactory, - 'scored_answer': ScoreFieldBlockFactory, + 'score': ScoreFieldBlockFactory, 'recommendation': RecommendationBlockFactory, 'recommendation_comments': RecommendationCommentsBlockFactory, }) diff --git a/opentech/apply/review/tests/factories/models.py b/opentech/apply/review/tests/factories/models.py index 79a82eb4b..6e7e57229 100644 --- a/opentech/apply/review/tests/factories/models.py +++ b/opentech/apply/review/tests/factories/models.py @@ -1,7 +1,6 @@ import factory -from opentech.apply.funds.models.forms import ApplicationBaseReviewForm -from opentech.apply.funds.tests.factories import ApplicationSubmissionFactory, FundTypeFactory +from opentech.apply.funds.tests.factories import ApplicationSubmissionFactory from opentech.apply.stream_forms.testing.factories import AddFormFieldsMetaclass from opentech.apply.users.tests.factories import StaffFactory @@ -11,26 +10,12 @@ from ...views import get_fields_for_stage from . import blocks -__all__ = ['ReviewFactory', 'ReviewFormFactory', - 'ApplicationBaseReviewFormFactory', 'ReviewFundTypeFactory', - 'ReviewApplicationSubmissionFactory'] +__all__ = ['ReviewFactory', 'ReviewFormFactory'] class ReviewFormDataFactory(factory.DictFactory, metaclass=AddFormFieldsMetaclass): field_factory = blocks.ReviewFormFieldsFactory - @classmethod - def _build(cls, model_class, *args, **kwargs): - submission = kwargs.pop('submission') - - form_fields = { - field.id: 0 - for field in get_fields_for_stage(submission) - } - - form_fields.update(**kwargs) - return super()._build(model_class, *args, **form_fields) - class ReviewFactory(factory.DjangoModelFactory): class Meta: @@ -47,7 +32,6 @@ class ReviewFactory(factory.DjangoModelFactory): form_data = factory.SubFactory( ReviewFormDataFactory, form_fields=factory.SelfAttribute('..form_fields'), - submission=factory.SelfAttribute('..submission'), ) is_draft = False recommendation = NO @@ -60,32 +44,3 @@ class ReviewFormFactory(factory.DjangoModelFactory): name = factory.Faker('word') form_fields = blocks.ReviewFormFieldsFactory - - -class AbstractRelatedReviewFormFactory(factory.DjangoModelFactory): - class Meta: - abstract = True - form = factory.SubFactory(ReviewFormFactory) - - -class ReviewFundTypeFactory(FundTypeFactory): - - @factory.post_generation - def review_forms(self, create, extracted, **kwargs): - if create: - for _ in self.workflow.stages: - # Generate a form based on all defined fields on the model - ApplicationBaseReviewFormFactory( - application=self, - **kwargs - ) - - -class ApplicationBaseReviewFormFactory(AbstractRelatedReviewFormFactory): - class Meta: - model = ApplicationBaseReviewForm - application = factory.SubFactory(ReviewFundTypeFactory, parent=None) - - -class ReviewApplicationSubmissionFactory(ApplicationSubmissionFactory): - page = factory.SubFactory(ReviewFundTypeFactory) diff --git a/opentech/apply/review/tests/test_views.py b/opentech/apply/review/tests/test_views.py index d1b1da318..3ac1e6ab2 100644 --- a/opentech/apply/review/tests/test_views.py +++ b/opentech/apply/review/tests/test_views.py @@ -1,6 +1,6 @@ from django.urls import reverse -from opentech.apply.review.tests.factories.models import ReviewApplicationSubmissionFactory +from opentech.apply.funds.tests.factories.models import ApplicationSubmissionFactory from opentech.apply.users.tests.factories import StaffFactory, UserFactory from opentech.apply.utils.testing.tests import BaseViewTestCase from .factories import ReviewFactory @@ -15,15 +15,14 @@ class StaffReviewsTestCase(BaseViewTestCase): return {'pk': instance.id, 'submission_pk': instance.submission.id} def test_can_access_review(self): - submission = ReviewApplicationSubmissionFactory() - review = ReviewFactory(submission=submission, author=self.user) + review = ReviewFactory(author=self.user) response = self.get_page(review) self.assertContains(response, review.submission.title) self.assertContains(response, self.user.full_name) - self.assertContains(response, reverse('funds:submissions:detail', kwargs={'pk': submission.id})) + self.assertContains(response, reverse('funds:submissions:detail', kwargs={'pk': review.submission.id})) def test_cant_access_other_review(self): - submission = ReviewApplicationSubmissionFactory() + submission = ApplicationSubmissionFactory() review = ReviewFactory(submission=submission) response = self.get_page(review) self.assertEqual(response.status_code, 403) @@ -38,7 +37,7 @@ class StaffReviewListingTestCase(BaseViewTestCase): return {'submission_pk': instance.id} def test_can_access_review_listing(self): - submission = ReviewApplicationSubmissionFactory() + submission = ApplicationSubmissionFactory() reviews = ReviewFactory.create_batch(3, submission=submission) response = self.get_page(submission, 'list') self.assertContains(response, submission.title) @@ -56,18 +55,18 @@ class StaffReviewFormTestCase(BaseViewTestCase): return {'submission_pk': instance.id} def test_can_access_form(self): - submission = ReviewApplicationSubmissionFactory(status='internal_review') + submission = ApplicationSubmissionFactory(status='internal_review') response = self.get_page(submission, 'form') self.assertContains(response, submission.title) self.assertContains(response, reverse('funds:submissions:detail', kwargs={'pk': submission.id})) def test_cant_access_wrong_status(self): - submission = ReviewApplicationSubmissionFactory() + submission = ApplicationSubmissionFactory() response = self.get_page(submission, 'form') self.assertEqual(response.status_code, 403) def test_cant_resubmit_review(self): - submission = ReviewApplicationSubmissionFactory(status='internal_review') + submission = ApplicationSubmissionFactory(status='internal_review') ReviewFactory(submission=submission, author=self.user) response = self.post_page(submission, {'data': 'value'}, 'form') self.assertEqual(response.context['has_submitted_review'], True) @@ -76,7 +75,7 @@ class StaffReviewFormTestCase(BaseViewTestCase): def test_can_edit_draft_review(self): # FIXME fix form generation issue in ReviewFundTypeFactory review_forms() return - submission = ReviewApplicationSubmissionFactory(status='internal_review') + submission = ApplicationSubmissionFactory(status='internal_review') ReviewFactory(submission=submission, author=self.user, is_draft=True) response = self.post_page(submission, {'data': 'value'}, 'form') self.assertEqual(response.context['has_submitted_review'], False) @@ -92,7 +91,7 @@ class UserReviewFormTestCase(BaseViewTestCase): return {'submission_pk': instance.id} def test_cant_access_form(self): - submission = ReviewApplicationSubmissionFactory(status='internal_review') + submission = ApplicationSubmissionFactory(status='internal_review') response = self.get_page(submission, 'form') self.assertEqual(response.status_code, 403) @@ -106,7 +105,7 @@ class ReviewDetailTestCase(BaseViewTestCase): return {'pk': instance.id, 'submission_pk': instance.submission.id} def test_review_detail_recommendation(self): - submission = ReviewApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2) + submission = ApplicationSubmissionFactory(status='draft_proposal', workflow_stages=2) review = ReviewFactory(submission=submission, author=self.user, recommendation_yes=True) response = self.get_page(review) self.assertContains(response, submission.title) diff --git a/opentech/apply/stream_forms/blocks.py b/opentech/apply/stream_forms/blocks.py index 77692d430..b0c450dc5 100644 --- a/opentech/apply/stream_forms/blocks.py +++ b/opentech/apply/stream_forms/blocks.py @@ -61,7 +61,7 @@ class FormFieldBlock(StructBlock): def format_data(self, data): return data - def no_responose(self): + def no_response(self): return "No response" diff --git a/opentech/apply/stream_forms/testing/factories.py b/opentech/apply/stream_forms/testing/factories.py index dfccb6bf9..a7a12f3fe 100644 --- a/opentech/apply/stream_forms/testing/factories.py +++ b/opentech/apply/stream_forms/testing/factories.py @@ -88,6 +88,10 @@ class FormFieldBlockFactory(wagtail_factories.StructBlockFactory): def make_answer(cls, params=dict()): return cls.default_value.generate(params) + @classmethod + def make_form_answer(cls, params=dict()): + return cls.make_answer(params) + class CharFieldBlockFactory(FormFieldBlockFactory): default_value = factory.Faker('sentence') @@ -123,11 +127,15 @@ class UploadableMediaFactory(FormFieldBlockFactory): @classmethod def make_answer(cls, params=dict()): + params = params.copy() + params.setdefault('data', b'this is some content') file_name, file = cls.default_value()._make_content(params) return InMemoryUploadedFile(file, 'file', file_name, None, file.tell(), None) class ImageFieldBlockFactory(UploadableMediaFactory): + default_value = factory.django.ImageField + class Meta: model = stream_blocks.ImageFieldBlock -- GitLab