1
|
|
|
"""Test form wizard.""" |
2
|
|
|
|
3
|
|
|
import json |
4
|
|
|
|
5
|
|
|
import pytest |
6
|
|
|
|
7
|
|
|
from flask import ( |
8
|
|
|
flash, |
9
|
|
|
jsonify, |
10
|
|
|
render_template, |
11
|
|
|
request, |
12
|
|
|
session, |
13
|
|
|
) |
14
|
|
|
from flask.ext.wtf import FlaskForm |
15
|
|
|
from wtforms import ( |
16
|
|
|
IntegerField, |
17
|
|
|
StringField, |
18
|
|
|
validators, |
19
|
|
|
) |
20
|
|
|
from flask_extras.forms.wizard import MultiStepWizard |
21
|
|
|
|
22
|
|
|
from conftest import app |
23
|
|
|
|
24
|
|
|
|
25
|
|
|
TESTING_SESSION_KEY = 'fakename' |
26
|
|
|
|
27
|
|
|
|
28
|
|
|
class MultiStepTest1(FlaskForm): |
29
|
|
|
field1 = StringField(validators=[validators.DataRequired()],) |
30
|
|
|
field2 = IntegerField(validators=[validators.DataRequired()],) |
31
|
|
|
|
32
|
|
|
|
33
|
|
|
class MultiStepTest2(FlaskForm): |
34
|
|
|
field3 = StringField(validators=[validators.DataRequired()],) |
35
|
|
|
field4 = IntegerField(validators=[validators.DataRequired()],) |
36
|
|
|
|
37
|
|
|
|
38
|
|
|
class MyCoolForm(MultiStepWizard): |
39
|
|
|
__forms__ = [ |
40
|
|
|
MultiStepTest1, |
41
|
|
|
MultiStepTest2, |
42
|
|
|
] |
43
|
|
|
|
44
|
|
|
|
45
|
|
|
@pytest.fixture(scope='module') |
46
|
|
|
def ctx(request): |
47
|
|
|
with app.test_request_context() as req_ctx: |
48
|
|
|
yield req_ctx |
49
|
|
|
if TESTING_SESSION_KEY in session: |
50
|
|
|
del session[TESTING_SESSION_KEY] |
51
|
|
|
if 'MyCoolForm' in session: |
52
|
|
|
del session['MyCoolForm'] |
53
|
|
|
|
54
|
|
|
|
55
|
|
|
@app.route('/wizard', methods=['GET', 'POST']) |
56
|
|
|
def wizard_valid_route(): |
57
|
|
|
form = MyCoolForm() |
58
|
|
|
curr_step = request.args.get('curr_step') |
59
|
|
|
form_kwargs = dict(session_key=TESTING_SESSION_KEY) |
60
|
|
|
combine = 'combine' in request.args |
61
|
|
|
if curr_step is not None: |
62
|
|
|
form_kwargs.update(curr_step=curr_step) |
63
|
|
|
form = MyCoolForm(**form_kwargs) |
64
|
|
|
msg, data = None, None |
65
|
|
|
if request.method == 'POST': |
66
|
|
|
data = form.data |
67
|
|
|
if form.validate_on_submit(): |
68
|
|
|
if form.is_complete(): |
69
|
|
|
data = form.alldata(combine_fields=combine, flush_after=True) |
70
|
|
|
msg = 'Form validated AND COMPLETE! data = {}'.format(data) |
71
|
|
|
else: |
72
|
|
|
msg = ('Great job, but not done yet' |
73
|
|
|
' ({} steps remain!).'.format(form.remaining)) |
74
|
|
|
else: |
75
|
|
|
msg = 'Invalid' |
76
|
|
|
return jsonify(dict(data=data, msg=msg)) |
77
|
|
|
|
78
|
|
|
|
79
|
|
|
@app.route('/wizard-bad', methods=['GET', 'POST']) |
80
|
|
|
def wizard_invalid_route(): |
81
|
|
|
form = MultiStepWizard() |
82
|
|
|
return jsonify(form.data) |
83
|
|
|
|
84
|
|
|
|
85
|
|
|
def test_wizard_basic_init_nodata(client): |
86
|
|
|
app, test = client |
87
|
|
|
with pytest.raises(AssertionError): |
88
|
|
|
res = test.get('/wizard-bad') |
89
|
|
|
assert res.status_code == 500 |
90
|
|
|
|
91
|
|
|
|
92
|
|
|
def test_wizard_basic_init_get(client): |
93
|
|
|
app, test = client |
94
|
|
|
res = test.get('/wizard') |
95
|
|
|
assert res.status_code == 200 |
96
|
|
|
data = json.loads(res.data) |
97
|
|
|
assert data['data'] is None |
98
|
|
|
|
99
|
|
|
|
100
|
|
|
def test_wizard_basic_init_post(client): |
101
|
|
|
app, test = client |
102
|
|
|
res = test.post('/wizard', data=dict(field1='Foo', field2=2)) |
103
|
|
|
assert res.status_code == 200 |
104
|
|
|
data = json.loads(res.data) |
105
|
|
|
assert data['data'] == dict(field1='Foo', field2=2) |
106
|
|
|
|
107
|
|
|
|
108
|
|
|
def test_wizard_basic_post_jumpstep(client): |
109
|
|
|
app, test = client |
110
|
|
|
res = test.post('/wizard?curr_step=2', |
111
|
|
|
data=dict(field3='Foo', field4=4)) |
112
|
|
|
assert res.status_code == 200 |
113
|
|
|
data = json.loads(res.data) |
114
|
|
|
assert data['data'] == dict(field3='Foo', field4=4) |
115
|
|
|
|
116
|
|
|
|
117
|
|
|
def test_wizard_post_fullsteps(ctx, client): |
118
|
|
|
app, test = client |
119
|
|
|
res1 = test.post('/wizard?curr_step=1', |
120
|
|
|
data=dict(field1='Foo', field2=2)) |
121
|
|
|
res2 = test.post('/wizard?curr_step=2', |
122
|
|
|
data=dict(field3='Foo', field4=4)) |
123
|
|
|
assert res1.status_code == 200 |
124
|
|
|
assert res2.status_code == 200 |
125
|
|
|
data = json.loads(res2.data) |
126
|
|
|
assert data['data'] == dict(field3='Foo', field4=4) |
127
|
|
|
|
128
|
|
|
|
129
|
|
|
def test_wizard_post_bad_fieldtype(ctx, client): |
130
|
|
|
app, test = client |
131
|
|
|
res1 = test.post('/wizard?curr_step=1', |
132
|
|
|
data=dict(field1=1, field2=2)) |
133
|
|
|
res2 = test.post('/wizard?curr_step=2', |
134
|
|
|
data=dict(field3='Foo', field4='Foo')) |
135
|
|
|
assert res1.status_code == 200 |
136
|
|
|
assert res2.status_code == 200 |
137
|
|
|
data = json.loads(res2.data) |
138
|
|
|
assert data['data']['field4'] is None |
139
|
|
|
assert data['msg'] == 'Invalid' |
140
|
|
|
|
141
|
|
|
|
142
|
|
|
def test_form_is_complete(client): |
143
|
|
|
form = MyCoolForm() |
144
|
|
|
assert not form.is_complete() |
145
|
|
|
|
146
|
|
|
|
147
|
|
|
def test_form_setfields(client): |
148
|
|
|
form = MyCoolForm() |
149
|
|
|
assert len(form._unbound_fields) == 1 |
150
|
|
|
assert len(form.__forms__) == 2 |
151
|
|
|
# Confirm it's obfuscated for non-local access. |
152
|
|
|
assert not hasattr(form, '__forms') |
153
|
|
|
|
154
|
|
|
|
155
|
|
|
def test_form_get_data(client): |
156
|
|
|
app, test = client |
157
|
|
|
form = MyCoolForm() |
158
|
|
|
assert form.data == dict(field1=None, field2=None) |
159
|
|
|
|
160
|
|
|
|
161
|
|
|
def test_form_get_alldata(client): |
162
|
|
|
form = MyCoolForm() |
163
|
|
|
assert form.alldata() == dict( |
164
|
|
|
MultiStepTest1=None, |
165
|
|
|
MultiStepTest2=None, |
166
|
|
|
) |
167
|
|
|
|
168
|
|
|
|
169
|
|
|
def test_form_get_alldata_combined_none(client): |
170
|
|
|
form = MyCoolForm() |
171
|
|
|
assert form.alldata(combine_fields=True) == dict() |
172
|
|
|
|
173
|
|
|
|
174
|
|
|
def test_form_populate_forms(client): |
175
|
|
|
form = MyCoolForm() |
176
|
|
|
assert len(form.forms) == 2 |
177
|
|
|
|
178
|
|
|
|
179
|
|
|
def test_form_populate_forms_once_only(client): |
180
|
|
|
form = MyCoolForm() |
181
|
|
|
form._populate_forms() |
182
|
|
|
form._populate_forms() |
183
|
|
|
assert len(form.forms) == 2 |
184
|
|
|
|
185
|
|
|
|
186
|
|
|
def test_form_active(client): |
187
|
|
|
form = MyCoolForm() |
188
|
|
|
assert form.active_name == 'MultiStepTest1' |
189
|
|
|
assert isinstance(form.active_form, MultiStepTest1) |
190
|
|
|
|
191
|
|
|
|
192
|
|
|
def test_form_step_attrs(client): |
193
|
|
|
form = MyCoolForm() |
194
|
|
|
assert form.step == 1 |
195
|
|
|
assert form.total_steps == 2 |
196
|
|
|
assert form.remaining == 2 |
197
|
|
|
assert form.steps == [1, 2] |
198
|
|
|
|
199
|
|
|
|
200
|
|
|
def test_form_flush(client): |
201
|
|
|
form = MyCoolForm() |
202
|
|
|
assert 'MyCoolForm' in session |
203
|
|
|
form.flush() |
204
|
|
|
assert 'MyCoolForm' not in session |
205
|
|
|
|
206
|
|
|
|
207
|
|
|
def test_form_validate_on_submit(client): |
208
|
|
|
form = MyCoolForm() |
209
|
|
|
assert not form.validate_on_submit() |
210
|
|
|
|
211
|
|
|
|
212
|
|
|
def test_form_len_override(client): |
213
|
|
|
form = MyCoolForm() |
214
|
|
|
assert len(form) == 2 |
215
|
|
|
|
216
|
|
|
|
217
|
|
|
def test_form_iter_override(client): |
218
|
|
|
form = MyCoolForm() |
219
|
|
|
assert iter(form) |
220
|
|
|
|
221
|
|
|
|
222
|
|
|
def test_form_getitem_override(client): |
223
|
|
|
form = MyCoolForm() |
224
|
|
|
assert isinstance(form['field1'], StringField) |
225
|
|
|
assert form['field1'].name == 'field1' |
226
|
|
|
form.validate_on_submit() |
227
|
|
|
assert form['field1'].data is None |
228
|
|
|
|
229
|
|
|
|
230
|
|
|
def test_form_wtform_contains(client): |
231
|
|
|
form = MyCoolForm() |
232
|
|
|
assert 'field1' in form |
233
|
|
|
assert 'field2' in form |
234
|
|
|
assert 'field3' not in form |
235
|
|
|
assert 'field4' not in form |
236
|
|
|
|
237
|
|
|
|
238
|
|
|
def test_errors(client): |
239
|
|
|
form = MyCoolForm() |
240
|
|
|
assert form.errors == dict() |
241
|
|
|
|