8000 Fixes handling of <select> and <select multiple> (with unit tests) · awesome-python/html5lib-python@0b11ee2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0b11ee2

Browse files
committed
Fixes handling of <select> and <select multiple> (with unit tests)
--HG-- extra : convert_revision : svn%3Aacbfec75-9323-0410-a652-858a13e371e0/trunk%40917
1 parent d7698cd commit 0b11ee2

File tree

2 files changed

+212
-14
lines changed

2 files changed

+212
-14
lines changed

src/html5lib/filters/formfiller.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ def __iter__(self):
7373
attributes = dict(reversed(token["data"]))
7474
field_name = attributes.get("name")
7575
is_select_multiple = "multiple" in attributes
76+
is_selected_option_found = False
7677

77-
elif state == "in_select" and field_name and name == "option":
78+
elif field_type == "select" and field_name and name == "option":
7879
option_selected_index = -1
7980
option_value = None
8081
for i,(n,v) in enumerate(token["data"]):
@@ -84,7 +85,7 @@ def __iter__(self):
8485
elif n == "value":
8586
option_value = v.strip(spaceCharacters)
8687
if option_value is None:
87-
raise NotImplementedError("<option>s must have a value= attribute")
88+
raise NotImplementedError("<option>s without a value= attribute")
8889
else:
8990
value_list = self.fieldStorage.getlist(field_name)
9091
if value_list:
@@ -93,27 +94,32 @@ def __iter__(self):
9394
value = value_list[field_index]
9495
else:
9596
value = ""
96-
if option_value == value:
97+
if (is_select_multiple or not is_selected_option_found) and option_value == value:
9798
if option_selected_index < 0:
9899
token["data"].append((u"selected", u""))
99100
field_indices[field_name] = field_index + 1
101+
is_selected_option_found = True
100102
elif option_selected_index >= 0:
101103
del token["data"][option_selected_index]
102104

103105
elif field_type is not None and field_name and type == "EndTag":
104106
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}
107+
if name == field_type:
108+
if name == "textarea":
109+
value_list = self.fieldStorage.getlist(field_name)
110+
if value_list:
111+
field_index = field_indices.setdefault(field_name, 0)
112+
if field_index < len(value_list):
113+
value = value_list[field_index]
114+
else:
115+
value = ""
116+
yield {"type": "Characters", "data": value}
117+
field_indices[field_name] = field_index + 1
118+
119+
field_name = None
114120

115-
field_indices[field_name] = field_index + 1
116-
field_name = None
121+
elif name == "option" and field_type == "select":
122+
pass # TODO: part of "option without value= attribute" processing
117123

118124
elif field_type == "textarea":
119125
continue # ignore token

tests/test_formfiller.py

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,198 @@ def testSingleTextareaWithoutValue(self):
8787
{"type": u"Characters", "data": u"bar"},
8888
{"type": u"EndTag", "name": u"textarea", "data": []}])
8989

