Skip to content

Commit d7698cd

Browse files
committed
Fixed form-filler wrt checkboxes and radios, and added some tests (to be completed: <select> and <input type=radio> still don't have tests)
--HG-- extra : convert_revision : svn%3Aacbfec75-9323-0410-a652-858a13e371e0/trunk%40916
1 parent 31d2b07 commit d7698cd

File tree

2 files changed

+215
-164
lines changed

2 files changed

+215
-164
lines changed

src/html5lib/filters/formfiller.py

Lines changed: 121 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,121 @@
1-
#
2-
# The goal is to finally have a form filler where you pass data for
3-
# each form, using the algorithm for "Seeding a form with initial values"
4-
# See http://www.whatwg.org/specs/web-forms/current-work/#seeding
5-
#
6-
7-
import _base
8-
9-
from html5lib.constants import spaceCharacters
10-
spaceCharacters = u"".join(spaceCharacters)
11-
12-
class SimpleFilter(_base.Filter):
13-
def __init__(self, source, fieldStorage):
14-
_base.Filter.__init__(self, source)
15-
self.fieldStorage = fieldStorage
16-
17-
def __iter__(self):
18-
field_indices = {}
19-
state = None
20-
field_name = None
21-
for token in _base.Filter.__iter__(self):
22-
type = token["type"]
23-
if type in ("StartTag", "EmptyTag"):
24-
name = token["name"].lower()
25-
if name == "input":
26-
field_name = None
27-
field_type = None
28-
input_value_index = -1
29-
input_checked_index = -1
30-
for i,(n,v) in enumerate(token["data"]):
31-
n = n.lower()
32-
if n == "name":
33-
field_name = v.strip(spaceCharacters)
34-
elif n == "type":
35-
field_type = v.strip(spaceCharacters)
36-
elif n == "checked":
37-
input_checked_index = i
38-
elif n == "value":
39-
input_value_index = i
40-
41-
value_list = self.fieldStorage.getlist(field_name)
42-
field_index = field_indices.setdefault(field_name, 0)
43-
if field_index < len(value_list):
44-
value = value_list[field_index]
45-
else:
46-
value = ""
47-
48-
if type in ("checkbox", "radio"):
49-
if value_list:
50-
if token["data"][input_value_index] == value:
51-
if input_checked_index < 0:
52-
token["data"].append((u"checked", u""))
53-
field_indices[field_name] = field_index + 1
54-
elif input_checked_index >= 0:
55-
del token["data"][input_checked_index]
56-
57-
elif type not in ("button", "submit", "reset"):
58-
if input_value_index >= 0:
59-
token["data"][input_value_index] = (u"value", value)
60-
else:
61-
token["data"].append((u"value", value))
62-
field_indices[field_name] = field_index + 1
63-
64-
field_type = None
65-
field_name = None
66-
67-
elif name == "textarea":
68-
field_type = "textarea"
69-
field_name = dict(reversed(token["data"]))["name"]
70-
71-
elif name == "select":
72-
field_type = "select"
73-
attributes = dict(reversed(token["data"]))
74-
field_name = attributes.get("name")
75-
is_select_multiple = "multiple" in attributes
76-
77-
elif state == "in_select" and field_name and name == "option":
78-
option_selected_index = -1
79-
option_value = None
80-
for i,(n,v) in enumerate(token["data"]):
81-
n = n.lower()
82-
if n == "selected":
83-
option_selected_index = i
84-
elif n == "value":
85-
option_value = v.strip(spaceCharacters)
86-
if option_value is None:
87-
raise NotImplementedError("<option>s must have a value= attribute")
88-
else:
89-
value_list = self.fieldStorage.getlist(field_name)
90-
if value_list:
91-
field_index = field_indices.setdefault(field_name, 0)
92-
if field_index < len(value_list):
93-
value = value_list[field_index]
94-
else:
95-
value = ""
96-
if option_value == value:
97-
if option_selected_index < 0:
98-
token["data"].append((u"selected", u""))
99-
field_indices[field_name] = field_index + 1
100-
elif option_selected_index >= 0:
101-
del token["data"][option_selected_index]
102-
103-
elif field_type is not None and field_name and type == "EndTag":
104-
name = token["name"].lower()
105-
if name == "textarea":
106-
value_list = self.fieldStorage.getlist(field_name)
107-
if value_list:
108-
field_index = field_indices.setdefault(field_name, 0)
109-
if field_index < len(value_list):
110-
value = value_list[field_index]
111-
else:
112-
value = ""
113-
yield {"type": "Characters", "data": value}
114-
115-
field_indices[field_name] = field_index + 1
116-
field_name = None
117-
118-
elif field_type == "textarea":
119-
continue # ignore token
120-
121-
yield token
1+
#
2+
# The goal is to finally have a form filler where you pass data for
3+
# each form, using the algorithm for "Seeding a form with initial values"
4+
# See http://www.whatwg.org/specs/web-forms/current-work/#seeding
5+
#
6+
7+
import _base
8+
9+
from html5lib.constants import spaceCharacters
10+
spaceCharacters = u"".join(spaceCharacters)
11+
12+
class SimpleFilter(_base.Filter):
13+
def __init__(self, source, fieldStorage):
14+
_base.Filter.__init__(self, source)
15+
self.fieldStorage = fieldStorage
16+
17+
def __iter__(self):
18+
field_indices = {}
19+
state = None
20+
field_name = None
21+
for token in _base.Filter.__iter__(self):
22+
type = token["type"]
23+
if type in ("StartTag", "EmptyTag"):
24+
name = token["name"].lower()
25+
if name == "input":
26+
field_name = None
27+
field_type = None
28+
input_value_index = -1
29+
input_checked_index = -1
30+
for i,(n,v) in enumerate(token["data"]):
31+
n = n.lower()
32+
if n == u"name":
33+
field_name = v.strip(spaceCharacters)
34+
elif n == u"type":
35+
field_type = v.strip(spaceCharacters)
36+
elif n == u"checked":
37+
input_checked_index = i
38+
elif n == u"value":
39+
input_value_index = i
40+
41+
value_list = self.fieldStorage.getlist(field_name)
42+
field_index = field_indices.setdefault(field_name, 0)
43+
if field_index < len(value_list):
44+
value = value_list[field_index]
45+
else:
46+
value = ""
47+
48+
if field_type in (u"checkbox", u"radio"):
49+
if value_list:
50+
if token["data"][input_value_index][1] == value:
51+
if input_checked_index < 0:
52+
token["data"].append((u"checked", u""))
53+
field_indices[field_name] = field_index + 1
54+
elif input_checked_index >= 0:
55+
del token["data"][input_checked_index]
56+
57+
elif field_type not in (u"button", u"submit", u"reset"):
58+
if input_value_index >= 0:
59+
token["data"][input_value_index] = (u"value", value)
60+
else:
61+
token["data"].append((u"value", value))
62+
field_indices[field_name] = field_index + 1
63+
64+
field_type = None
65+
field_name = None
66+
67+
elif name == "textarea":
68+
field_type = "textarea"
69+
field_name = dict(reversed(token["data"]))["name"]
70+
71+
elif name == "select":
72+
field_type = "select"
73+
attributes = dict(reversed(token["data"]))
74+
field_name = attributes.get("name")
75+
is_select_multiple = "multiple" in attributes
76+
77+
elif state == "in_select" and field_name and name == "option":
78+
option_selected_index = -1
79+
option_value = None
80+
for i,(n,v) in enumerate(token["data"]):
81+
n = n.lower()
82+
if n == "selected":
83+
option_selected_index = i
84+
elif n == "value":
85+
option_value = v.strip(spaceCharacters)
86+
if option_value is None:
87+
raise NotImplementedError("<option>s must have a value= attribute")
88+
else:
89+
value_list = self.fieldStorage.getlist(field_name)
90+
if value_list:
91+
field_index = field_indices.setdefault(field_name, 0)
92+
if field_index < len(value_list):
93+
value = value_list[field_index]
94+
else:
95+
value = ""
96+
if option_value == value:
97+
if option_selected_index < 0:
98+
token["data"].append((u"selected", u""))
99+
field_indices[field_name] = field_index + 1
100+
elif option_selected_index >= 0:
101+
del token["data"][option_selected_index]
102+
103+
elif field_type is not None and field_name and type == "EndTag":
104+
name = token["name"].lower()
105+
if name == "textarea":
106+
value_list = self.fieldStorage.getlist(field_name)
107+
if value_list:
108+
field_index = field_indices.setdefault(field_name, 0)
109+
if field_index < len(value_list):
110+
value = value_list[field_index]
111+
else:
112+
value = ""
113+
yield {"type": "Characters", "data": value}
114+
115+
field_indices[field_name] = field_index + 1
116+
field_name = None
117+
118+
elif field_type == "textarea":
119+
continue # ignore token
120+
121+
yield token

tests/test_formfiller.py

Lines changed: 94 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,94 @@
1-
import unittest
2-
3-
from html5lib.filters.formfiller import SimpleFilter
4-
5-
class FieldStorage(dict):
6-
def getlist(self, name):
7-
l = self[name]
8-
if isinstance(l, list):
9-
return l
10-
elif isinstance(l, tuple) or hasattr(l, '__iter__'):
11-
return list(l)
12-
return [l]
13-
14-
class TestCase(unittest.TestCase):
15-
def runTest(self, input, formdata, expected):
16-
output = list(SimpleFilter(input, formdata))
17-
errorMsg = "\n".join(["\n\nInput:", str(input),
18-
"\nForm data:", str(formdata),
19-
"\nExpected:", str(expected),
20-
"\nReceived:", str(output)])
21-
self.assertEquals(output, expected, errorMsg)
22-
23-
def testSingleTextInputWithValue(self):
24-
self.runTest(
25-
[{"type": u"EmptyTag", "name": u"input",
26-
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", u"quux")]}],
27-
FieldStorage({"foo": "bar"}),
28-
[{"type": u"EmptyTag", "name": u"input",
29-
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", "bar")]}])
30-
31-
def testSingleTextInputWithoutValue(self):
32-
self.runTest(
33-
[{"type": u"EmptyTag", "name": u"input",
34-
"data": [(u"type", u"text"), (u"name", u"foo")]}],
35-
FieldStorage({"foo": "bar"}),
36-
[{"type": u"EmptyTag", "name": u"input",
37-
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", "bar")]}])
38-
39-
def main():
40-
unittest.main()
41-
42-
if __name__ == "__main__":
43-
main()
1+
import unittest
2+
3+
from html5lib.filters.formfiller import SimpleFilter
4+
5+
class FieldStorage(dict):
6+
def getlist(self, name):
7+
l = self[name]
8+
if isinstance(l, list):
9+
return l
10+
elif isinstance(l, tuple) or hasattr(l, '__iter__'):
11+
return list(l)
12+
return [l]
13+
14+
class TestCase(unittest.TestCase):
15+
def runTest(self, input, formdata, expected):
16+
output = list(SimpleFilter(input, formdata))
17+
errorMsg = "\n".join(["\n\nInput:", str(input),
18+
"\nForm data:", str(formdata),
19+
"\nExpected:", str(expected),
20+
"\nReceived:", str(output)])
21+
self.assertEquals(output, expected, errorMsg)
22+
23+
def testSingleTextInputWithValue(self):
24+
self.runTest(
25+
[{"type": u"EmptyTag", "name": u"input",
26+
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", u"quux")]}],
27+
FieldStorage({"foo": "bar"}),
28+
[{"type": u"EmptyTag", "name": u"input",
29+
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", u"bar")]}])
30+
31+
def testSingleTextInputWithoutValue(self):
32+
self.runTest(
33+
[{"type": u"EmptyTag", "name": u"input",
34+
"data": [(u"type", u"text"), (u"name", u"foo")]}],
35+
FieldStorage({"foo": "bar"}),
36+
[{"type": u"EmptyTag", "name": u"input",
37+
"data": [(u"type", u"text"), (u"name", u"foo"), (u"value", u"bar")]}])
38+
39+
def testSingleCheckbox(self):
40+
self.runTest(
41+
[{"type": u"EmptyTag", "name": u"input",
42+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"bar")]}],
43+
FieldStorage({"foo": "bar"}),
44+
[{"type": u"EmptyTag", "name": u"input",
45+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"bar"), (u"checked", u"")]}])
46+
47+
def testSingleCheckboxShouldBeUnchecked(self):
48+
self.runTest(
49+
[{"type": u"EmptyTag", "name": u"input",
50+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"quux")]}],
51+
FieldStorage({"foo": "bar"}),
52+
[{"type": u"EmptyTag", "name": u"input",
53+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"quux")]}])
54+
55+
def testSingleCheckboxCheckedByDefault(self):
56+
self.runTest(
57+
[{"type": u"EmptyTag", "name": u"input",
58+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"bar"), (u"checked", u"")]}],
59+
FieldStorage({"foo": "bar"}),
60+
[{"type": u"EmptyTag", "name": u"input",
61+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"bar"), (u"checked", u"")]}])
62+
63+
def testSingleCheckboxCheckedByDefaultShouldBeUnchecked(self):
64+
self.runTest(
65+
[{"type": u"EmptyTag", "name": u"input",
66+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"quux"), (u"checked", u"")]}],
67+
FieldStorage({"foo": "bar"}),
68+
[{"type": u"EmptyTag", "name": u"input",
69+
"data": [(u"type", u"checkbox"), (u"name", u"foo"), (u"value", u"quux")]}])
70+
71+
def testSingleTextareaWithValue(self):
72+
self.runTest(
73+
[{"type": u"StartTag", "name": u"textarea", "data": [(u"name", u"foo")]},
74+
{"type": u"Characters", "data": u"quux"},
75+
{"type": u"EndTag", "name": u"textarea", "data": []}],
76+
FieldStorage({"foo": "bar"}),
77+
[{"type": u"StartTag", "name": u"textarea", "data": [(u"name", u"foo")]},
78+
{"type": u"Characters", "data": u"bar"},
79+
{"type": u"EndTag", "name": u"textarea", "data": []}])
80+
81+
def testSingleTextareaWithoutValue(self):
82+
self.runTest(
83+
[{"type": u"StartTag", "name": u"textarea", "data": [(u"name", u"foo")]},
84+
{"type": u"EndTag", "name": u"textarea", "data": []}],
85+
FieldStorage({"foo": "bar"}),
86+
[{"type": u"StartTag", "name": u"textarea", "data": [(u"name", u"foo")]},
87+
{"type": u"Characters", "data": u"bar"},
88+
{"type": u"EndTag", "name": u"textarea", "data": []}])
89+
90+
def main():
91+
unittest.main()
92+
93+
if __name__ == "__main__":
94+
main()

0 commit comments

Comments
 (0)