From 1e28534e7aa0e360d2fd692584f2613272fcf478 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Fri, 18 Nov 2022 19:16:18 +0100 Subject: [PATCH 1/6] Change test_form_condition actually remove astep --- tests/wizard/test_forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/wizard/test_forms.py b/tests/wizard/test_forms.py index 5711848..d232c04 100644 --- a/tests/wizard/test_forms.py +++ b/tests/wizard/test_forms.py @@ -80,7 +80,7 @@ def get_form_kwargs(self, step, *args, **kwargs): class TestWizardWithInitAttrs(TestWizard): form_list = [Step1, Step2] - condition_dict = {'step2': True} + condition_dict = {'step2': False} initial_dict = {'start': {'name': 'value1'}} instance_dict = {'start': User()} @@ -156,7 +156,7 @@ def test_form_condition(self): [('start', Step1), ('step2', Step2), ('step3', Step3)] ) response, instance = testform(request) - self.assertEqual(instance.get_next_step(), 'step2') + self.assertEqual(instance.get_next_step(), 'step3') def test_form_condition_unstable(self): request = get_request() From c6d9eda3c1134c2001cac62472826967858b06c7 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Fri, 18 Nov 2022 19:16:28 +0100 Subject: [PATCH 2/6] Add test_form_condition_callable Skipped for now as this feature broke in 533a83053deab2adeb72de079e813aae78b03c1a see: https://github.com/jazzband/django-formtools/issues/220 --- tests/wizard/test_forms.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/wizard/test_forms.py b/tests/wizard/test_forms.py index d232c04..a6c784f 100644 --- a/tests/wizard/test_forms.py +++ b/tests/wizard/test_forms.py @@ -1,3 +1,4 @@ +import unittest from importlib import import_module from django import forms, http @@ -158,6 +159,34 @@ def test_form_condition(self): response, instance = testform(request) self.assertEqual(instance.get_next_step(), 'step3') + @unittest.skip('https://github.com/jazzband/django-formtools/issues/220') + def test_form_condition_callable(self): + def step2_is_enabled(wizard): + cleaned_data = wizard.get_cleaned_data_for_step('start') + if cleaned_data: + return cleaned_data.get('name') == 'yes' + + testform = TestWizard.as_view( + [('start', Step1), ('step2', Step2), ('step3', Step3)], + condition_dict={'step2': step2_is_enabled} + ) + request = get_request({'test_wizard-current_step': 'step1', 'start-name': 'yes'}) + response, instance = testform(request) + # being on step2 when giving name "yes" + self.assertEqual(instance.get_prev_step(), 'start') + self.assertEqual(instance.steps.current, 'step2') + self.assertEqual(instance.get_next_step(), 'step3') + self.assertEqual(instance.get_step_index(), 1) + + request = get_request({'test_wizard-current_step': 'start', 'start-name': 'no'}) + response, instance = testform(request) + # step2 being skipped and no more steps to go when giving name "no" + self.assertEqual(instance.get_prev_step(), 'start') + self.assertEqual(instance.steps.current, 'step3') + self.assertEqual(instance.get_next_step(), None) + # index is still 1, because step2 is not in list returned by get_form_list() + self.assertEqual(instance.get_step_index(), 1) + def test_form_condition_unstable(self): request = get_request() testform = TestWizard.as_view( From 1ceeffeed1efcf9777a084ec3e116717eb2577c8 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Fri, 18 Nov 2022 19:18:06 +0100 Subject: [PATCH 3/6] Improve form init tests --- tests/wizard/test_forms.py | 73 +++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/tests/wizard/test_forms.py b/tests/wizard/test_forms.py index a6c784f..e5d6a59 100644 --- a/tests/wizard/test_forms.py +++ b/tests/wizard/test_forms.py @@ -5,9 +5,11 @@ from django.conf import settings from django.contrib.auth.models import User from django.db import models +from django.forms import formset_factory from django.template.response import TemplateResponse from django.test import TestCase +from formtools.wizard.storage import NoFileStorageConfigured from formtools.wizard.views import ( CookieWizardView, SessionWizardView, WizardView, ) @@ -42,8 +44,14 @@ class Step3(forms.Form): data = forms.CharField() -class CustomKwargsStep1(Step1): +class FormWithFileField(forms.Form): + upload = forms.FileField() + + +Step3FormSet = formset_factory(Step3) + +class CustomKwargsStep1(Step1): def __init__(self, test=None, *args, **kwargs): self.test = test super().__init__(*args, **kwargs) @@ -79,11 +87,14 @@ def get_form_kwargs(self, step, *args, **kwargs): return kwargs +test_instance = User() + + class TestWizardWithInitAttrs(TestWizard): form_list = [Step1, Step2] condition_dict = {'step2': False} initial_dict = {'start': {'name': 'value1'}} - instance_dict = {'start': User()} + instance_dict = {'start': test_instance} class TestWizardWithTypeCheck(TestWizard): @@ -102,17 +113,55 @@ def get_form_list(self): class FormTests(TestCase): def test_form_init(self): - testform = TestWizard.get_initkwargs([Step1, Step2]) - self.assertEqual(testform['form_list'], {'0': Step1, '1': Step2}) - - testform = TestWizard.get_initkwargs([('start', Step1), ('step2', Step2)]) - self.assertEqual(testform['form_list'], {'start': Step1, 'step2': Step2}) - - testform = TestWizard.get_initkwargs([Step1, Step2, ('finish', Step3)]) - self.assertEqual(testform['form_list'], {'0': Step1, '1': Step2, 'finish': Step3}) + # get_initkwargs() is used by as_view() to build kwargs + with patch.object(TestWizard, 'get_initkwargs') as mock: + TestWizard.as_view() + mock.assert_called_once() + + # just forms: automatic step names are generated + kwargs = TestWizard.get_initkwargs([Step1, Step2]) + self.assertEqual(kwargs['form_list'], {'0': Step1, '1': Step2}) + self.assertEqual(kwargs['initial_dict'], {}) + self.assertEqual(kwargs['instance_dict'], {}) + self.assertEqual(kwargs['condition_dict'], {}) + + # tuples with step names; optional initial_dict, instance_dict & condition_dict + kwargs = TestWizard.get_initkwargs( + [('start', Step1), ('step2', Step2)], + initial_dict={'start': {'name': 'value1'}}, + instance_dict={'start': test_instance}, + condition_dict={'step2': False}, + ) + self.assertEqual(kwargs['form_list'], {'start': Step1, 'step2': Step2}) + self.assertEqual(kwargs['initial_dict'], {'start': {'name': 'value1'}}) + self.assertEqual(kwargs['instance_dict'], {'start': test_instance}) + self.assertEqual(kwargs['condition_dict'], {'step2': False}) - testform = TestWizardWithInitAttrs.get_initkwargs() - self.assertEqual(testform['form_list'], {'0': Step1, '1': Step2}) + # forms and formsets work; name tuples optional and can be mixed with just forms + kwargs = TestWizard.get_initkwargs([Step1, Step2, ('finish', Step3FormSet)]) + self.assertEqual( + kwargs['form_list'], + {'0': Step1, '1': Step2, 'finish': Step3FormSet} + ) + self.assertEqual(kwargs['initial_dict'], {}) + self.assertEqual(kwargs['instance_dict'], {}) + self.assertEqual(kwargs['condition_dict'], {}) + + # all kwargs are defined on the class + kwargs = TestWizardWithInitAttrs.get_initkwargs() + self.assertEqual(kwargs['form_list'], {'0': Step1, '1': Step2}) + self.assertEqual(kwargs['initial_dict'], {'start': {'name': 'value1'}}) + self.assertEqual(kwargs['instance_dict'], {'start': test_instance}) + self.assertEqual(kwargs['condition_dict'], {'step2': False}) + + # FileFields require additional storage in subclass of WizardView + with self.assertRaises(NoFileStorageConfigured) as cm: + TestWizard.get_initkwargs([FormWithFileField]) + self.assertEqual( + str(cm.exception), + "You need to define 'file_storage' in your wizard view in order to " + "handle file uploads." + ) def test_first_step(self): request = get_request() From 0d7573da8fd510b9d54626dc67ff690d961cf950 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Sat, 19 Nov 2022 11:27:20 +0100 Subject: [PATCH 4/6] Check type of args passed to WizardView.done() using mock --- tests/wizard/test_forms.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/wizard/test_forms.py b/tests/wizard/test_forms.py index e5d6a59..3c08a9d 100644 --- a/tests/wizard/test_forms.py +++ b/tests/wizard/test_forms.py @@ -1,5 +1,7 @@ import unittest +from collections import OrderedDict from importlib import import_module +from unittest.mock import patch from django import forms, http from django.conf import settings @@ -97,12 +99,6 @@ class TestWizardWithInitAttrs(TestWizard): instance_dict = {'start': test_instance} -class TestWizardWithTypeCheck(TestWizard): - def done(self, form_list, **kwargs): - assert type(form_list) is list, "`form_list` was {}, should be a list".format(type(form_list)) - return http.HttpResponse("All good") - - class TestWizardWithCustomGetFormList(TestWizard): form_list = [Step1] @@ -317,6 +313,7 @@ def test_formset_instance(self): self.assertEqual(instance.get_form().initial_form_count(), 1) def test_done(self): + # validate WizardView.done() is not implemented on base class request = get_request() testform = TestWizard.as_view([('start', Step1), ('step2', Step2)]) response, instance = testform(request) @@ -338,10 +335,15 @@ def test_revalidation(self): self.assertEqual(instance.storage.current_step, 'start') def test_form_list_type(self): - request = get_request({'test_wizard_with_type_check-current_step': 'start', 'start-name': 'data1'}) - testform = TestWizardWithTypeCheck.as_view([('start', Step1)]) - response, instance = testform(request) - self.assertEqual(response.status_code, 200) + # validate WizardView.done() is called with a `list` of forms (not odict_values) + request = get_request({'test_wizard-current_step': 'start', 'start-name': 'foo'}) + testform = TestWizard.as_view([('start', Step1)]) + with patch.object(TestWizard, 'done') as mock: + _, _ = testform(request) + mock.assert_called_once() + args, kwargs = mock.call_args_list[0] + self.assertIsInstance(args[0], list) # form_list + self.assertIsInstance(kwargs['form_dict'], OrderedDict) def test_get_form_list_default(self): request = get_request() From 55b1e4cac60622ae31b784993fa4508cc4e1c822 Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Mon, 21 Nov 2022 19:41:54 +0100 Subject: [PATCH 5/6] Test initialization & context --- tests/wizard/test_forms.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/wizard/test_forms.py b/tests/wizard/test_forms.py index 3c08a9d..af46292 100644 --- a/tests/wizard/test_forms.py +++ b/tests/wizard/test_forms.py @@ -12,6 +12,8 @@ from django.test import TestCase from formtools.wizard.storage import NoFileStorageConfigured +from formtools.wizard.storage.cookie import CookieStorage +from formtools.wizard.storage.session import SessionStorage from formtools.wizard.views import ( CookieWizardView, SessionWizardView, WizardView, ) @@ -108,6 +110,21 @@ def get_form_list(self): class FormTests(TestCase): + def test_init(self): + request = get_request() + testform = TestWizard.as_view([Step1, Step2]) + response, instance = testform(request) + self.assertIsInstance(response, TemplateResponse) + context = response.context_data + self.assertEqual(set(context.keys()), {'form', 'view', 'wizard'}) + self.assertIsInstance(context['form'], Step1) + self.assertIsInstance(context['view'], TestWizard) + self.assertEqual( + set(context['wizard'].keys()), + {'form', 'steps', 'management_form'}, + ) + self.assertIsInstance(instance.storage, SessionStorage) + def test_form_init(self): # get_initkwargs() is used by as_view() to build kwargs with patch.object(TestWizard, 'get_initkwargs') as mock: @@ -369,11 +386,17 @@ class SessionFormTests(TestCase): def test_init(self): request = get_request() testform = SessionWizardView.as_view([('start', Step1)]) - self.assertIsInstance(testform(request), TemplateResponse) + response = testform(request) + self.assertIsInstance(response, TemplateResponse) + instance = response.context_data['view'] + self.assertIsInstance(instance.storage, SessionStorage) class CookieFormTests(TestCase): def test_init(self): request = get_request() testform = CookieWizardView.as_view([('start', Step1)]) - self.assertIsInstance(testform(request), TemplateResponse) + response = testform(request) + self.assertIsInstance(response, TemplateResponse) + instance = response.context_data['view'] + self.assertIsInstance(instance.storage, CookieStorage) From 69cf190ed5acec9f42059db7125aac5194a74c1a Mon Sep 17 00:00:00 2001 From: Florian Demmer Date: Mon, 21 Nov 2022 19:42:40 +0100 Subject: [PATCH 6/6] Improve tests providing instance(s) --- tests/wizard/test_forms.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/tests/wizard/test_forms.py b/tests/wizard/test_forms.py index af46292..b7cbda9 100644 --- a/tests/wizard/test_forms.py +++ b/tests/wizard/test_forms.py @@ -299,35 +299,44 @@ def test_form_initial(self): self.assertEqual(instance.get_form_initial('step2'), {}) def test_form_instance(self): + # instance_dict can provide instances for forms request = get_request() - the_instance = TestModel() + model_instance = TestModel(name='test object') testform = TestWizard.as_view( [('start', TestModelForm), ('step2', Step2)], - instance_dict={'start': the_instance} + instance_dict={'start': model_instance} ) response, instance = testform(request) - self.assertEqual(instance.get_form_instance('start'), the_instance) + self.assertEqual(instance.get_form_instance('start'), model_instance) self.assertIsNone(instance.get_form_instance('non_exist_instance')) - testform = TestWizardWithInitAttrs.as_view([('start', TestModelForm), ('step2', Step2)]) - response, instance = testform(request) - self.assertEqual( - instance.get_form_instance('start'), - TestWizardWithInitAttrs.instance_dict['start'] + # instance_dict defined in class + testform = TestWizardWithInitAttrs.as_view( + [('start', TestModelForm), ('step2', Step2)] ) + response, instance = testform(request) + self.assertEqual(instance.get_form_instance('start'), test_instance) + self.assertIsNone(instance.get_form_instance('non_exist_instance')) def test_formset_instance(self): + # instance_dict can provide querysets for formsets request = get_request() - the_instance1, created = TestModel.objects.get_or_create(name='test object 1') - the_instance2, created = TestModel.objects.get_or_create(name='test object 2') + model_instance1, _ = TestModel.objects.get_or_create(name='test object 1') + model_instance2, _ = TestModel.objects.get_or_create(name='test object 2') testform = TestWizard.as_view( [('start', TestModelFormSet), ('step2', Step2)], - instance_dict={'start': TestModel.objects.filter(name='test object 1')} + instance_dict={'start': TestModel.objects.all()} ) response, instance = testform(request) - self.assertEqual(list(instance.get_form_instance('start')), [the_instance1]) - self.assertEqual(instance.get_form_instance('non_exist_instance'), None) - self.assertEqual(instance.get_form().initial_form_count(), 1) + self.assertEqual( + list(instance.get_form_instance('start')), + [model_instance1, model_instance2], + ) + self.assertIsNone(instance.get_form_instance('non_exist_instance')) + # number of forms in formset correspond to queryset + form = instance.get_form() + self.assertIsInstance(form, TestModelFormSet) + self.assertEqual(form.initial_form_count(), 2) def test_done(self): # validate WizardView.done() is not implemented on base class