90+
def testSingleSelectWithValue(self):
91+
self.runTest(
92+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
93+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
94+
{"type": u"Characters", "data": u"quux"},
95+
{"type": u"EndTag", "name": u"option", "data": []},
96+
{"type": u"EndTag", "name": u"select", "data": []}],
97+
FieldStorage({"foo": "bar"}),
98+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
99+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar"), (u"selected", u"")]},
100+
{"type": u"Characters", "data": u"quux"},
101+
{"type": u"EndTag", "name": u"option", "data": []},
102< F438 code class="diff-text syntax-highlighted-line addition">+
{"type": u"EndTag", "name": u"select", "data": []}])
103+
104+
def testSingleSelectWithValueShouldBeUnselected(self):
105+
self.runTest(
106+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
107+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
108+
{"type": u"Characters", "data": u"quux"},
109+
{"type": u"EndTag", "name": u"option", "data": []},
110+
{"type": u"EndTag", "name": u"select", "data": []}],
111+
FieldStorage({"foo": "quux"}),
112+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
113+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
114+
{"type": u"Characters", "data": u"quux"},
115+
{"type": u"EndTag", "name": u"option", "data": []},
116+
{"type": u"EndTag", "name": u"select", "data": []}])
117+
118+
def testSingleSelectWithoutValue(self):
119+
self.runTest(
120+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
121+
{"type": u"StartTag", "name": u"option", "data": []},
122+
{"type": u"Characters", "data": u"bar"},
123+
{"type": u"EndTag", "name": u"option", "data": []},
124+
{"type": u"EndTag", "name": u"select", "data": []}],
125+
FieldStorage({"foo": "bar"}),
126+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
127+
{"type": u"StartTag", "name": u"option", "data": [(u"selected", u"")]},
128+
{"type": u"Characters", "data": u"bar"},
129+
{"type": u"EndTag", "name": u"option", "data": []},
130+
{"type": u"EndTag", "name": u"select", "data": []}])
131+
132+
def testSingleSelectWithoutValueShouldBeUnselected(self):
133+
self.runTest(
134+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
135+
{"type": u"StartTag", "name": u"option", "data": []},
136+
{"type": u"Characters", "data": u"bar"},
137+
{"type": u"EndTag", "name": u"option", "data": []},
138+
{"type": u"EndTag", "name": u"select", "data": []}],
139+
FieldStorage({"foo": "quux"}),
140+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
141+
{"type": u"StartTag", "name": u"option", "data": []},
142+
{"type": u"Characters", "data": u"bar"},
143+
{"type": u"EndTag", "name": u"option", "data": []},
144+
{"type": u"EndTag", "name": u"select", "data": []}])
145+
146+
def testSingleSelectTwoOptionsWithValue(self):
147+
self.runTest(
148+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
149+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
150+
{"type": u"Characters", "data": u"quux"},
151+
{"type": u"EndTag", "name": u"option", "data": []},
152+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux")]},
153+
{"type": u"Characters", "data": u"quux"},
154+
{"type": u"EndTag", "name": u"option", "data": []},
155+
{"type": u"EndTag", "name": u"select", "data": []}],
156+
FieldStorage({"foo": "bar"}),
157+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
158+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar"), (u"selected", u"")]},
159+
{"type": u"Characters", "data": u"quux"},
160+
{"type": u"EndTag", "name": u"option", "data": []},
161+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux")]},
162+
{"type": u"Characters", "data": u"quux"},
163+
{"type": u"EndTag", "name": u"option", "data": []},
164+
{"type": u"EndTag", "name": u"select", "data": []}])
165+
166+
def testSingleSelectTwoOptionsWithValueShouldBeUnselected(self):
167+
self.runTest(
168+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
169+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
170+
{"type": u"Characters", "data": u"quux"},
171+
{"type": u"EndTag", "name": u"option", "data": []},
172+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"baz")]},
173+
{"type": u"Characters", "data": u"quux"},
174+
{"type": u"EndTag", "name": u"option", "data": []},
175+
{"type": u"EndTag", "name": u"select", "data": []}],
176+
FieldStorage({"foo": "quux"}),
177+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
178+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
179+
{"type": u"Characters", "data": u"quux"},
180+
{"type": u"EndTag", "name": u"option", "data": []},
181+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"baz")]},
182+
{"type": u"Characters", "data": u"quux"},
183+
{"type": u"EndTag", "name": u"option", "data": []},
184+
{"type": u"EndTag", "name": u"select", "data": []}])
185+
186+
def testSingleSelectTwoOptionsWithoutValue(self):
187+
self.runTest(
188+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
189+
{"type": u"StartTag", "name": u"option", "data": []},
190+
{"type": u"Characters", "data": u"bar"},
191+
{"type": u"EndTag", "name": u"option", "data": []},
192+
{"type": u"StartTag", "name": u"option", "data": []},
193+
{"type": u"Characters", "data": u"quux"},
194+
{"type": u"EndTag", "name": u"option", "data": []},
195+
{"type": u"EndTag", "name": u"select", "data": []}],
196+
FieldStorage({"foo": "bar"}),
197+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
198+
{"type": u"StartTag", "name": u"option", "data": [(u"selected", u"")]},
199+
{"type": u"Characters", "data": u"bar"},
200+
{"type": u"EndTag", "name": u"option", "data": []},
201+
{"type": u"StartTag", "name": u"option", "data": []},
202+
{"type": u"Characters", "data": u"quux"},
203+
{"type": u"EndTag", "name": u"option", "data": []},
204+
{"type": u"EndTag", "name": u"select", "data": []}])
205+
206+
def testSingleSelectTwoOptionsWithoutValueShouldBeUnselected(self):
207+
self.runTest(
208+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
209+
{"type": u"StartTag", "name": u"option", "data": []},
210+
{"type": u"Characters", "data": u"bar"},
211+
{"type": u"EndTag", "name": u"option", "data": []},
212+
{"type": u"StartTag", "name": u"option", "data": []},
213+
{"type": u"Characters", "data": u"baz"},
214+
{"type": u"EndTag", "name": u"option", "data": []},
215+
{"type": u"EndTag", "name": u"select", "data": []}],
216+
FieldStorage({"foo": "quux"}),
217+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
218+
{"type": u"StartTag", "name": u"option", "data": []},
219+
{"type": u"Characters", "data": u"bar"},
220+
{"type": u"EndTag", "name": u"option", "data": []},
221+
{"type": u"StartTag", "name": u"option", "data": []},
222+
{"type": u"Characters", "data": u"baz"},
223+
{"type": u"EndTag", "name": u"option", "data": []},
224+
{"type": u"EndTag", "name": u"select", "data": []}])
225+
226+
def testSingleSelectMultiple(self):
227+
self.runTest(
228+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo"), (u"multiple", u"")]},
229+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
230+
{"type": u"Characters", "data": u"quux"},
231+
{"type": u"EndTag", "name": u"option", "data": []},
232+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux")]},
233+
{"type": u"Characters", "data": u"quux"},
234+
{"type": u"EndTag", "name": u"option", "data": []},
235+
{"type": u"EndTag", "name": u"select", "data": []}],
236+
FieldStorage({"foo": ["bar", "quux"]}),
237+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo"), (u"multiple", u"")]},
238+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar"), (u"selected", u"")]},
239+
{"type": u"Characters", "data": u"quux"},
240+
{"type": u"EndTag", "name": u"option", "data": []},
241+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux"), (u"selected", u"")]},
242+
{"type": u"Characters", "data": u"quux"},
243+
{"type": u"EndTag", "name": u"option", "data": []},
244+
{"type": u"EndTag", "name": u"select", "data": []}])
245+
246+
def testTwoSelect(self):
247+
self.runTest(
248+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
249+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
250+
{"type": u"Characters", "data": u"quux"},
251+
{"type": u"EndTag", "name": u"option", "data": []},
252+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux")]},
253+
{"type": u"Characters", "data": u"quux"},
254+
{"type": u"EndTag", "name": u"option", "data": []},
255+
{"type": u"EndTag", "name": u"select", "data": []},
256+
{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
257+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
258+
{"type": u"Characters", "data": u"quux"},
259+
{"type": u"EndTag", "name": u"option", "data": []},
260+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux")]},
261+
{"type": u"Characters", "data": u"quux"},
262+
{"type": u"EndTag", "name": u"option", "data": []},
263+
{"type": u"EndTag", "name": u"select", "data": []}],
264+
FieldStorage({"foo": ["bar", "quux"]}),
265+
[{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
266+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar"), (u"selected", u"")]},
267+
{"type": u"Characters", "data": u"quux"},
268+
{"type": u"EndTag", "name": u"option", "data": []},
269+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux")]},
270+
{"type": u"Characters", "data": u"quux"},
271+
{"type": u"EndTag", "name": u"option", "data": []},
272+
{"type": u"EndTag", "name": u"select", "data": []},
273+
{"type": u"StartTag", "name": u"select", "data": [(u"name", u"foo")]},
274+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"bar")]},
275+
{"type": u"Characters", "data": u"quux"},
276+
{"type": u"EndTag", "name": u"option", "data": []},
277+
{"type": u"StartTag", "name": u"option", "data": [(u"value", u"quux"), (u"selected", u"")]},
278+
{"type": u"Characters", "data": u"quux"},
279+
{"type": u"EndTag", "name": u"option", "data": []},
280+
{"type": u"EndTag", "name": u"select", "data": []}])
281+
90282
def main():
91283
unittest.main()
92284

0 commit comments

Comments
 (0)
0