9 is(DOM::Tiny->new, '', 'right result');
10 is(DOM::Tiny->new(''), '', 'right result');
11 is(DOM::Tiny->new->parse(''), '', 'right result');
12 is(DOM::Tiny->new->at('p'), undef, 'no result');
13 is(DOM::Tiny->new->append_content(''), '', 'right result');
14 is(DOM::Tiny->new->all_text, '', 'right result');
18 = DOM::Tiny->new('<div><div FOO="0" id="a">A</div><div id="b">B</div></div>');
19 is $dom->at('#b')->text, 'B', 'right text';
21 push @div, $dom->find('div[id]')->map('text')->each;
22 is_deeply \@div, [qw(A B)], 'found all div elements with id';
24 $dom->find('div[id]')->each(sub { push @div, $_->text });
25 is_deeply \@div, [qw(A B)], 'found all div elements with id';
26 is $dom->at('#a')->attr('foo'), 0, 'right attribute';
27 is $dom->at('#a')->attr->{foo}, 0, 'right attribute';
28 is "$dom", '<div><div foo="0" id="a">A</div><div id="b">B</div></div>',
31 # Tap into method chain
32 $dom = DOM::Tiny->new->parse('<div id="a">A</div><div id="b">B</div>');
33 is_deeply [$dom->find('[id]')->map(attr => 'id')->each], [qw(a b)],
35 is $dom->tap(sub { $_->at('#b')->remove }), '<div id="a">A</div>',
38 # Build tree from scratch
39 is(DOM::Tiny->new->append_content('<p>')->at('p')->append_content('0')->text,
42 # Simple nesting with healing (tree structure)
43 $dom = DOM::Tiny->new(<<EOF);
44 <foo><bar a="b<c">ju<baz a23>s<bazz />t</bar>works</foo>
46 is $dom->tree->[0], 'root', 'right type';
47 is $dom->tree->[1][0], 'tag', 'right type';
48 is $dom->tree->[1][1], 'foo', 'right tag';
49 is_deeply $dom->tree->[1][2], {}, 'empty attributes';
50 is $dom->tree->[1][3], $dom->tree, 'right parent';
51 is $dom->tree->[1][4][0], 'tag', 'right type';
52 is $dom->tree->[1][4][1], 'bar', 'right tag';
53 is_deeply $dom->tree->[1][4][2], {a => 'b<c'}, 'right attributes';
54 is $dom->tree->[1][4][3], $dom->tree->[1], 'right parent';
55 is $dom->tree->[1][4][4][0], 'text', 'right type';
56 is $dom->tree->[1][4][4][1], 'ju', 'right text';
57 is $dom->tree->[1][4][4][2], $dom->tree->[1][4], 'right parent';
58 is $dom->tree->[1][4][5][0], 'tag', 'right type';
59 is $dom->tree->[1][4][5][1], 'baz', 'right tag';
60 is_deeply $dom->tree->[1][4][5][2], {a23 => undef}, 'right attributes';
61 is $dom->tree->[1][4][5][3], $dom->tree->[1][4], 'right parent';
62 is $dom->tree->[1][4][5][4][0], 'text', 'right type';
63 is $dom->tree->[1][4][5][4][1], 's', 'right text';
64 is $dom->tree->[1][4][5][4][2], $dom->tree->[1][4][5], 'right parent';
65 is $dom->tree->[1][4][5][5][0], 'tag', 'right type';
66 is $dom->tree->[1][4][5][5][1], 'bazz', 'right tag';
67 is_deeply $dom->tree->[1][4][5][5][2], {}, 'empty attributes';
68 is $dom->tree->[1][4][5][5][3], $dom->tree->[1][4][5], 'right parent';
69 is $dom->tree->[1][4][5][6][0], 'text', 'right type';
70 is $dom->tree->[1][4][5][6][1], 't', 'right text';
71 is $dom->tree->[1][4][5][6][2], $dom->tree->[1][4][5], 'right parent';
72 is $dom->tree->[1][5][0], 'text', 'right type';
73 is $dom->tree->[1][5][1], 'works', 'right text';
74 is $dom->tree->[1][5][2], $dom->tree->[1], 'right parent';
75 is "$dom", <<EOF, 'right result';
76 <foo><bar a="b<c">ju<baz a23>s<bazz></bazz>t</baz></bar>works</foo>
79 # Select based on parent
80 $dom = DOM::Tiny->new(<<EOF);
83 <div><div>test2</div></div>
86 is $dom->find('body > div')->[0]->text, 'test1', 'right text';
87 is $dom->find('body > div')->[1]->text, '', 'no content';
88 is $dom->find('body > div')->[2], undef, 'no result';
89 is $dom->find('body > div')->size, 2, 'right number of elements';
90 is $dom->find('body > div > div')->[0]->text, 'test2', 'right text';
91 is $dom->find('body > div > div')->[1], undef, 'no result';
92 is $dom->find('body > div > div')->size, 1, 'right number of elements';
94 # A bit of everything (basic navigation)
95 $dom = DOM::Tiny->new->parse(<<EOF);
99 <simple class="working">easy</simple>
100 <test foo="bar" id="test" />
103 <![CDATA[ yada yada]]>
105 <a little bit broken>
111 ok !$dom->xml, 'XML mode not detected';
112 is $dom->tag, undef, 'no tag';
113 is $dom->attr('foo'), undef, 'no attribute';
114 is $dom->attr(foo => 'bar')->attr('foo'), undef, 'no attribute';
115 is $dom->tree->[1][0], 'doctype', 'right type';
116 is $dom->tree->[1][1], ' foo', 'right doctype';
117 is "$dom", <<EOF, 'right result';
121 <simple class="working">easy</simple>
122 <test foo="bar" id="test"></test>
125 <![CDATA[ yada yada]]>
127 <a bit broken little>
133 my $simple = $dom->at('foo simple.working[class^="wor"]');
134 is $simple->parent->all_text,
135 'test easy works well yada yada < very broken more text', 'right text';
136 is $simple->tag, 'simple', 'right tag';
137 is $simple->attr('class'), 'working', 'right class attribute';
138 is $simple->text, 'easy', 'right text';
139 is $simple->parent->tag, 'foo', 'right parent tag';
140 is $simple->parent->attr->{bar}, 'ba<z', 'right parent attribute';
141 is $simple->parent->children->[1]->tag, 'test', 'right sibling';
142 is $simple->to_string, '<simple class="working">easy</simple>',
144 $simple->parent->attr(bar => 'baz')->attr({this => 'works', too => 'yea'});
145 is $simple->parent->attr('bar'), 'baz', 'right parent attribute';
146 is $simple->parent->attr('this'), 'works', 'right parent attribute';
147 is $simple->parent->attr('too'), 'yea', 'right parent attribute';
148 is $dom->at('test#test')->tag, 'test', 'right tag';
149 is $dom->at('[class$="ing"]')->tag, 'simple', 'right tag';
150 is $dom->at('[class="working"]')->tag, 'simple', 'right tag';
151 is $dom->at('[class$=ing]')->tag, 'simple', 'right tag';
152 is $dom->at('[class=working][class]')->tag, 'simple', 'right tag';
153 is $dom->at('foo > simple')->next->tag, 'test', 'right tag';
154 is $dom->at('foo > simple')->next->next->tag, 'a', 'right tag';
155 is $dom->at('foo > test')->previous->tag, 'simple', 'right tag';
156 is $dom->next, undef, 'no siblings';
157 is $dom->previous, undef, 'no siblings';
158 is $dom->at('foo > a')->next, undef, 'no next sibling';
159 is $dom->at('foo > simple')->previous, undef, 'no previous sibling';
160 is_deeply [$dom->at('simple')->ancestors->map('tag')->each], ['foo'],
162 ok !$dom->at('simple')->ancestors->first->xml, 'XML mode not active';
165 $dom = DOM::Tiny->new(
166 '<!DOCTYPE before><p>test<![CDATA[123]]><!-- 456 --></p><?after?>');
167 is $dom->at('p')->preceding_nodes->first->content, ' before', 'right content';
168 is $dom->at('p')->preceding_nodes->size, 1, 'right number of nodes';
169 is $dom->at('p')->child_nodes->last->preceding_nodes->first->content, 'test',
171 is $dom->at('p')->child_nodes->last->preceding_nodes->last->content, '123',
173 is $dom->at('p')->child_nodes->last->preceding_nodes->size, 2,
174 'right number of nodes';
175 is $dom->preceding_nodes->size, 0, 'no preceding nodes';
176 is $dom->at('p')->following_nodes->first->content, 'after', 'right content';
177 is $dom->at('p')->following_nodes->size, 1, 'right number of nodes';
178 is $dom->child_nodes->first->following_nodes->first->tag, 'p', 'right tag';
179 is $dom->child_nodes->first->following_nodes->last->content, 'after',
181 is $dom->child_nodes->first->following_nodes->size, 2, 'right number of nodes';
182 is $dom->following_nodes->size, 0, 'no following nodes';
183 is $dom->at('p')->previous_node->content, ' before', 'right content';
184 is $dom->at('p')->previous_node->previous_node, undef, 'no more siblings';
185 is $dom->at('p')->next_node->content, 'after', 'right content';
186 is $dom->at('p')->next_node->next_node, undef, 'no more siblings';
187 is $dom->at('p')->child_nodes->last->previous_node->previous_node->content,
188 'test', 'right content';
189 is $dom->at('p')->child_nodes->first->next_node->next_node->content, ' 456 ',
191 is $dom->descendant_nodes->[0]->type, 'doctype', 'right type';
192 is $dom->descendant_nodes->[0]->content, ' before', 'right content';
193 is $dom->descendant_nodes->[0], '<!DOCTYPE before>', 'right content';
194 is $dom->descendant_nodes->[1]->tag, 'p', 'right tag';
195 is $dom->descendant_nodes->[2]->type, 'text', 'right type';
196 is $dom->descendant_nodes->[2]->content, 'test', 'right content';
197 is $dom->descendant_nodes->[5]->type, 'pi', 'right type';
198 is $dom->descendant_nodes->[5]->content, 'after', 'right content';
199 is $dom->at('p')->descendant_nodes->[0]->type, 'text', 'right type';
200 is $dom->at('p')->descendant_nodes->[0]->content, 'test', 'right type';
201 is $dom->at('p')->descendant_nodes->last->type, 'comment', 'right type';
202 is $dom->at('p')->descendant_nodes->last->content, ' 456 ', 'right type';
203 is $dom->child_nodes->[1]->child_nodes->first->parent->tag, 'p', 'right tag';
204 is $dom->child_nodes->[1]->child_nodes->first->content, 'test', 'right content';
205 is $dom->child_nodes->[1]->child_nodes->first, 'test', 'right content';
206 is $dom->at('p')->child_nodes->first->type, 'text', 'right type';
207 is $dom->at('p')->child_nodes->first->remove->tag, 'p', 'right tag';
208 is $dom->at('p')->child_nodes->first->type, 'cdata', 'right type';
209 is $dom->at('p')->child_nodes->first->content, '123', 'right content';
210 is $dom->at('p')->child_nodes->[1]->type, 'comment', 'right type';
211 is $dom->at('p')->child_nodes->[1]->content, ' 456 ', 'right content';
212 is $dom->[0]->type, 'doctype', 'right type';
213 is $dom->[0]->content, ' before', 'right content';
214 is $dom->child_nodes->[2]->type, 'pi', 'right type';
215 is $dom->child_nodes->[2]->content, 'after', 'right content';
216 is $dom->child_nodes->first->content(' again')->content, ' again',
218 is $dom->child_nodes->grep(sub { $_->type eq 'pi' })->map('remove')
219 ->first->type, 'root', 'right type';
220 is "$dom", '<!DOCTYPE again><p><![CDATA[123]]><!-- 456 --></p>', 'right result';
223 $dom = DOM::Tiny->new('<script>la<la>la</script>');
224 is $dom->at('script')->type, 'tag', 'right type';
225 is $dom->at('script')->[0]->type, 'raw', 'right type';
226 is $dom->at('script')->[0]->content, 'la<la>la', 'right content';
227 is "$dom", '<script>la<la>la</script>', 'right result';
228 is $dom->at('script')->child_nodes->first->replace('a<b>c</b>1<b>d</b>')->tag,
229 'script', 'right tag';
230 is "$dom", '<script>a<b>c</b>1<b>d</b></script>', 'right result';
231 is $dom->at('b')->child_nodes->first->append('e')->content, 'c',
233 is $dom->at('b')->child_nodes->first->prepend('f')->type, 'text', 'right type';
234 is "$dom", '<script>a<b>fce</b>1<b>d</b></script>', 'right result';
235 is $dom->at('script')->child_nodes->first->following->first->tag, 'b',
237 is $dom->at('script')->child_nodes->first->next->content, 'fce',
239 is $dom->at('script')->child_nodes->first->previous, undef, 'no siblings';
240 is $dom->at('script')->child_nodes->[2]->previous->content, 'fce',
242 is $dom->at('b')->child_nodes->[1]->next, undef, 'no siblings';
243 is $dom->at('script')->child_nodes->first->wrap('<i>:)</i>')->root,
244 '<script><i>:)a</i><b>fce</b>1<b>d</b></script>', 'right result';
245 is $dom->at('i')->child_nodes->first->wrap_content('<b></b>')->root,
246 '<script><i>:)a</i><b>fce</b>1<b>d</b></script>', 'no changes';
247 is $dom->at('i')->child_nodes->first->wrap('<b></b>')->root,
248 '<script><i><b>:)</b>a</i><b>fce</b>1<b>d</b></script>', 'right result';
249 is $dom->at('b')->child_nodes->first->ancestors->map('tag')->join(','),
250 'b,i,script', 'right result';
251 is $dom->at('b')->child_nodes->first->append_content('g')->content, ':)g',
253 is $dom->at('b')->child_nodes->first->prepend_content('h')->content, 'h:)g',
255 is "$dom", '<script><i><b>h:)g</b>a</i><b>fce</b>1<b>d</b></script>',
257 is $dom->at('script > b:last-of-type')->append('<!--y-->')
258 ->following_nodes->first->content, 'y', 'right content';
259 is $dom->at('i')->prepend('z')->preceding_nodes->first->content, 'z',
261 is $dom->at('i')->following->last->text, 'd', 'right text';
262 is $dom->at('i')->following->size, 2, 'right number of following elements';
263 is $dom->at('i')->following('b:last-of-type')->first->text, 'd', 'right text';
264 is $dom->at('i')->following('b:last-of-type')->size, 1,
265 'right number of following elements';
266 is $dom->following->size, 0, 'no following elements';
267 is $dom->at('script > b:last-of-type')->preceding->first->tag, 'i', 'right tag';
268 is $dom->at('script > b:last-of-type')->preceding->size, 2,
269 'right number of preceding elements';
270 is $dom->at('script > b:last-of-type')->preceding('b')->first->tag, 'b',
272 is $dom->at('script > b:last-of-type')->preceding('b')->size, 1,
273 'right number of preceding elements';
274 is $dom->preceding->size, 0, 'no preceding elements';
275 is "$dom", '<script>z<i><b>h:)g</b>a</i><b>fce</b>1<b>d</b><!--y--></script>',
279 $dom = DOM::Tiny->new->xml(1)->parse('<b>test<image /></b>');
280 ok $dom->at('b')->child_nodes->first->xml, 'XML mode active';
281 ok $dom->at('b')->child_nodes->first->replace('<br>')->child_nodes->first->xml,
283 is "$dom", '<b><br /><image /></b>', 'right result';
285 # Treating nodes as elements
286 $dom = DOM::Tiny->new('foo<b>bar</b>baz');
287 is $dom->child_nodes->first->child_nodes->size, 0, 'no nodes';
288 is $dom->child_nodes->first->descendant_nodes->size, 0, 'no nodes';
289 is $dom->child_nodes->first->children->size, 0, 'no children';
290 is $dom->child_nodes->first->strip->parent, 'foo<b>bar</b>baz', 'no changes';
291 is $dom->child_nodes->first->at('b'), undef, 'no result';
292 is $dom->child_nodes->first->find('*')->size, 0, 'no results';
293 ok !$dom->child_nodes->first->matches('*'), 'no match';
294 is_deeply $dom->child_nodes->first->attr, {}, 'no attributes';
295 is $dom->child_nodes->first->namespace, undef, 'no namespace';
296 is $dom->child_nodes->first->tag, undef, 'no tag';
297 is $dom->child_nodes->first->text, '', 'no text';
298 is $dom->child_nodes->first->all_text, '', 'no text';
301 $dom = DOM::Tiny->new('<div id="id" class="class">a</div>');
302 is $dom->at('div#id.class')->text, 'a', 'right text';
304 # Deep nesting (parent combinator)
305 $dom = DOM::Tiny->new(<<EOF);
313 <div id="logo">Hello World</div>
323 <div id="content">More stuff</div>
328 my $p = $dom->find('body > #container > div p[id]');
329 is $p->[0]->attr('id'), 'foo', 'right id attribute';
330 is $p->[1], undef, 'no second result';
331 is $p->size, 1, 'right number of elements';
334 $dom->find('div')->each(sub { push @div, $_->attr('id') });
335 $dom->find('p')->each(sub { push @p, $_->attr('id') });
336 is_deeply \@p, [qw(foo bar)], 'found all p elements';
337 my $ids = [qw(container header logo buttons buttons content)];
338 is_deeply \@div, $ids, 'found all div elements';
339 is_deeply [$dom->at('p')->ancestors->map('tag')->each],
340 [qw(div div div body html)], 'right results';
341 is_deeply [$dom->at('html')->ancestors->each], [], 'no results';
342 is_deeply [$dom->ancestors->each], [], 'no results';
345 $dom = DOM::Tiny->new(<<EOF);
346 <script charset="utf-8">alert('lalala');</script>
348 is $dom->at('script')->text, "alert('lalala');", 'right script content';
350 # HTML5 (unquoted values)
351 $dom = DOM::Tiny->new(
352 '<div id = test foo ="bar" class=tset bar=/baz/ baz=//>works</div>');
353 is $dom->at('#test')->text, 'works', 'right text';
354 is $dom->at('div')->text, 'works', 'right text';
355 is $dom->at('[foo=bar][foo="bar"]')->text, 'works', 'right text';
356 is $dom->at('[foo="ba"]'), undef, 'no result';
357 is $dom->at('[foo=bar]')->text, 'works', 'right text';
358 is $dom->at('[foo=ba]'), undef, 'no result';
359 is $dom->at('.tset')->text, 'works', 'right text';
360 is $dom->at('[bar=/baz/]')->text, 'works', 'right text';
361 is $dom->at('[baz=//]')->text, 'works', 'right text';
363 # HTML1 (single quotes, uppercase tags and whitespace in attributes)
364 $dom = DOM::Tiny->new(q{<DIV id = 'test' foo ='bar' class= "tset">works</DIV>});
365 is $dom->at('#test')->text, 'works', 'right text';
366 is $dom->at('div')->text, 'works', 'right text';
367 is $dom->at('[foo="bar"]')->text, 'works', 'right text';
368 is $dom->at('[foo="ba"]'), undef, 'no result';
369 is $dom->at('[foo=bar]')->text, 'works', 'right text';
370 is $dom->at('[foo=ba]'), undef, 'no result';
371 is $dom->at('.tset')->text, 'works', 'right text';
373 # Already decoded Unicode snowman and quotes in selector
374 $dom = DOM::Tiny->new('<div id="snow'm"an">☃</div>');
375 is $dom->at('[id="snow\'m\"an"]')->text, '☃', 'right text';
376 is $dom->at('[id="snow\'m\22 an"]')->text, '☃', 'right text';
377 is $dom->at('[id="snow\'m\000022an"]')->text, '☃', 'right text';
378 is $dom->at('[id="snow\'m\22an"]'), undef, 'no result';
379 is $dom->at('[id="snow\'m\21 an"]'), undef, 'no result';
380 is $dom->at('[id="snow\'m\000021an"]'), undef, 'no result';
381 is $dom->at('[id="snow\'m\000021 an"]'), undef, 'no result';
382 is $dom->at("[id='snow\\'m\"an']")->text, '☃', 'right text';
383 is $dom->at("[id='snow\\27m\"an']")->text, '☃', 'right text';
385 # Unicode and escaped selectors
387 = '<html><div id="☃x">Snowman</div><div class="x ♥">Heart</div></html>';
388 $dom = DOM::Tiny->new($html);
389 is $dom->at("#\\\n\\002603x")->text, 'Snowman', 'right text';
390 is $dom->at('#\\2603 x')->text, 'Snowman', 'right text';
391 is $dom->at("#\\\n\\2603 x")->text, 'Snowman', 'right text';
392 is $dom->at(qq{[id="\\\n\\2603 x"]})->text, 'Snowman', 'right text';
393 is $dom->at(qq{[id="\\\n\\002603x"]})->text, 'Snowman', 'right text';
394 is $dom->at(qq{[id="\\\\2603 x"]})->text, 'Snowman', 'right text';
395 is $dom->at("html #\\\n\\002603x")->text, 'Snowman', 'right text';
396 is $dom->at('html #\\2603 x')->text, 'Snowman', 'right text';
397 is $dom->at("html #\\\n\\2603 x")->text, 'Snowman', 'right text';
398 is $dom->at(qq{html [id="\\\n\\2603 x"]})->text, 'Snowman', 'right text';
399 is $dom->at(qq{html [id="\\\n\\002603x"]})->text, 'Snowman', 'right text';
400 is $dom->at(qq{html [id="\\\\2603 x"]})->text, 'Snowman', 'right text';
401 is $dom->at('#☃x')->text, 'Snowman', 'right text';
402 is $dom->at('div#☃x')->text, 'Snowman', 'right text';
403 is $dom->at('html div#☃x')->text, 'Snowman', 'right text';
404 is $dom->at('[id^="☃"]')->text, 'Snowman', 'right text';
405 is $dom->at('div[id^="☃"]')->text, 'Snowman', 'right text';
406 is $dom->at('html div[id^="☃"]')->text, 'Snowman', 'right text';
407 is $dom->at('html > div[id^="☃"]')->text, 'Snowman', 'right text';
408 is $dom->at('[id^=☃]')->text, 'Snowman', 'right text';
409 is $dom->at('div[id^=☃]')->text, 'Snowman', 'right text';
410 is $dom->at('html div[id^=☃]')->text, 'Snowman', 'right text';
411 is $dom->at('html > div[id^=☃]')->text, 'Snowman', 'right text';
412 is $dom->at(".\\\n\\002665")->text, 'Heart', 'right text';
413 is $dom->at('.\\2665')->text, 'Heart', 'right text';
414 is $dom->at("html .\\\n\\002665")->text, 'Heart', 'right text';
415 is $dom->at('html .\\2665')->text, 'Heart', 'right text';
416 is $dom->at(qq{html [class\$="\\\n\\002665"]})->text, 'Heart', 'right text';
417 is $dom->at(qq{html [class\$="\\2665"]})->text, 'Heart', 'right text';
418 is $dom->at(qq{[class\$="\\\n\\002665"]})->text, 'Heart', 'right text';
419 is $dom->at(qq{[class\$="\\2665"]})->text, 'Heart', 'right text';
420 is $dom->at('.x')->text, 'Heart', 'right text';
421 is $dom->at('html .x')->text, 'Heart', 'right text';
422 is $dom->at('.♥')->text, 'Heart', 'right text';
423 is $dom->at('html .♥')->text, 'Heart', 'right text';
424 is $dom->at('div.♥')->text, 'Heart', 'right text';
425 is $dom->at('html div.♥')->text, 'Heart', 'right text';
426 is $dom->at('[class$="♥"]')->text, 'Heart', 'right text';
427 is $dom->at('div[class$="♥"]')->text, 'Heart', 'right text';
428 is $dom->at('html div[class$="♥"]')->text, 'Heart', 'right text';
429 is $dom->at('html > div[class$="♥"]')->text, 'Heart', 'right text';
430 is $dom->at('[class$=♥]')->text, 'Heart', 'right text';
431 is $dom->at('div[class$=♥]')->text, 'Heart', 'right text';
432 is $dom->at('html div[class$=♥]')->text, 'Heart', 'right text';
433 is $dom->at('html > div[class$=♥]')->text, 'Heart', 'right text';
434 is $dom->at('[class~="♥"]')->text, 'Heart', 'right text';
435 is $dom->at('div[class~="♥"]')->text, 'Heart', 'right text';
436 is $dom->at('html div[class~="♥"]')->text, 'Heart', 'right text';
437 is $dom->at('html > div[class~="♥"]')->text, 'Heart', 'right text';
438 is $dom->at('[class~=♥]')->text, 'Heart', 'right text';
439 is $dom->at('div[class~=♥]')->text, 'Heart', 'right text';
440 is $dom->at('html div[class~=♥]')->text, 'Heart', 'right text';
441 is $dom->at('html > div[class~=♥]')->text, 'Heart', 'right text';
442 is $dom->at('[class~="x"]')->text, 'Heart', 'right text';
443 is $dom->at('div[class~="x"]')->text, 'Heart', 'right text';
444 is $dom->at('html div[class~="x"]')->text, 'Heart', 'right text';
445 is $dom->at('html > div[class~="x"]')->text, 'Heart', 'right text';
446 is $dom->at('[class~=x]')->text, 'Heart', 'right text';
447 is $dom->at('div[class~=x]')->text, 'Heart', 'right text';
448 is $dom->at('html div[class~=x]')->text, 'Heart', 'right text';
449 is $dom->at('html > div[class~=x]')->text, 'Heart', 'right text';
450 is $dom->at('html'), $html, 'right result';
451 is $dom->at('#☃x')->parent, $html, 'right result';
452 is $dom->at('#☃x')->root, $html, 'right result';
453 is $dom->children('html')->first, $html, 'right result';
454 is $dom->to_string, $html, 'right result';
455 is $dom->content, $html, 'right result';
457 # Looks remotely like HTML
458 $dom = DOM::Tiny->new(
459 '<!DOCTYPE H "-/W/D HT 4/E">☃<title class=test>♥</title>☃');
460 is $dom->at('title')->text, '♥', 'right text';
461 is $dom->at('*')->text, '♥', 'right text';
462 is $dom->at('.test')->text, '♥', 'right text';
465 $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>');
466 is $dom->at('p')->replace('<foo>bar</foo>'), '<div>foo<foo>bar</foo>bar</div>',
468 is "$dom", '<div>foo<foo>bar</foo>bar</div>', 'right result';
469 $dom->at('foo')->replace(DOM::Tiny->new('text'));
470 is "$dom", '<div>footextbar</div>', 'right result';
471 $dom = DOM::Tiny->new('<div>foo</div><div>bar</div>');
472 $dom->find('div')->each(sub { shift->replace('<p>test</p>') });
473 is "$dom", '<p>test</p><p>test</p>', 'right result';
474 $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>');
475 is $dom->replace('♥'), '♥', 'right result';
476 is "$dom", '♥', 'right result';
477 $dom->replace('<div>foo<p>lalala</p>bar</div>');
478 is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result';
479 is $dom->at('p')->replace(''), '<div>foobar</div>', 'right result';
480 is "$dom", '<div>foobar</div>', 'right result';
481 is $dom->replace(''), '', 'no result';
482 is "$dom", '', 'no result';
483 $dom->replace('<div>foo<p>lalala</p>bar</div>');
484 is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result';
485 $dom->find('p')->map(replace => '');
486 is "$dom", '<div>foobar</div>', 'right result';
487 $dom = DOM::Tiny->new('<div>♥</div>');
488 $dom->at('div')->content('☃');
489 is "$dom", '<div>☃</div>', 'right result';
490 $dom = DOM::Tiny->new('<div>♥</div>');
491 $dom->at('div')->content("\x{2603}");
492 is $dom->to_string, '<div>☃</div>', 'right result';
493 is $dom->at('div')->replace('<p>♥</p>')->root, '<p>♥</p>', 'right result';
494 is $dom->to_string, '<p>♥</p>', 'right result';
495 is $dom->replace('<b>whatever</b>')->root, '<b>whatever</b>', 'right result';
496 is $dom->to_string, '<b>whatever</b>', 'right result';
497 $dom->at('b')->prepend('<p>foo</p>')->append('<p>bar</p>');
498 is "$dom", '<p>foo</p><b>whatever</b><p>bar</p>', 'right result';
499 is $dom->find('p')->map('remove')->first->root->at('b')->text, 'whatever',
501 is "$dom", '<b>whatever</b>', 'right result';
502 is $dom->at('b')->strip, 'whatever', 'right result';
503 is $dom->strip, 'whatever', 'right result';
504 is $dom->remove, '', 'right result';
505 $dom->replace('A<div>B<p>C<b>D<i><u>E</u></i>F</b>G</p><div>H</div></div>I');
506 is $dom->find(':not(div):not(i):not(u)')->map('strip')->first->root,
507 'A<div>BCD<i><u>E</u></i>FG<div>H</div></div>I', 'right result';
508 is $dom->at('i')->to_string, '<i><u>E</u></i>', 'right result';
509 $dom = DOM::Tiny->new('<div><div>A</div><div>B</div>C</div>');
510 is $dom->at('div')->at('div')->text, 'A', 'right text';
511 $dom->at('div')->find('div')->map('strip');
512 is "$dom", '<div>ABC</div>', 'right result';
514 # Replace element content
515 $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>');
516 is $dom->at('p')->content('bar'), '<p>bar</p>', 'right result';
517 is "$dom", '<div>foo<p>bar</p>bar</div>', 'right result';
518 $dom->at('p')->content(DOM::Tiny->new('text'));
519 is "$dom", '<div>foo<p>text</p>bar</div>', 'right result';
520 $dom = DOM::Tiny->new('<div>foo</div><div>bar</div>');
521 $dom->find('div')->each(sub { shift->content('<p>test</p>') });
522 is "$dom", '<div><p>test</p></div><div><p>test</p></div>', 'right result';
523 $dom->find('p')->each(sub { shift->content('') });
524 is "$dom", '<div><p></p></div><div><p></p></div>', 'right result';
525 $dom = DOM::Tiny->new('<div><p id="☃" /></div>');
526 $dom->at('#☃')->content('♥');
527 is "$dom", '<div><p id="☃">♥</p></div>', 'right result';
528 $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>');
530 is "$dom", '♥', 'right result';
531 is $dom->content('<div>foo<p>lalala</p>bar</div>'),
532 '<div>foo<p>lalala</p>bar</div>', 'right result';
533 is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result';
534 is $dom->content(''), '', 'no result';
535 is "$dom", '', 'no result';
536 $dom->content('<div>foo<p>lalala</p>bar</div>');
537 is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result';
538 is $dom->at('p')->content(''), '<p></p>', 'right result';
540 # Mixed search and tree walk
541 $dom = DOM::Tiny->new(<<EOF);
550 for my $tr ($dom->find('table tr')->each) {
551 for my $td (@{$tr->children}) {
552 push @data, $td->tag, $td->all_text;
555 is $data[0], 'td', 'right tag';
556 is $data[1], 'text1', 'right text';
557 is $data[2], 'td', 'right tag';
558 is $data[3], 'text2', 'right text';
559 is $data[4], undef, 'no tag';
562 $dom = DOM::Tiny->new(<<EOF);
563 <?xml version="1.0" encoding="UTF-8"?>
564 <rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
566 <title>Test Blog</title>
567 <link>http://blog.example.com</link>
568 <description>lalala</description>
569 <generator>DOM::Tiny</generator>
571 <pubDate>Mon, 12 Jul 2010 20:42:00</pubDate>
572 <title>Works!</title>
573 <link>http://blog.example.com/test</link>
574 <guid>http://blog.example.com/test</guid>
576 <![CDATA[<p>trololololo>]]>
578 <my:extension foo:id="works">
587 ok $dom->xml, 'XML mode detected';
588 is $dom->find('rss')->[0]->attr('version'), '2.0', 'right version';
589 is_deeply [$dom->at('title')->ancestors->map('tag')->each], [qw(channel rss)],
591 is $dom->at('extension')->attr('foo:id'), 'works', 'right id';
592 like $dom->at('#works')->text, qr/\[awesome\]\]/, 'right text';
593 like $dom->at('[id="works"]')->text, qr/\[awesome\]\]/, 'right text';
594 is $dom->find('description')->[1]->text, '<p>trololololo>', 'right text';
595 is $dom->at('pubDate')->text, 'Mon, 12 Jul 2010 20:42:00', 'right text';
596 like $dom->at('[id*="ork"]')->text, qr/\[awesome\]\]/, 'right text';
597 like $dom->at('[id*="orks"]')->text, qr/\[awesome\]\]/, 'right text';
598 like $dom->at('[id*="work"]')->text, qr/\[awesome\]\]/, 'right text';
599 like $dom->at('[id*="or"]')->text, qr/\[awesome\]\]/, 'right text';
600 ok $dom->at('rss')->xml, 'XML mode active';
601 ok $dom->at('extension')->parent->xml, 'XML mode active';
602 ok $dom->at('extension')->root->xml, 'XML mode active';
603 ok $dom->children('rss')->first->xml, 'XML mode active';
604 ok $dom->at('title')->ancestors->first->xml, 'XML mode active';
607 $dom = DOM::Tiny->new(<<EOF);
608 <?xml version="1.0"?>
609 <bk:book xmlns='uri:default-ns'
610 xmlns:bk='uri:book-ns'
611 xmlns:isbn='uri:isbn-ns'>
612 <bk:title>Programming Perl</bk:title>
613 <comment>rocks!</comment>
615 <section>Nothing</section>
617 <meta xmlns='uri:meta-ns'>
618 <isbn:number>978-0596000271</isbn:number>
622 ok $dom->xml, 'XML mode detected';
623 is $dom->namespace, undef, 'no namespace';
624 is $dom->at('book comment')->namespace, 'uri:default-ns', 'right namespace';
625 is $dom->at('book comment')->text, 'rocks!', 'right text';
626 is $dom->at('book nons section')->namespace, '', 'no namespace';
627 is $dom->at('book nons section')->text, 'Nothing', 'right text';
628 is $dom->at('book meta number')->namespace, 'uri:isbn-ns', 'right namespace';
629 is $dom->at('book meta number')->text, '978-0596000271', 'right text';
630 is $dom->children('bk\:book')->first->{xmlns}, 'uri:default-ns',
632 is $dom->children('book')->first->{xmlns}, 'uri:default-ns', 'right attribute';
633 is $dom->children('k\:book')->first, undef, 'no result';
634 is $dom->children('ook')->first, undef, 'no result';
635 is $dom->at('k\:book'), undef, 'no result';
636 is $dom->at('ook'), undef, 'no result';
637 is $dom->at('[xmlns\:bk]')->{'xmlns:bk'}, 'uri:book-ns', 'right attribute';
638 is $dom->at('[bk]')->{'xmlns:bk'}, 'uri:book-ns', 'right attribute';
639 is $dom->at('[bk]')->attr('xmlns:bk'), 'uri:book-ns', 'right attribute';
640 is $dom->at('[bk]')->attr('s:bk'), undef, 'no attribute';
641 is $dom->at('[bk]')->attr('bk'), undef, 'no attribute';
642 is $dom->at('[bk]')->attr('k'), undef, 'no attribute';
643 is $dom->at('[s\:bk]'), undef, 'no result';
644 is $dom->at('[k]'), undef, 'no result';
645 is $dom->at('number')->ancestors('meta')->first->{xmlns}, 'uri:meta-ns',
647 ok $dom->at('nons')->matches('book > nons'), 'element did match';
648 ok !$dom->at('title')->matches('book > nons > section'),
649 'element did not match';
652 $dom = DOM::Tiny->new(<<EOF);
653 <?xml version="1.0"?>
654 <foo xmlns:foo.bar="uri:first">
655 <bar xmlns:fooxbar="uri:second">
656 <foo.bar:baz>First</fooxbar:baz>
657 <fooxbar:ya.da>Second</foo.bar:ya.da>
661 is $dom->at('foo bar baz')->text, 'First', 'right text';
662 is $dom->at('baz')->namespace, 'uri:first', 'right namespace';
663 is $dom->at('foo bar ya\.da')->text, 'Second', 'right text';
664 is $dom->at('ya\.da')->namespace, 'uri:second', 'right namespace';
665 is $dom->at('foo')->namespace, undef, 'no namespace';
666 is $dom->at('[xml\.s]'), undef, 'no result';
667 is $dom->at('b\.z'), undef, 'no result';
670 $dom = DOM::Tiny->new(<<'EOF');
671 <?xml version="1.0" encoding="UTF-8"?>
672 <XRDS xmlns="xri://$xrds">
673 <XRD xmlns="xri://$xrd*($v*2.0)">
675 <Type>http://o.r.g/sso/2.0</Type>
678 <Type>http://o.r.g/sso/1.0</Type>
683 ok $dom->xml, 'XML mode detected';
684 is $dom->at('XRDS')->namespace, 'xri://$xrds', 'right namespace';
685 is $dom->at('XRD')->namespace, 'xri://$xrd*($v*2.0)', 'right namespace';
686 my $s = $dom->find('XRDS XRD Service');
687 is $s->[0]->at('Type')->text, 'http://o.r.g/sso/2.0', 'right text';
688 is $s->[0]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace';
689 is $s->[1]->at('Type')->text, 'http://o.r.g/sso/1.0', 'right text';
690 is $s->[1]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace';
691 is $s->[2], undef, 'no result';
692 is $s->size, 2, 'right number of elements';
694 # Yadis (roundtrip with namespace)
696 <?xml version="1.0" encoding="UTF-8"?>
697 <xrds:XRDS xmlns="xri://$xrd*($v*2.0)" xmlns:xrds="xri://$xrds">
700 <Type>http://o.r.g/sso/3.0</Type>
703 <Type>http://o.r.g/sso/4.0</Type>
708 <Type test="23">http://o.r.g/sso/2.0</Type>
711 <Type Test="23" test="24">http://o.r.g/sso/1.0</Type>
716 $dom = DOM::Tiny->new($yadis);
717 ok $dom->xml, 'XML mode detected';
718 is $dom->at('XRDS')->namespace, 'xri://$xrds', 'right namespace';
719 is $dom->at('XRD')->namespace, 'xri://$xrd*($v*2.0)', 'right namespace';
720 $s = $dom->find('XRDS XRD Service');
721 is $s->[0]->at('Type')->text, 'http://o.r.g/sso/3.0', 'right text';
722 is $s->[0]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace';
723 is $s->[1]->at('Type')->text, 'http://o.r.g/sso/4.0', 'right text';
724 is $s->[1]->namespace, 'xri://$xrds', 'right namespace';
725 is $s->[2]->at('Type')->text, 'http://o.r.g/sso/2.0', 'right text';
726 is $s->[2]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace';
727 is $s->[3]->at('Type')->text, 'http://o.r.g/sso/1.0', 'right text';
728 is $s->[3]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace';
729 is $s->[4], undef, 'no result';
730 is $s->size, 4, 'right number of elements';
731 is $dom->at('[Test="23"]')->text, 'http://o.r.g/sso/1.0', 'right text';
732 is $dom->at('[test="23"]')->text, 'http://o.r.g/sso/2.0', 'right text';
733 is $dom->find('xrds\:Service > Type')->[0]->text, 'http://o.r.g/sso/4.0',
735 is $dom->find('xrds\:Service > Type')->[1], undef, 'no result';
736 is $dom->find('xrds\3AService > Type')->[0]->text, 'http://o.r.g/sso/4.0',
738 is $dom->find('xrds\3AService > Type')->[1], undef, 'no result';
739 is $dom->find('xrds\3A Service > Type')->[0]->text, 'http://o.r.g/sso/4.0',
741 is $dom->find('xrds\3A Service > Type')->[1], undef, 'no result';
742 is $dom->find('xrds\00003AService > Type')->[0]->text, 'http://o.r.g/sso/4.0',
744 is $dom->find('xrds\00003AService > Type')->[1], undef, 'no result';
745 is $dom->find('xrds\00003A Service > Type')->[0]->text, 'http://o.r.g/sso/4.0',
747 is $dom->find('xrds\00003A Service > Type')->[1], undef, 'no result';
748 is "$dom", $yadis, 'successful roundtrip';
750 # Result and iterator order
751 $dom = DOM::Tiny->new('<a><b>1</b></a><b>2</b><b>3</b>');
753 $dom->find('b')->each(sub { push @numbers, pop, shift->text });
754 is_deeply \@numbers, [1, 1, 2, 2, 3, 3], 'right order';
756 # Attributes on multiple lines
757 $dom = DOM::Tiny->new("<div test=23 id='a' \n class='x' foo=bar />");
758 is $dom->at('div.x')->attr('test'), 23, 'right attribute';
759 is $dom->at('[foo="bar"]')->attr('class'), 'x', 'right attribute';
760 is $dom->at('div')->attr(baz => undef)->root->to_string,
761 '<div baz class="x" foo="bar" id="a" test="23"></div>', 'right result';
763 # Markup characters in attribute values
764 $dom = DOM::Tiny->new(qq{<div id="<a>" \n test='='>Test<div id='><' /></div>});
765 is $dom->at('div[id="<a>"]')->attr->{test}, '=', 'right attribute';
766 is $dom->at('[id="<a>"]')->text, 'Test', 'right text';
767 is $dom->at('[id="><"]')->attr->{id}, '><', 'right attribute';
770 $dom = DOM::Tiny->new(qq{<div test="" test2='' />});
771 is $dom->at('div')->attr->{test}, '', 'empty attribute value';
772 is $dom->at('div')->attr->{test2}, '', 'empty attribute value';
773 is $dom->at('[test]')->tag, 'div', 'right tag';
774 is $dom->at('[test2]')->tag, 'div', 'right tag';
775 is $dom->at('[test3]'), undef, 'no result';
776 is $dom->at('[test=""]')->tag, 'div', 'right tag';
777 is $dom->at('[test2=""]')->tag, 'div', 'right tag';
778 is $dom->at('[test3=""]'), undef, 'no result';
780 # Multi-line attribute
781 $dom = DOM::Tiny->new(qq{<div class="line1\nline2" />});
782 is $dom->at('div')->attr->{class}, "line1\nline2", 'multi-line attribute value';
783 is $dom->at('.line1')->tag, 'div', 'right tag';
784 is $dom->at('.line2')->tag, 'div', 'right tag';
785 is $dom->at('.line3'), undef, 'no result';
787 # Whitespaces before closing bracket
788 $dom = DOM::Tiny->new('<div >content</div>');
789 ok $dom->at('div'), 'tag found';
790 is $dom->at('div')->text, 'content', 'right text';
791 is $dom->at('div')->content, 'content', 'right text';
794 $dom = DOM::Tiny->new('<div class="a">A</div><div class="a-1">A1</div>');
796 $dom->find('.a')->each(sub { push @div, shift->text });
797 is_deeply \@div, ['A'], 'found first element only';
799 $dom->find('.a-1')->each(sub { push @div, shift->text });
800 is_deeply \@div, ['A1'], 'found last element only';
802 # Defined but false text
803 $dom = DOM::Tiny->new(
804 '<div><div id="a">A</div><div id="b">B</div></div><div id="0">0</div>');
806 $dom->find('div[id]')->each(sub { push @div, shift->text });
807 is_deeply \@div, [qw(A B 0)], 'found all div elements with id';
810 $dom = DOM::Tiny->new('<hr /><br/><br id="br"/><br />');
811 is "$dom", '<hr><br><br id="br"><br>', 'right result';
812 is $dom->at('br')->content, '', 'empty result';
815 $dom = DOM::Tiny->new('<a>xxx<x>x</x>xxx</a>');
816 is $dom->at('a')->content, 'xxx<x>x</x>xxx', 'right result';
817 is $dom->content, '<a>xxx<x>x</x>xxx</a>', 'right result';
820 $dom = DOM::Tiny->new(
821 '<div id="a">A</div><div id="b">B</div><div id="c">C</div><p>D</p>');
823 $dom->find('p, div')->each(sub { push @div, shift->text });
824 is_deeply \@div, [qw(A B C D)], 'found all elements';
826 $dom->find('#a, #c')->each(sub { push @div, shift->text });
827 is_deeply \@div, [qw(A C)], 'found all div elements with the right ids';
829 $dom->find('div#a, div#b')->each(sub { push @div, shift->text });
830 is_deeply \@div, [qw(A B)], 'found all div elements with the right ids';
832 $dom->find('div[id="a"], div[id="c"]')->each(sub { push @div, shift->text });
833 is_deeply \@div, [qw(A C)], 'found all div elements with the right ids';
834 $dom = DOM::Tiny->new(
835 '<div id="☃">A</div><div id="b">B</div><div id="♥x">C</div>');
837 $dom->find('#☃, #♥x')->each(sub { push @div, shift->text });
838 is_deeply \@div, [qw(A C)], 'found all div elements with the right ids';
840 $dom->find('div#☃, div#b')->each(sub { push @div, shift->text });
841 is_deeply \@div, [qw(A B)], 'found all div elements with the right ids';
843 $dom->find('div[id="☃"], div[id="♥x"]')
844 ->each(sub { push @div, shift->text });
845 is_deeply \@div, [qw(A C)], 'found all div elements with the right ids';
847 # Multiple attributes
848 $dom = DOM::Tiny->new(<<EOF);
849 <div foo="bar" bar="baz">A</div>
850 <div foo="bar">B</div>
851 <div foo="bar" bar="baz">C</div>
852 <div foo="baz" bar="baz">D</div>
855 $dom->find('div[foo="bar"][bar="baz"]')->each(sub { push @div, shift->text });
856 is_deeply \@div, [qw(A C)], 'found all div elements with the right atributes';
858 $dom->find('div[foo^="b"][foo$="r"]')->each(sub { push @div, shift->text });
859 is_deeply \@div, [qw(A B C)], 'found all div elements with the right atributes';
860 is $dom->at('[foo="bar"]')->previous, undef, 'no previous sibling';
861 is $dom->at('[foo="bar"]')->next->text, 'B', 'right text';
862 is $dom->at('[foo="bar"]')->next->previous->text, 'A', 'right text';
863 is $dom->at('[foo="bar"]')->next->next->next->next, undef, 'no next sibling';
866 $dom = DOM::Tiny->new(<<EOF);
868 <input type="text" name="user" value="test" />
869 <input type="checkbox" checked="checked" name="groovy">
871 <option value="b">b</option>
873 <option value="d">d</option>
874 <option selected="selected" value="e">E</option>
875 <option value="f">f</option>
877 <option value="g">g</option>
878 <option selected value="h">H</option>
880 <input type="submit" value="Ok!" />
881 <input type="checkbox" checked name="I">
882 <p id="content">test 123</p>
883 <p id="no_content"><? test ?><!-- 123 --></p>
886 is $dom->find(':root')->[0]->tag, 'form', 'right tag';
887 is $dom->find('*:root')->[0]->tag, 'form', 'right tag';
888 is $dom->find('form:root')->[0]->tag, 'form', 'right tag';
889 is $dom->find(':root')->[1], undef, 'no result';
890 is $dom->find(':checked')->[0]->attr->{name}, 'groovy', 'right name';
891 is $dom->find('option:checked')->[0]->attr->{value}, 'e', 'right value';
892 is $dom->find(':checked')->[1]->text, 'E', 'right text';
893 is $dom->find('*:checked')->[1]->text, 'E', 'right text';
894 is $dom->find(':checked')->[2]->text, 'H', 'right name';
895 is $dom->find(':checked')->[3]->attr->{name}, 'I', 'right name';
896 is $dom->find(':checked')->[4], undef, 'no result';
897 is $dom->find('option[selected]')->[0]->attr->{value}, 'e', 'right value';
898 is $dom->find('option[selected]')->[1]->text, 'H', 'right text';
899 is $dom->find('option[selected]')->[2], undef, 'no result';
900 is $dom->find(':checked[value="e"]')->[0]->text, 'E', 'right text';
901 is $dom->find('*:checked[value="e"]')->[0]->text, 'E', 'right text';
902 is $dom->find('option:checked[value="e"]')->[0]->text, 'E', 'right text';
903 is $dom->at('optgroup option:checked[value="e"]')->text, 'E', 'right text';
904 is $dom->at('select option:checked[value="e"]')->text, 'E', 'right text';
905 is $dom->at('select :checked[value="e"]')->text, 'E', 'right text';
906 is $dom->at('optgroup > :checked[value="e"]')->text, 'E', 'right text';
907 is $dom->at('select *:checked[value="e"]')->text, 'E', 'right text';
908 is $dom->at('optgroup > *:checked[value="e"]')->text, 'E', 'right text';
909 is $dom->find(':checked[value="e"]')->[1], undef, 'no result';
910 is $dom->find(':empty')->[0]->attr->{name}, 'user', 'right name';
911 is $dom->find('input:empty')->[0]->attr->{name}, 'user', 'right name';
912 is $dom->at(':empty[type^="ch"]')->attr->{name}, 'groovy', 'right name';
913 is $dom->at('p')->attr->{id}, 'content', 'right attribute';
914 is $dom->at('p:empty')->attr->{id}, 'no_content', 'right attribute';
916 # More pseudo-classes
917 $dom = DOM::Tiny->new(<<EOF);
930 $dom->find('li:nth-child(odd)')->each(sub { push @li, shift->text });
931 is_deeply \@li, [qw(A C E G)], 'found all odd li elements';
933 $dom->find('li:NTH-CHILD(ODD)')->each(sub { push @li, shift->text });
934 is_deeply \@li, [qw(A C E G)], 'found all odd li elements';
936 $dom->find('li:nth-last-child(odd)')->each(sub { push @li, shift->text });
937 is_deeply \@li, [qw(B D F H)], 'found all odd li elements';
938 is $dom->find(':nth-child(odd)')->[0]->tag, 'ul', 'right tag';
939 is $dom->find(':nth-child(odd)')->[1]->text, 'A', 'right text';
940 is $dom->find(':nth-child(1)')->[0]->tag, 'ul', 'right tag';
941 is $dom->find(':nth-child(1)')->[1]->text, 'A', 'right text';
942 is $dom->find(':nth-last-child(odd)')->[0]->tag, 'ul', 'right tag';
943 is $dom->find(':nth-last-child(odd)')->last->text, 'H', 'right text';
944 is $dom->find(':nth-last-child(1)')->[0]->tag, 'ul', 'right tag';
945 is $dom->find(':nth-last-child(1)')->[1]->text, 'H', 'right text';
947 $dom->find('li:nth-child(2n+1)')->each(sub { push @li, shift->text });
948 is_deeply \@li, [qw(A C E G)], 'found all odd li elements';
950 $dom->find('li:nth-child(2n + 1)')->each(sub { push @li, shift->text });
951 is_deeply \@li, [qw(A C E G)], 'found all odd li elements';
953 $dom->find('li:nth-last-child(2n+1)')->each(sub { push @li, shift->text });
954 is_deeply \@li, [qw(B D F H)], 'found all odd li elements';
956 $dom->find('li:nth-child(even)')->each(sub { push @li, shift->text });
957 is_deeply \@li, [qw(B D F H)], 'found all even li elements';
959 $dom->find('li:NTH-CHILD(EVEN)')->each(sub { push @li, shift->text });
960 is_deeply \@li, [qw(B D F H)], 'found all even li elements';
962 $dom->find('li:nth-last-child( even )')->each(sub { push @li, shift->text });
963 is_deeply \@li, [qw(A C E G)], 'found all even li elements';
965 $dom->find('li:nth-child(2n+2)')->each(sub { push @li, shift->text });
966 is_deeply \@li, [qw(B D F H)], 'found all even li elements';
968 $dom->find('li:nTh-chILd(2N+2)')->each(sub { push @li, shift->text });
969 is_deeply \@li, [qw(B D F H)], 'found all even li elements';
971 $dom->find('li:nth-child( 2n + 2 )')->each(sub { push @li, shift->text });
972 is_deeply \@li, [qw(B D F H)], 'found all even li elements';
974 $dom->find('li:nth-last-child(2n+2)')->each(sub { push @li, shift->text });
975 is_deeply \@li, [qw(A C E G)], 'found all even li elements';
977 $dom->find('li:nth-child(4n+1)')->each(sub { push @li, shift->text });
978 is_deeply \@li, [qw(A E)], 'found the right li elements';
980 $dom->find('li:nth-last-child(4n+1)')->each(sub { push @li, shift->text });
981 is_deeply \@li, [qw(D H)], 'found the right li elements';
983 $dom->find('li:nth-child(4n+4)')->each(sub { push @li, shift->text });
984 is_deeply \@li, [qw(D H)], 'found the right li elements';
986 $dom->find('li:nth-last-child(4n+4)')->each(sub { push @li, shift->text });
987 is_deeply \@li, [qw(A E)], 'found the right li elements';
989 $dom->find('li:nth-child(4n)')->each(sub { push @li, shift->text });
990 is_deeply \@li, [qw(D H)], 'found the right li elements';
992 $dom->find('li:nth-child( 4n )')->each(sub { push @li, shift->text });
993 is_deeply \@li, [qw(D H)], 'found the right li elements';
995 $dom->find('li:nth-last-child(4n)')->each(sub { push @li, shift->text });
996 is_deeply \@li, [qw(A E)], 'found the right li elements';
998 $dom->find('li:nth-child(5n-2)')->each(sub { push @li, shift->text });
999 is_deeply \@li, [qw(C H)], 'found the right li elements';
1001 $dom->find('li:nth-child( 5n - 2 )')->each(sub { push @li, shift->text });
1002 is_deeply \@li, [qw(C H)], 'found the right li elements';
1004 $dom->find('li:nth-last-child(5n-2)')->each(sub { push @li, shift->text });
1005 is_deeply \@li, [qw(A F)], 'found the right li elements';
1007 $dom->find('li:nth-child(-n+3)')->each(sub { push @li, shift->text });
1008 is_deeply \@li, [qw(A B C)], 'found first three li elements';
1010 $dom->find('li:nth-child( -n + 3 )')->each(sub { push @li, shift->text });
1011 is_deeply \@li, [qw(A B C)], 'found first three li elements';
1013 $dom->find('li:nth-last-child(-n+3)')->each(sub { push @li, shift->text });
1014 is_deeply \@li, [qw(F G H)], 'found last three li elements';
1016 $dom->find('li:nth-child(-1n+3)')->each(sub { push @li, shift->text });
1017 is_deeply \@li, [qw(A B C)], 'found first three li elements';
1019 $dom->find('li:nth-last-child(-1n+3)')->each(sub { push @li, shift->text });
1020 is_deeply \@li, [qw(F G H)], 'found first three li elements';
1022 $dom->find('li:nth-child(3n)')->each(sub { push @li, shift->text });
1023 is_deeply \@li, [qw(C F)], 'found every third li elements';
1025 $dom->find('li:nth-last-child(3n)')->each(sub { push @li, shift->text });
1026 is_deeply \@li, [qw(C F)], 'found every third li elements';
1028 $dom->find('li:NTH-LAST-CHILD(3N)')->each(sub { push @li, shift->text });
1029 is_deeply \@li, [qw(C F)], 'found every third li elements';
1031 $dom->find('li:Nth-Last-Child(3N)')->each(sub { push @li, shift->text });
1032 is_deeply \@li, [qw(C F)], 'found every third li elements';
1034 $dom->find('li:nth-child( 3 )')->each(sub { push @li, shift->text });
1035 is_deeply \@li, ['C'], 'found third li element';
1037 $dom->find('li:nth-last-child( +3 )')->each(sub { push @li, shift->text });
1038 is_deeply \@li, ['F'], 'found third last li element';
1040 $dom->find('li:nth-child(1n+0)')->each(sub { push @li, shift->text });
1041 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1043 $dom->find('li:nth-child(1n-0)')->each(sub { push @li, shift->text });
1044 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1046 $dom->find('li:nth-child(n+0)')->each(sub { push @li, shift->text });
1047 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1049 $dom->find('li:nth-child(n)')->each(sub { push @li, shift->text });
1050 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1052 $dom->find('li:nth-child(n+0)')->each(sub { push @li, shift->text });
1053 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1055 $dom->find('li:NTH-CHILD(N+0)')->each(sub { push @li, shift->text });
1056 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1058 $dom->find('li:Nth-Child(N+0)')->each(sub { push @li, shift->text });
1059 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1061 $dom->find('li:nth-child(n)')->each(sub { push @li, shift->text });
1062 is_deeply \@li, [qw(A B C D E F G)], 'found all li elements';
1064 $dom->find('li:nth-child(0n+1)')->each(sub { push @li, shift->text });
1065 is_deeply \@li, [qw(A)], 'found first li element';
1066 is $dom->find('li:nth-child(0n+0)')->size, 0, 'no results';
1067 is $dom->find('li:nth-child(0)')->size, 0, 'no results';
1068 is $dom->find('li:nth-child()')->size, 0, 'no results';
1069 is $dom->find('li:nth-child(whatever)')->size, 0, 'no results';
1070 is $dom->find('li:whatever(whatever)')->size, 0, 'no results';
1072 # Even more pseudo-classes
1073 $dom = DOM::Tiny->new(<<EOF);
1077 <li class="test ♥">C</li>
1086 <div class="☃">J</div>
1089 <a href="http://www.w3.org/DOM/">DOM</a>
1090 <div class="☃">K</div>
1091 <a href="http://www.w3.org/DOM/">DOM</a>
1095 $dom->find('ul :nth-child(odd)')->each(sub { push @e, shift->text });
1096 is_deeply \@e, [qw(A C E G I)], 'found all odd elements';
1098 $dom->find('li:nth-of-type(odd)')->each(sub { push @e, shift->text });
1099 is_deeply \@e, [qw(A E H)], 'found all odd li elements';
1101 $dom->find('li:nth-last-of-type( odd )')->each(sub { push @e, shift->text });
1102 is_deeply \@e, [qw(C F I)], 'found all odd li elements';
1104 $dom->find('p:nth-of-type(odd)')->each(sub { push @e, shift->text });
1105 is_deeply \@e, [qw(B G)], 'found all odd p elements';
1107 $dom->find('p:nth-last-of-type(odd)')->each(sub { push @e, shift->text });
1108 is_deeply \@e, [qw(B G)], 'found all odd li elements';
1110 $dom->find('ul :nth-child(1)')->each(sub { push @e, shift->text });
1111 is_deeply \@e, ['A'], 'found first child';
1113 $dom->find('ul :first-child')->each(sub { push @e, shift->text });
1114 is_deeply \@e, ['A'], 'found first child';
1116 $dom->find('p:nth-of-type(1)')->each(sub { push @e, shift->text });
1117 is_deeply \@e, ['B'], 'found first child';
1119 $dom->find('p:first-of-type')->each(sub { push @e, shift->text });
1120 is_deeply \@e, ['B'], 'found first child';
1122 $dom->find('li:nth-of-type(1)')->each(sub { push @e, shift->text });
1123 is_deeply \@e, ['A'], 'found first child';
1125 $dom->find('li:first-of-type')->each(sub { push @e, shift->text });
1126 is_deeply \@e, ['A'], 'found first child';
1128 $dom->find('ul :nth-last-child(-n+1)')->each(sub { push @e, shift->text });
1129 is_deeply \@e, ['I'], 'found last child';
1131 $dom->find('ul :last-child')->each(sub { push @e, shift->text });
1132 is_deeply \@e, ['I'], 'found last child';
1134 $dom->find('p:nth-last-of-type(-n+1)')->each(sub { push @e, shift->text });
1135 is_deeply \@e, ['G'], 'found last child';
1137 $dom->find('p:last-of-type')->each(sub { push @e, shift->text });
1138 is_deeply \@e, ['G'], 'found last child';
1140 $dom->find('li:nth-last-of-type(-n+1)')->each(sub { push @e, shift->text });
1141 is_deeply \@e, ['I'], 'found last child';
1143 $dom->find('li:last-of-type')->each(sub { push @e, shift->text });
1144 is_deeply \@e, ['I'], 'found last child';
1146 $dom->find('ul :nth-child(-n+3):not(li)')->each(sub { push @e, shift->text });
1147 is_deeply \@e, ['B'], 'found first p element';
1149 $dom->find('ul :nth-child(-n+3):NOT(li)')->each(sub { push @e, shift->text });
1150 is_deeply \@e, ['B'], 'found first p element';
1152 $dom->find('ul :nth-child(-n+3):not(:first-child)')
1153 ->each(sub { push @e, shift->text });
1154 is_deeply \@e, [qw(B C)], 'found second and third element';
1156 $dom->find('ul :nth-child(-n+3):not(.♥)')->each(sub { push @e, shift->text });
1157 is_deeply \@e, [qw(A B)], 'found first and second element';
1159 $dom->find('ul :nth-child(-n+3):not([class$="♥"])')
1160 ->each(sub { push @e, shift->text });
1161 is_deeply \@e, [qw(A B)], 'found first and second element';
1163 $dom->find('ul :nth-child(-n+3):not(li[class$="♥"])')
1164 ->each(sub { push @e, shift->text });
1165 is_deeply \@e, [qw(A B)], 'found first and second element';
1167 $dom->find('ul :nth-child(-n+3):not([class$="♥"][class^="test"])')
1168 ->each(sub { push @e, shift->text });
1169 is_deeply \@e, [qw(A B)], 'found first and second element';
1171 $dom->find('ul :nth-child(-n+3):not(*[class$="♥"])')
1172 ->each(sub { push @e, shift->text });
1173 is_deeply \@e, [qw(A B)], 'found first and second element';
1175 $dom->find('ul :nth-child(-n+3):not(:nth-child(-n+2))')
1176 ->each(sub { push @e, shift->text });
1177 is_deeply \@e, ['C'], 'found third element';
1179 $dom->find('ul :nth-child(-n+3):not(:nth-child(1)):not(:nth-child(2))')
1180 ->each(sub { push @e, shift->text });
1181 is_deeply \@e, ['C'], 'found third element';
1183 $dom->find(':only-child')->each(sub { push @e, shift->text });
1184 is_deeply \@e, ['J'], 'found only child';
1186 $dom->find('div :only-of-type')->each(sub { push @e, shift->text });
1187 is_deeply \@e, [qw(J K)], 'found only child';
1189 $dom->find('div:only-child')->each(sub { push @e, shift->text });
1190 is_deeply \@e, ['J'], 'found only child';
1192 $dom->find('div div:only-of-type')->each(sub { push @e, shift->text });
1193 is_deeply \@e, [qw(J K)], 'found only child';
1195 # Sibling combinator
1196 $dom = DOM::Tiny->new(<<EOF);
1204 <p id="☃">F<b>H</b></p>
1207 is $dom->at('li ~ p')->text, 'B', 'right text';
1208 is $dom->at('li + p')->text, 'B', 'right text';
1209 is $dom->at('h1 ~ p ~ p')->text, 'F', 'right text';
1210 is $dom->at('h1 + p ~ p')->text, 'F', 'right text';
1211 is $dom->at('h1 ~ p + p')->text, 'F', 'right text';
1212 is $dom->at('h1 + p + p')->text, 'F', 'right text';
1213 is $dom->at('h1 + p+p')->text, 'F', 'right text';
1214 is $dom->at('ul > li ~ li')->text, 'C', 'right text';
1215 is $dom->at('ul li ~ li')->text, 'C', 'right text';
1216 is $dom->at('ul>li~li')->text, 'C', 'right text';
1217 is $dom->at('ul li li'), undef, 'no result';
1218 is $dom->at('ul ~ li ~ li'), undef, 'no result';
1219 is $dom->at('ul + li ~ li'), undef, 'no result';
1220 is $dom->at('ul > li + li'), undef, 'no result';
1221 is $dom->at('h1 ~ div')->text, 'G', 'right text';
1222 is $dom->at('h1 + div'), undef, 'no result';
1223 is $dom->at('p + div')->text, 'G', 'right text';
1224 is $dom->at('ul + h1 + p + p + div')->text, 'G', 'right text';
1225 is $dom->at('ul + h1 ~ p + div')->text, 'G', 'right text';
1226 is $dom->at('h1 ~ #♥')->text, 'E', 'right text';
1227 is $dom->at('h1 + #♥')->text, 'E', 'right text';
1228 is $dom->at('#♥~#☃')->text, 'F', 'right text';
1229 is $dom->at('#♥+#☃')->text, 'F', 'right text';
1230 is $dom->at('#♥+#☃>b')->text, 'H', 'right text';
1231 is $dom->at('#♥ > #☃'), undef, 'no result';
1232 is $dom->at('#♥ #☃'), undef, 'no result';
1233 is $dom->at('#♥ + #☃ + :nth-last-child(1)')->text, 'G', 'right text';
1234 is $dom->at('#♥ ~ #☃ + :nth-last-child(1)')->text, 'G', 'right text';
1235 is $dom->at('#♥ + #☃ ~ :nth-last-child(1)')->text, 'G', 'right text';
1236 is $dom->at('#♥ ~ #☃ ~ :nth-last-child(1)')->text, 'G', 'right text';
1237 is $dom->at('#♥ + :nth-last-child(2)')->text, 'F', 'right text';
1238 is $dom->at('#♥ ~ :nth-last-child(2)')->text, 'F', 'right text';
1239 is $dom->at('#♥ + #☃ + *:nth-last-child(1)')->text, 'G', 'right text';
1240 is $dom->at('#♥ ~ #☃ + *:nth-last-child(1)')->text, 'G', 'right text';
1241 is $dom->at('#♥ + #☃ ~ *:nth-last-child(1)')->text, 'G', 'right text';
1242 is $dom->at('#♥ ~ #☃ ~ *:nth-last-child(1)')->text, 'G', 'right text';
1243 is $dom->at('#♥ + *:nth-last-child(2)')->text, 'F', 'right text';
1244 is $dom->at('#♥ ~ *:nth-last-child(2)')->text, 'F', 'right text';
1247 $dom = DOM::Tiny->new(<<EOF);
1255 $dom->at('li')->append('<p>A1</p>23');
1256 is "$dom", <<EOF, 'right result';
1258 <li>A</li><p>A1</p>23
1264 $dom->at('li')->prepend('24')->prepend('<div>A-1</div>25');
1265 is "$dom", <<EOF, 'right result';
1267 24<div>A-1</div>25<li>A</li><p>A1</p>23
1273 is $dom->at('div')->text, 'A-1', 'right text';
1274 is $dom->at('iv'), undef, 'no result';
1275 is $dom->prepend('l')->prepend('alal')->prepend('a')->type, 'root',
1277 is "$dom", <<EOF, 'no changes';
1279 24<div>A-1</div>25<li>A</li><p>A1</p>23
1285 is $dom->append('lalala')->type, 'root', 'right type';
1286 is "$dom", <<EOF, 'no changes';
1288 24<div>A-1</div>25<li>A</li><p>A1</p>23
1294 $dom->find('div')->each(sub { shift->append('works') });
1295 is "$dom", <<EOF, 'right result';
1297 24<div>A-1</div>works25<li>A</li><p>A1</p>23
1303 $dom->at('li')->prepend_content('A3<p>A2</p>')->prepend_content('A4');
1304 is $dom->at('li')->text, 'A4A3 A', 'right text';
1305 is "$dom", <<EOF, 'right result';
1307 24<div>A-1</div>works25<li>A4A3<p>A2</p>A</li><p>A1</p>23
1313 $dom->find('li')->[1]->append_content('<p>C2</p>C3')->append_content(' C4')
1314 ->append_content('C5');
1315 is $dom->find('li')->[1]->text, 'C C3 C4C5', 'right text';
1316 is "$dom", <<EOF, 'right result';
1318 24<div>A-1</div>works25<li>A4A3<p>A2</p>A</li><p>A1</p>23
1320 <li>C<p>C2</p>C3 C4C5</li>
1325 # Optional "head" and "body" tags
1326 $dom = DOM::Tiny->new(<<EOF);
1332 is $dom->at('html > head > title')->text, 'foo', 'right text';
1333 is $dom->at('html > body')->text, 'bar', 'right text';
1336 $dom = DOM::Tiny->new(<<EOF);
1350 is $dom->find('ul > li > ol > li')->[0]->text, 'F', 'right text';
1351 is $dom->find('ul > li > ol > li')->[1]->text, 'G', 'right text';
1352 is $dom->find('ul > li')->[1]->text, 'A', 'right text';
1353 is $dom->find('ul > li')->[2]->text, 'B', 'right text';
1354 is $dom->find('ul > li')->[3]->text, 'C', 'right text';
1355 is $dom->find('ul > li')->[4]->text, 'D', 'right text';
1356 is $dom->find('ul > li')->[5]->text, 'E', 'right text';
1359 $dom = DOM::Tiny->new(<<EOF);
1365 <p>E<img src="foo.png">
1370 is $dom->find('div > p')->[0]->text, 'A', 'right text';
1371 is $dom->find('div > p')->[1]->text, 'B', 'right text';
1372 is $dom->find('div > p')->[2]->text, 'C', 'right text';
1373 is $dom->find('div > p')->[3]->text, 'D', 'right text';
1374 is $dom->find('div > p')->[4]->text, 'E', 'right text';
1375 is $dom->find('div > p')->[5]->text, 'F G', 'right text';
1376 is $dom->find('div > p')->[6]->text, 'H', 'right text';
1377 is $dom->find('div > p > p')->[0], undef, 'no results';
1378 is $dom->at('div > p > img')->attr->{src}, 'foo.png', 'right attribute';
1379 is $dom->at('div > div')->text, 'X', 'right text';
1381 # Optional "dt" and "dd" tags
1382 $dom = DOM::Tiny->new(<<EOF);
1392 is $dom->find('dl > dt')->[0]->text, 'A', 'right text';
1393 is $dom->find('dl > dd')->[0]->text, 'B', 'right text';
1394 is $dom->find('dl > dt')->[1]->text, 'C', 'right text';
1395 is $dom->find('dl > dd')->[1]->text, 'D', 'right text';
1396 is $dom->find('dl > dt')->[2]->text, 'E', 'right text';
1397 is $dom->find('dl > dd')->[2]->text, 'F', 'right text';
1399 # Optional "rp" and "rt" tags
1400 $dom = DOM::Tiny->new(<<EOF);
1410 is $dom->find('ruby > rp')->[0]->text, 'A', 'right text';
1411 is $dom->find('ruby > rt')->[0]->text, 'B', 'right text';
1412 is $dom->find('ruby > rp')->[1]->text, 'C', 'right text';
1413 is $dom->find('ruby > rt')->[1]->text, 'D', 'right text';
1414 is $dom->find('ruby > rp')->[2]->text, 'E', 'right text';
1415 is $dom->find('ruby > rt')->[2]->text, 'F', 'right text';
1417 # Optional "optgroup" and "option" tags
1418 $dom = DOM::Tiny->new(<<EOF);
1430 is $dom->find('div > optgroup')->[0]->text, 'A', 'right text';
1431 is $dom->find('div > optgroup > #foo')->[0]->text, 'B', 'right text';
1432 is $dom->find('div > optgroup > option')->[1]->text, 'C', 'right text';
1433 is $dom->find('div > optgroup > option')->[2]->text, 'D', 'right text';
1434 is $dom->find('div > optgroup')->[1]->text, 'E', 'right text';
1435 is $dom->find('div > optgroup > option')->[3]->text, 'F', 'right text';
1436 is $dom->find('div > optgroup')->[2]->text, 'G', 'right text';
1437 is $dom->find('div > optgroup > option')->[4]->text, 'H', 'right text';
1439 # Optional "colgroup" tag
1440 $dom = DOM::Tiny->new(<<EOF);
1451 is $dom->find('table > col')->[0]->attr->{id}, 'morefail', 'right attribute';
1452 is $dom->find('table > col')->[1]->attr->{id}, 'fail', 'right attribute';
1453 is $dom->find('table > colgroup > col')->[0]->attr->{id}, 'foo',
1455 is $dom->find('table > colgroup > col')->[1]->attr->{class}, 'foo',
1457 is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar',
1460 # Optional "thead", "tbody", "tfoot", "tr", "th" and "td" tags
1461 $dom = DOM::Tiny->new(<<EOF);
1475 is $dom->at('table > thead > tr > th')->text, 'A', 'right text';
1476 is $dom->find('table > thead > tr > th')->[1]->text, 'D', 'right text';
1477 is $dom->at('table > tbody > tr > td')->text, 'B', 'right text';
1478 is $dom->at('table > tfoot > tr > td')->text, 'C', 'right text';
1480 # Optional "colgroup", "thead", "tbody", "tr", "th" and "td" tags
1481 $dom = DOM::Tiny->new(<<EOF);
1503 is $dom->find('table > col')->[0]->attr->{id}, 'morefail', 'right attribute';
1504 is $dom->find('table > col')->[1]->attr->{id}, 'fail', 'right attribute';
1505 is $dom->find('table > colgroup > col')->[0]->attr->{id}, 'foo',
1507 is $dom->find('table > colgroup > col')->[1]->attr->{class}, 'foo',
1509 is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar',
1511 is $dom->at('table > thead > tr > th')->text, 'A', 'right text';
1512 is $dom->find('table > thead > tr > th')->[1]->text, 'D', 'right text';
1513 is $dom->at('table > tbody > tr > td')->text, 'B', 'right text';
1514 is $dom->find('table > tbody > tr > td')->map('text')->join("\n"), "B\nE",
1517 # Optional "colgroup", "tbody", "tr", "th" and "td" tags
1518 $dom = DOM::Tiny->new(<<EOF);
1531 is $dom->find('table > colgroup > col')->[0]->attr->{id}, 'foo',
1533 is $dom->find('table > colgroup > col')->[1]->attr->{class}, 'foo',
1535 is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar',
1537 is $dom->at('table > tbody > tr > td')->text, 'B', 'right text';
1539 # Optional "tr" and "td" tags
1540 $dom = DOM::Tiny->new(<<EOF);
1552 is $dom->find('table > tr > td')->[0]->text, 'A', 'right text';
1553 is $dom->find('table > tr > td')->[1]->text, 'B', 'right text';
1554 is $dom->find('table > tr > td')->[2]->text, 'C', 'right text';
1555 is $dom->find('table > tr > td')->[3]->text, 'D', 'right text';
1558 $dom = DOM::Tiny->new(<<EOF);
1561 <title>Real World!</title>
1564 <table class=RealWorld>
1569 <th class=three>Three
1573 <td class=alpha>Alpha
1575 <td class=gamma><a href="#gamma">Gamma</a>
1576 <td class=delta>Delta
1578 <td class=alpha>Alpha Two
1579 <td class=beta>Beta Two
1580 <td class=gamma><a href="#gamma-two">Gamma Two</a>
1581 <td class=delta>Delta Two
1584 is $dom->find('html > head > title')->[0]->text, 'Real World!', 'right text';
1585 is $dom->find('html > body > p')->[0]->text, 'Just a test', 'right text';
1586 is $dom->find('p')->[0]->text, 'Just a test', 'right text';
1587 is $dom->find('thead > tr > .three')->[0]->text, 'Three', 'right text';
1588 is $dom->find('thead > tr > .four')->[0]->text, 'Four', 'right text';
1589 is $dom->find('tbody > tr > .beta')->[0]->text, 'Beta', 'right text';
1590 is $dom->find('tbody > tr > .gamma')->[0]->text, '', 'no text';
1591 is $dom->find('tbody > tr > .gamma > a')->[0]->text, 'Gamma', 'right text';
1592 is $dom->find('tbody > tr > .alpha')->[1]->text, 'Alpha Two', 'right text';
1593 is $dom->find('tbody > tr > .gamma > a')->[1]->text, 'Gamma Two', 'right text';
1595 = $dom->find('tr > td:nth-child(1)')->map(following => ':nth-child(even)')
1596 ->flatten->map('all_text')->each;
1597 is_deeply \@following, ['Beta', 'Delta', 'Beta Two', 'Delta Two'],
1601 $dom = DOM::Tiny->new(<<EOF);
1604 <title>Real World!</title>
1626 is $dom->find('html > head > title')->[0]->text, 'Real World!', 'right text';
1627 is $dom->find('body > ul > li')->[0]->text, 'Test 123', 'right text';
1628 is $dom->find('body > ul > li > p')->[0]->text, '', 'no text';
1629 is $dom->find('body > ul > li')->[1]->text, 'Test 321', 'right text';
1630 is $dom->find('body > ul > li > p')->[1]->text, '', 'no text';
1631 is $dom->find('body > ul > li')->[1]->all_text, 'Test 321', 'right text';
1632 is $dom->find('body > ul > li > p')->[1]->all_text, '', 'no text';
1633 is $dom->find('body > ul > li')->[2]->text, 'Test 3 2 1', 'right text';
1634 is $dom->find('body > ul > li > p')->[2]->text, '', 'no text';
1635 is $dom->find('body > ul > li')->[2]->all_text, 'Test 3 2 1', 'right text';
1636 is $dom->find('body > ul > li > p')->[2]->all_text, '', 'no text';
1638 # Advanced whitespace trimming (punctuation)
1639 $dom = DOM::Tiny->new(<<EOF);
1642 <title>Real World!</title>
1644 <div>foo <strong>bar</strong>.</div>
1645 <div>foo<strong>, bar</strong>baz<strong>; yada</strong>.</div>
1646 <div>foo<strong>: bar</strong>baz<strong>? yada</strong>!</div>
1648 is $dom->find('html > head > title')->[0]->text, 'Real World!', 'right text';
1649 is $dom->find('body > div')->[0]->all_text, 'foo bar.', 'right text';
1650 is $dom->find('body > div')->[1]->all_text, 'foo, bar baz; yada.', 'right text';
1651 is $dom->find('body > div')->[1]->text, 'foo baz.', 'right text';
1652 is $dom->find('body > div')->[2]->all_text, 'foo: bar baz? yada!', 'right text';
1653 is $dom->find('body > div')->[2]->text, 'foo baz!', 'right text';
1655 # Real world JavaScript and CSS
1656 $dom = DOM::Tiny->new(<<EOF);
1659 <style test=works>#style { foo: style('<test>'); }</style>
1665 < sCriPt two="23" >if (b > c) { alert('&<ohoh>') }< / scRiPt >
1668 is $dom->find('html > body')->[0]->text, 'Foo!', 'right text';
1669 is $dom->find('html > head > style')->[0]->text,
1670 "#style { foo: style('<test>'); }", 'right text';
1671 is $dom->find('html > head > script')->[0]->text,
1672 "\n if (a < b) {\n alert('<123>');\n }\n ", 'right text';
1673 is $dom->find('html > head > script')->[1]->text,
1674 "if (b > c) { alert('&<ohoh>') }", 'right text';
1676 # More real world JavaScript
1677 $dom = DOM::Tiny->new(<<EOF);
1682 <script src="/js/one.js"></script>
1683 <script src="/js/two.js"></script>
1684 <script src="/js/three.js"></script>
1689 is $dom->at('title')->text, 'Foo', 'right text';
1690 is $dom->find('html > head > script')->[0]->attr('src'), '/js/one.js',
1692 is $dom->find('html > head > script')->[1]->attr('src'), '/js/two.js',
1694 is $dom->find('html > head > script')->[2]->attr('src'), '/js/three.js',
1696 is $dom->find('html > head > script')->[2]->text, '', 'no text';
1697 is $dom->at('html > body')->text, 'Bar', 'right text';
1699 # Even more real world JavaScript
1700 $dom = DOM::Tiny->new(<<EOF);
1705 <script src="/js/one.js"></script>
1706 <script src="/js/two.js"></script>
1707 <script src="/js/three.js">
1712 is $dom->at('title')->text, 'Foo', 'right text';
1713 is $dom->find('html > head > script')->[0]->attr('src'), '/js/one.js',
1715 is $dom->find('html > head > script')->[1]->attr('src'), '/js/two.js',
1717 is $dom->find('html > head > script')->[2]->attr('src'), '/js/three.js',
1719 is $dom->find('html > head > script')->[2]->text, '', 'no text';
1720 is $dom->at('html > body')->text, 'Bar', 'right text';
1723 $dom = DOM::Tiny->new(<<EOF);
1724 <?xml version="1.0"?>
1725 <!-- This is a Test! -->
1727 <!ELEMENT root (#PCDATA)>
1728 <!ATTLIST root att CDATA #REQUIRED>
1731 <![CDATA[<hello>world</hello>]]>
1734 ok $dom->xml, 'XML mode detected';
1735 is $dom->at('root')->attr('att'), 'test', 'right attribute';
1736 is $dom->tree->[5][1], ' root [
1737 <!ELEMENT root (#PCDATA)>
1738 <!ATTLIST root att CDATA #REQUIRED>
1739 ]', 'right doctype';
1740 is $dom->at('root')->text, '<hello>world</hello>', 'right text';
1741 $dom = DOM::Tiny->new(<<EOF);
1745 <!ENTITY test "yeah">
1749 is $dom->tree->[1][1], ' book
1752 <!ENTITY test "yeah">
1753 ]', 'right doctype';
1754 ok !$dom->xml, 'XML mode not detected';
1755 is $dom->at('foo'), '<foo></foo>', 'right element';
1756 $dom = DOM::Tiny->new(<<EOF);
1757 <?xml version="1.0" encoding = 'utf-8'?>
1760 <!ATTLIST foo xml:lang CDATA #IMPLIED>
1761 <!ENTITY % e SYSTEM "myentities.ent">
1764 <foo xml:lang="de">Check!</fOo>
1766 ok $dom->xml, 'XML mode detected';
1767 is $dom->tree->[3][1], ' foo [
1769 <!ATTLIST foo xml:lang CDATA #IMPLIED>
1770 <!ENTITY % e SYSTEM "myentities.ent">
1772 ] ', 'right doctype';
1773 is $dom->at('foo')->attr->{'xml:lang'}, 'de', 'right attribute';
1774 is $dom->at('foo')->text, 'Check!', 'right text';
1775 $dom = DOM::Tiny->new(<<EOF);
1776 <!DOCTYPE TESTSUITE PUBLIC "my.dtd" 'mhhh' [
1778 <!ATTLIST foo bar ENTITY 'true'>
1779 <!ENTITY system_entities SYSTEM 'systems.xml'>
1780 <!ENTITY leertaste ' '>
1781 <!-- This is a comment -->
1782 <!NOTATION hmmm SYSTEM "hmmm">
1784 <?check for-nothing?>
1785 <foo bar='false'>&leertaste;!!!</foo>
1787 is $dom->tree->[1][1], ' TESTSUITE PUBLIC "my.dtd" \'mhhh\' [
1789 <!ATTLIST foo bar ENTITY \'true\'>
1790 <!ENTITY system_entities SYSTEM \'systems.xml\'>
1791 <!ENTITY leertaste \' \'>
1792 <!-- This is a comment -->
1793 <!NOTATION hmmm SYSTEM "hmmm">
1794 ] ', 'right doctype';
1795 is $dom->at('foo')->attr('bar'), 'false', 'right attribute';
1797 # Broken "font" block and useless end tags
1798 $dom = DOM::Tiny->new(<<EOF);
1800 <head><title>Test</title></head>
1803 <tr><td><font>test</td></font></tr>
1809 is $dom->at('html > head > title')->text, 'Test', 'right text';
1810 is $dom->at('html body table tr td > font')->text, 'test', 'right text';
1812 # Different broken "font" block
1813 $dom = DOM::Tiny->new(<<EOF);
1815 <head><title>Test</title></head>
1820 <td>test1<br></td></font>
1826 is $dom->at('html > head > title')->text, 'Test', 'right text';
1827 is $dom->find('html > body > font > table > tr > td')->[0]->text, 'test1',
1829 is $dom->find('html > body > font > table > tr > td')->[1]->text, 'test2',
1832 # Broken "font" and "div" blocks
1833 $dom = DOM::Tiny->new(<<EOF);
1835 <head><title>Test</title></head>
1839 <div>test2<br></font>
1844 is $dom->at('html head title')->text, 'Test', 'right text';
1845 is $dom->at('html body font > div')->text, 'test1', 'right text';
1846 is $dom->at('html body font > div > div')->text, 'test2', 'right text';
1848 # Broken "div" blocks
1849 $dom = DOM::Tiny->new(<<EOF);
1851 <head><title>Test</title></head>
1855 <tr><td><div>test</td></div></tr>
1861 is $dom->at('html head title')->text, 'Test', 'right text';
1862 is $dom->at('html body div table tr td > div')->text, 'test', 'right text';
1864 # And another broken "font" block
1865 $dom = DOM::Tiny->new(<<EOF);
1867 <head><title>Test</title></head>
1871 <td><font><br>te<br>st<br>1</td></font>
1872 <td>x1<td><img>tes<br>t2</td>
1873 <td>x2<td><font>t<br>est3</font></td>
1879 is $dom->at('html > head > title')->text, 'Test', 'right text';
1880 is $dom->find('html body table tr > td > font')->[0]->text, 'te st 1',
1882 is $dom->find('html body table tr > td')->[1]->text, 'x1', 'right text';
1883 is $dom->find('html body table tr > td')->[2]->text, 'tes t2', 'right text';
1884 is $dom->find('html body table tr > td')->[3]->text, 'x2', 'right text';
1885 is $dom->find('html body table tr > td')->[5], undef, 'no result';
1886 is $dom->find('html body table tr > td')->size, 5, 'right number of elements';
1887 is $dom->find('html body table tr > td > font')->[1]->text, 't est3',
1889 is $dom->find('html body table tr > td > font')->[2], undef, 'no result';
1890 is $dom->find('html body table tr > td > font')->size, 2,
1891 'right number of elements';
1892 is $dom, <<EOF, 'right result';
1894 <head><title>Test</title></head>
1898 <td><font><br>te<br>st<br>1</font></td>
1899 <td>x1</td><td><img>tes<br>t2</td>
1900 <td>x2</td><td><font>t<br>est3</font></td>
1907 # A collection of wonderful screwups
1908 $dom = DOM::Tiny->new(<<'EOF');
1911 <head><title>Wonderful Screwups</title></head>
1912 <body id="screw-up">
1915 <a href="/test" target='_blank'><img src="/test.png"></a>
1916 <a href='/real bad' screwup: http://localhost/bad' target='_blank'>
1917 <img src="/test2.png">
1921 <b>>la<>la<<>>la<</b>
1925 is $dom->at('#screw-up > b')->text, '>la<>la<<>>la<', 'right text';
1926 is $dom->at('#screw-up .ewww > a > img')->attr('src'), '/test.png',
1928 is $dom->find('#screw-up .ewww > a > img')->[1]->attr('src'), '/test2.png',
1930 is $dom->find('#screw-up .ewww > a > img')->[2], undef, 'no result';
1931 is $dom->find('#screw-up .ewww > a > img')->size, 2, 'right number of elements';
1934 $dom = DOM::Tiny->new('<br< abc abc abc abc abc abc abc abc<p>Test</p>');
1935 is $dom->at('p')->text, 'Test', 'right text';
1937 # Modifying an XML document
1938 $dom = DOM::Tiny->new(<<'EOF');
1939 <?xml version='1.0' encoding='UTF-8'?>
1942 ok $dom->xml, 'XML mode detected';
1943 $dom->at('XMLTest')->content('<Element />');
1944 my $element = $dom->at('Element');
1945 is $element->tag, 'Element', 'right tag';
1946 ok $element->xml, 'XML mode active';
1947 $element = $dom->at('XMLTest')->children->[0];
1948 is $element->tag, 'Element', 'right child';
1949 is $element->parent->tag, 'XMLTest', 'right parent';
1950 ok $element->root->xml, 'XML mode active';
1951 $dom->replace('<XMLTest2 /><XMLTest3 just="works" />');
1952 ok $dom->xml, 'XML mode active';
1953 $dom->at('XMLTest2')->{foo} = undef;
1954 is $dom, '<XMLTest2 foo="foo" /><XMLTest3 just="works" />', 'right result';
1956 # Ensure HTML semantics
1957 ok !DOM::Tiny->new->xml(undef)->parse('<?xml version="1.0"?>')->xml,
1958 'XML mode not detected';
1960 = DOM::Tiny->new->xml(0)->parse('<?xml version="1.0"?><br><div>Test</div>');
1961 is $dom->at('div:root')->text, 'Test', 'right text';
1963 # Ensure XML semantics
1964 ok !!DOM::Tiny->new->xml(1)->parse('<foo />')->xml, 'XML mode active';
1965 $dom = DOM::Tiny->new(<<'EOF');
1966 <?xml version='1.0' encoding='UTF-8'?>
1970 <tr><thead>foo<thead></tr>
1973 <tr><thead>bar<thead></tr>
1978 is $dom->find('table > td > tr > thead')->[0]->text, 'foo', 'right text';
1979 is $dom->find('script > table > td > tr > thead')->[1]->text, 'bar',
1981 is $dom->find('table > td > tr > thead')->[2], undef, 'no result';
1982 is $dom->find('table > td > tr > thead')->size, 2, 'right number of elements';
1984 # Ensure XML semantics again
1985 $dom = DOM::Tiny->new->xml(1)->parse(<<'EOF');
1988 <tr><thead>foo<thead></tr>
1991 <tr><thead>bar<thead></tr>
1995 is $dom->find('table > td > tr > thead')->[0]->text, 'foo', 'right text';
1996 is $dom->find('table > td > tr > thead')->[1]->text, 'bar', 'right text';
1997 is $dom->find('table > td > tr > thead')->[2], undef, 'no result';
1998 is $dom->find('table > td > tr > thead')->size, 2, 'right number of elements';
2001 $dom = DOM::Tiny->new(<<'EOF');
2014 is $dom->find('#foo > tr > td > #bar > tr >td')->[0]->text, 'baz', 'right text';
2015 is $dom->find('table > tr > td > table > tr >td')->[0]->text, 'baz',
2034 $dom->find('b')->each(
2036 $_->find('a')->each(sub { push @results, $_->text });
2039 is_deeply \@results, [qw(bar baz yada)], 'right results';
2041 $dom->find('a')->each(sub { push @results, $_->text });
2042 is_deeply \@results, [qw(foo bar baz yada)], 'right results';
2044 $dom->find('b')->each(
2046 $_->find('c a')->each(sub { push @results, $_->text });
2049 is_deeply \@results, [qw(baz yada)], 'right results';
2050 is $dom->at('b')->at('a')->text, 'bar', 'right text';
2051 is $dom->at('c > b > a')->text, 'bar', 'right text';
2052 is $dom->at('b')->at('c > b > a'), undef, 'no result';
2054 # Direct hash access to attributes in XML mode
2055 $dom = DOM::Tiny->new->xml(1)->parse(<<EOF);
2057 <B class="two" test>
2059 <c id="three">bar</c>
2060 <c ID="four">baz</c>
2064 ok $dom->xml, 'XML mode active';
2065 is $dom->at('a')->{id}, 'one', 'right attribute';
2066 is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes';
2067 is $dom->at('a')->at('B')->text, 'foo', 'right text';
2068 is $dom->at('B')->{class}, 'two', 'right attribute';
2069 is_deeply [sort keys %{$dom->at('a B')}], [qw(class test)], 'right attributes';
2070 is $dom->find('a B c')->[0]->text, 'bar', 'right text';
2071 is $dom->find('a B c')->[0]{id}, 'three', 'right attribute';
2072 is_deeply [sort keys %{$dom->find('a B c')->[0]}], ['id'], 'right attributes';
2073 is $dom->find('a B c')->[1]->text, 'baz', 'right text';
2074 is $dom->find('a B c')->[1]{ID}, 'four', 'right attribute';
2075 is_deeply [sort keys %{$dom->find('a B c')->[1]}], ['ID'], 'right attributes';
2076 is $dom->find('a B c')->[2], undef, 'no result';
2077 is $dom->find('a B c')->size, 2, 'right number of elements';
2079 $dom->find('a B c')->each(sub { push @results, $_->text });
2080 is_deeply \@results, [qw(bar baz)], 'right results';
2081 is $dom->find('a B c')->join("\n"),
2082 qq{<c id="three">bar</c>\n<c ID="four">baz</c>}, 'right result';
2083 is_deeply [keys %$dom], [], 'root has no attributes';
2084 is $dom->find('#nothing')->join, '', 'no result';
2086 # Direct hash access to attributes in HTML mode
2087 $dom = DOM::Tiny->new(<<EOF);
2089 <B class="two" test>
2091 <c id="three">bar</c>
2092 <c ID="four">baz</c>
2096 ok !$dom->xml, 'XML mode not active';
2097 is $dom->at('a')->{id}, 'one', 'right attribute';
2098 is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes';
2099 is $dom->at('a')->at('b')->text, 'foo', 'right text';
2100 is $dom->at('b')->{class}, 'two', 'right attribute';
2101 is_deeply [sort keys %{$dom->at('a b')}], [qw(class test)], 'right attributes';
2102 is $dom->find('a b c')->[0]->text, 'bar', 'right text';
2103 is $dom->find('a b c')->[0]{id}, 'three', 'right attribute';
2104 is_deeply [sort keys %{$dom->find('a b c')->[0]}], ['id'], 'right attributes';
2105 is $dom->find('a b c')->[1]->text, 'baz', 'right text';
2106 is $dom->find('a b c')->[1]{id}, 'four', 'right attribute';
2107 is_deeply [sort keys %{$dom->find('a b c')->[1]}], ['id'], 'right attributes';
2108 is $dom->find('a b c')->[2], undef, 'no result';
2109 is $dom->find('a b c')->size, 2, 'right number of elements';
2111 $dom->find('a b c')->each(sub { push @results, $_->text });
2112 is_deeply \@results, [qw(bar baz)], 'right results';
2113 is $dom->find('a b c')->join("\n"),
2114 qq{<c id="three">bar</c>\n<c id="four">baz</c>}, 'right result';
2115 is_deeply [keys %$dom], [], 'root has no attributes';
2116 is $dom->find('#nothing')->join, '', 'no result';
2118 # Append and prepend content
2119 $dom = DOM::Tiny->new('<a><b>Test<c /></b></a>');
2120 $dom->at('b')->append_content('<d />');
2121 is $dom->children->[0]->tag, 'a', 'right tag';
2122 is $dom->all_text, 'Test', 'right text';
2123 is $dom->at('c')->parent->tag, 'b', 'right tag';
2124 is $dom->at('d')->parent->tag, 'b', 'right tag';
2125 $dom->at('b')->prepend_content('<e>DOM</e>');
2126 is $dom->at('e')->parent->tag, 'b', 'right tag';
2127 is $dom->all_text, 'DOM Test', 'right text';
2130 $dom = DOM::Tiny->new('<a>Test</a>');
2131 is "$dom", '<a>Test</a>', 'right result';
2132 is $dom->wrap('<b></b>')->type, 'root', 'right type';
2133 is "$dom", '<a>Test</a>', 'no changes';
2134 is $dom->at('a')->wrap('<b></b>')->type, 'tag', 'right type';
2135 is "$dom", '<b><a>Test</a></b>', 'right result';
2136 is $dom->at('b')->strip->at('a')->wrap('A')->tag, 'a', 'right tag';
2137 is "$dom", '<a>Test</a>', 'right result';
2138 is $dom->at('a')->wrap('<b></b>')->tag, 'a', 'right tag';
2139 is "$dom", '<b><a>Test</a></b>', 'right result';
2140 is $dom->at('a')->wrap('C<c><d>D</d><e>E</e></c>F')->parent->tag, 'd',
2142 is "$dom", '<b>C<c><d>D<a>Test</a></d><e>E</e></c>F</b>', 'right result';
2145 $dom = DOM::Tiny->new('<a>Test</a>');
2146 is $dom->at('a')->wrap_content('A')->tag, 'a', 'right tag';
2147 is "$dom", '<a>Test</a>', 'right result';
2148 is $dom->wrap_content('<b></b>')->type, 'root', 'right type';
2149 is "$dom", '<b><a>Test</a></b>', 'right result';
2150 is $dom->at('b')->strip->at('a')->tag('e:a')->wrap_content('1<b c="d"></b>')
2151 ->tag, 'e:a', 'right tag';
2152 is "$dom", '<e:a>1<b c="d">Test</b></e:a>', 'right result';
2153 is $dom->at('a')->wrap_content('C<c><d>D</d><e>E</e></c>F')->parent->type,
2154 'root', 'right type';
2155 is "$dom", '<e:a>C<c><d>D1<b c="d">Test</b></d><e>E</e></c>F</e:a>',
2158 # Broken "div" in "td"
2159 $dom = DOM::Tiny->new(<<EOF);
2162 <td><div id="A"></td>
2163 <td><div id="B"></td>
2167 is $dom->find('table tr td')->[0]->at('div')->{id}, 'A', 'right attribute';
2168 is $dom->find('table tr td')->[1]->at('div')->{id}, 'B', 'right attribute';
2169 is $dom->find('table tr td')->[2], undef, 'no result';
2170 is $dom->find('table tr td')->size, 2, 'right number of elements';
2171 is "$dom", <<EOF, 'right result';
2174 <td><div id="A"></div></td>
2175 <td><div id="B"></div></td>
2181 $dom = DOM::Tiny->new(<<EOF);
2191 is $dom->text, '', 'no text';
2192 is $dom->text(0), "\n", 'right text';
2193 is $dom->all_text, "looks like\n it\n really\n works", 'right text';
2194 is $dom->all_text(0), "\n looks\n like\n it\n really\n \n works\n\n",
2196 is $dom->at('div')->text, 'looks works', 'right text';
2197 is $dom->at('div')->text(0), "\n looks\n \n works\n", 'right text';
2198 is $dom->at('div')->all_text, "looks like\n it\n really\n works",
2200 is $dom->at('div')->all_text(0),
2201 "\n looks\n like\n it\n really\n \n works\n", 'right text';
2202 is $dom->at('div pre')->text, "\n ", 'right text';
2203 is $dom->at('div pre')->text(0), "\n ", 'right text';
2204 is $dom->at('div pre')->all_text, "like\n it\n really\n ", 'right text';
2205 is $dom->at('div pre')->all_text(0), "like\n it\n really\n ", 'right text';
2206 is $dom->at('div pre code')->text, "like\n it\n really", 'right text';
2207 is $dom->at('div pre code')->text(0), "like\n it\n really", 'right text';
2208 is $dom->at('div pre code')->all_text, "like\n it\n really", 'right text';
2209 is $dom->at('div pre code')->all_text(0), "like\n it\n really",
2213 $dom = DOM::Tiny->new(<<EOF);
2214 <form action="/foo">
2216 <input type="text" name="a" value="A" />
2217 <input type="checkbox" name="q">
2218 <input type="checkbox" checked name="b" value="B">
2219 <input type="radio" name="r">
2220 <input type="radio" checked name="c" value="C">
2222 <input type="checkbox" name="t" value="">
2223 <input type=text name="u">
2224 <select multiple name="f">
2225 <option value="F">G</option>
2228 <option selected>I</option>
2230 <option value="J" selected>K</option>
2232 <select name="n"><option>N</option></select>
2233 <select multiple name="q"><option>Q</option></select>
2235 <option selected>R</option>
2236 <option selected>D</option>
2238 <textarea name="m">M</textarea>
2239 <button name="o" value="O">No!</button>
2240 <input type="submit" name="p" value="P" />
2243 is $dom->at('p')->val, undef, 'no value';
2244 is $dom->at('input')->val, 'A', 'right value';
2245 is $dom->at('input:checked')->val, 'B', 'right value';
2246 is $dom->at('input:checked[type=radio]')->val, 'C', 'right value';
2247 is_deeply $dom->at('select')->val, ['I', 'J'], 'right values';
2248 is $dom->at('select option')->val, 'F', 'right value';
2249 is $dom->at('select optgroup option:not([selected])')->val, 'H', 'right value';
2250 is $dom->find('select')->[1]->at('option')->val, 'N', 'right value';
2251 is $dom->find('select')->[1]->val, undef, 'no value';
2252 is_deeply $dom->find('select')->[2]->val, undef, 'no value';
2253 is $dom->find('select')->[2]->at('option')->val, 'Q', 'right value';
2254 is_deeply $dom->find('select')->last->val, 'D', 'right value';
2255 is_deeply $dom->find('select')->last->at('option')->val, 'R', 'right value';
2256 is $dom->at('textarea')->val, 'M', 'right value';
2257 is $dom->at('button')->val, 'O', 'right value';
2258 is $dom->find('form input')->last->val, 'P', 'right value';
2259 is $dom->at('input[name=q]')->val, 'on', 'right value';
2260 is $dom->at('input[name=r]')->val, 'on', 'right value';
2261 is $dom->at('input[name=s]')->val, undef, 'no value';
2262 is $dom->at('input[name=t]')->val, '', 'right value';
2263 is $dom->at('input[name=u]')->val, undef, 'no value';
2265 # PoCo example with whitespace sensitive text
2266 $dom = DOM::Tiny->new(<<EOF);
2267 <?xml version="1.0" encoding="UTF-8"?>
2271 <displayName>Homer Simpson</displayName>
2274 <formatted><![CDATA[742 Evergreen Terrace
2275 Springfield, VT 12345 USA]]></formatted>
2280 <displayName>Marge Simpson</displayName>
2283 <formatted>742 Evergreen Terrace
2284 Springfield, VT 12345 USA</formatted>
2289 is $dom->find('entry')->[0]->at('displayName')->text, 'Homer Simpson',
2291 is $dom->find('entry')->[0]->at('id')->text, '1286823', 'right text';
2292 is $dom->find('entry')->[0]->at('addresses')->children('type')->[0]->text,
2293 'home', 'right text';
2294 is $dom->find('entry')->[0]->at('addresses formatted')->text,
2295 "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
2296 is $dom->find('entry')->[0]->at('addresses formatted')->text(0),
2297 "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
2298 is $dom->find('entry')->[1]->at('displayName')->text, 'Marge Simpson',
2300 is $dom->find('entry')->[1]->at('id')->text, '1286822', 'right text';
2301 is $dom->find('entry')->[1]->at('addresses')->children('type')->[0]->text,
2302 'home', 'right text';
2303 is $dom->find('entry')->[1]->at('addresses formatted')->text,
2304 '742 Evergreen Terrace Springfield, VT 12345 USA', 'right text';
2305 is $dom->find('entry')->[1]->at('addresses formatted')->text(0),
2306 "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
2307 is $dom->find('entry')->[2], undef, 'no result';
2308 is $dom->find('entry')->size, 2, 'right number of elements';
2310 # Find attribute with hyphen in name and value
2311 $dom = DOM::Tiny->new(<<EOF);
2313 <head><meta http-equiv="content-type" content="text/html"></head>
2316 is $dom->find('[http-equiv]')->[0]{content}, 'text/html', 'right attribute';
2317 is $dom->find('[http-equiv]')->[1], undef, 'no result';
2318 is $dom->find('[http-equiv="content-type"]')->[0]{content}, 'text/html',
2320 is $dom->find('[http-equiv="content-type"]')->[1], undef, 'no result';
2321 is $dom->find('[http-equiv^="content-"]')->[0]{content}, 'text/html',
2323 is $dom->find('[http-equiv^="content-"]')->[1], undef, 'no result';
2324 is $dom->find('head > [http-equiv$="-type"]')->[0]{content}, 'text/html',
2326 is $dom->find('head > [http-equiv$="-type"]')->[1], undef, 'no result';
2328 # Find "0" attribute value
2329 $dom = DOM::Tiny->new(<<EOF);
2330 <a accesskey="0">Zero</a>
2331 <a accesskey="1">O&gTn>e</a>
2333 is $dom->find('a[accesskey]')->[0]->text, 'Zero', 'right text';
2334 is $dom->find('a[accesskey]')->[1]->text, 'O&gTn>e', 'right text';
2335 is $dom->find('a[accesskey]')->[2], undef, 'no result';
2336 is $dom->find('a[accesskey=0]')->[0]->text, 'Zero', 'right text';
2337 is $dom->find('a[accesskey=0]')->[1], undef, 'no result';
2338 is $dom->find('a[accesskey^=0]')->[0]->text, 'Zero', 'right text';
2339 is $dom->find('a[accesskey^=0]')->[1], undef, 'no result';
2340 is $dom->find('a[accesskey$=0]')->[0]->text, 'Zero', 'right text';
2341 is $dom->find('a[accesskey$=0]')->[1], undef, 'no result';
2342 is $dom->find('a[accesskey~=0]')->[0]->text, 'Zero', 'right text';
2343 is $dom->find('a[accesskey~=0]')->[1], undef, 'no result';
2344 is $dom->find('a[accesskey*=0]')->[0]->text, 'Zero', 'right text';
2345 is $dom->find('a[accesskey*=0]')->[1], undef, 'no result';
2346 is $dom->find('a[accesskey=1]')->[0]->text, 'O&gTn>e', 'right text';
2347 is $dom->find('a[accesskey=1]')->[1], undef, 'no result';
2348 is $dom->find('a[accesskey^=1]')->[0]->text, 'O&gTn>e', 'right text';
2349 is $dom->find('a[accesskey^=1]')->[1], undef, 'no result';
2350 is $dom->find('a[accesskey$=1]')->[0]->text, 'O&gTn>e', 'right text';
2351 is $dom->find('a[accesskey$=1]')->[1], undef, 'no result';
2352 is $dom->find('a[accesskey~=1]')->[0]->text, 'O&gTn>e', 'right text';
2353 is $dom->find('a[accesskey~=1]')->[1], undef, 'no result';
2354 is $dom->find('a[accesskey*=1]')->[0]->text, 'O&gTn>e', 'right text';
2355 is $dom->find('a[accesskey*=1]')->[1], undef, 'no result';
2356 is $dom->at('a[accesskey*="."]'), undef, 'no result';
2358 # Empty attribute value
2359 $dom = DOM::Tiny->new(<<EOF);
2365 is $dom->tree->[0], 'root', 'right type';
2366 is $dom->tree->[1][0], 'tag', 'right type';
2367 is $dom->tree->[1][1], 'foo', 'right tag';
2368 is_deeply $dom->tree->[1][2], {bar => ''}, 'right attributes';
2369 is $dom->tree->[1][4][0], 'text', 'right type';
2370 is $dom->tree->[1][4][1], "\n test\n", 'right text';
2371 is $dom->tree->[3][0], 'tag', 'right type';
2372 is $dom->tree->[3][1], 'bar', 'right tag';
2373 is $dom->tree->[3][4][0], 'text', 'right type';
2374 is $dom->tree->[3][4][1], 'after', 'right text';
2375 is "$dom", <<EOF, 'right result';
2382 # Case-insensitive attribute values
2383 $dom = DOM::Tiny->new(<<EOF);
2384 <p class="foo">A</p>
2385 <p class="foo bAr">B</p>
2386 <p class="FOO">C</p>
2388 is $dom->find('.foo')->map('text')->join(','), 'A,B', 'right result';
2389 is $dom->find('.FOO')->map('text')->join(','), 'C', 'right result';
2390 is $dom->find('[class=foo]')->map('text')->join(','), 'A', 'right result';
2391 is $dom->find('[class=foo i]')->map('text')->join(','), 'A,C', 'right result';
2392 is $dom->find('[class="foo" i]')->map('text')->join(','), 'A,C', 'right result';
2393 is $dom->find('[class="foo bar"]')->size, 0, 'no results';
2394 is $dom->find('[class="foo bar" i]')->map('text')->join(','), 'B',
2396 is $dom->find('[class~=foo]')->map('text')->join(','), 'A,B', 'right result';
2397 is $dom->find('[class~=foo i]')->map('text')->join(','), 'A,B,C',
2399 is $dom->find('[class*=f]')->map('text')->join(','), 'A,B', 'right result';
2400 is $dom->find('[class*=f i]')->map('text')->join(','), 'A,B,C', 'right result';
2401 is $dom->find('[class^=F]')->map('text')->join(','), 'C', 'right result';
2402 is $dom->find('[class^=F i]')->map('text')->join(','), 'A,B,C', 'right result';
2403 is $dom->find('[class$=O]')->map('text')->join(','), 'C', 'right result';
2404 is $dom->find('[class$=O i]')->map('text')->join(','), 'A,C', 'right result';
2406 # Nested description lists
2407 $dom = DOM::Tiny->new(<<EOF);
2418 is $dom->find('dl > dd > dl > dt')->[0]->text, 'B', 'right text';
2419 is $dom->find('dl > dd > dl > dd')->[0]->text, 'C', 'right text';
2420 is $dom->find('dl > dt')->[0]->text, 'A', 'right text';
2423 $dom = DOM::Tiny->new(<<EOF);
2436 is $dom->find('div > ul > li')->[0]->text, 'A', 'right text';
2437 is $dom->find('div > ul > li')->[1], undef, 'no result';
2438 is $dom->find('div > ul li')->[0]->text, 'A', 'right text';
2439 is $dom->find('div > ul li')->[1]->text, 'B', 'right text';
2440 is $dom->find('div > ul li')->[2], undef, 'no result';
2441 is $dom->find('div > ul ul')->[0]->text, 'C', 'right text';
2442 is $dom->find('div > ul ul')->[1], undef, 'no result';
2446 = DOM::Tiny->new('<a href="http://example.com" id="foo" class="bar">Ok!</a>');
2447 is $dom->at('a:not([href$=foo])[href^=h]')->text, 'Ok!', 'right text';
2448 is $dom->at('a:not([href$=example.com])[href^=h]'), undef, 'no result';
2449 is $dom->at('a[href^=h]#foo.bar')->text, 'Ok!', 'right text';
2450 is $dom->at('a[href^=h]#foo.baz'), undef, 'no result';
2451 is $dom->at('a[href^=h]#foo:not(b)')->text, 'Ok!', 'right text';
2452 is $dom->at('a[href^=h]#foo:not(a)'), undef, 'no result';
2453 is $dom->at('[href^=h].bar:not(b)[href$=m]#foo')->text, 'Ok!', 'right text';
2454 is $dom->at('[href^=h].bar:not(b)[href$=m]#bar'), undef, 'no result';
2455 is $dom->at(':not(b)#foo#foo')->text, 'Ok!', 'right text';
2456 is $dom->at(':not(b)#foo#bar'), undef, 'no result';
2457 is $dom->at(':not([href^=h]#foo#bar)')->text, 'Ok!', 'right text';
2458 is $dom->at(':not([href^=h]#foo#foo)'), undef, 'no result';
2460 # Slash between attributes
2461 $dom = DOM::Tiny->new('<input /type=checkbox / value="/a/" checked/><br/>');
2462 is_deeply $dom->at('input')->attr,
2463 {type => 'checkbox', value => '/a/', checked => undef}, 'right attributes';
2464 is "$dom", '<input checked type="checkbox" value="/a/"><br>', 'right result';
2466 # Dot and hash in class and id attributes
2467 $dom = DOM::Tiny->new('<p class="a#b.c">A</p><p id="a#b.c">B</p>');
2468 is $dom->at('p.a\#b\.c')->text, 'A', 'right text';
2469 is $dom->at(':not(p.a\#b\.c)')->text, 'B', 'right text';
2470 is $dom->at('p#a\#b\.c')->text, 'B', 'right text';
2471 is $dom->at(':not(p#a\#b\.c)')->text, 'A', 'right text';
2474 $dom = DOM::Tiny->new('< span>a< /span><b >b</b><span >c</ span>');
2475 is $dom->at('span')->text, 'a', 'right text';
2476 is $dom->at('span + b')->text, 'b', 'right text';
2477 is $dom->at('b + span')->text, 'c', 'right text';
2478 is "$dom", '<span>a</span><b>b</b><span>c</span>', 'right result';
2480 # Selectors with leading and trailing whitespace
2481 $dom = DOM::Tiny->new('<div id=foo><b>works</b></div>');
2482 is $dom->at(' div b ')->text, 'works', 'right text';
2483 is $dom->at(' :not( #foo ) ')->text, 'works', 'right text';
2486 $dom = DOM::Tiny->new('0');
2487 is "$dom", '0', 'right result';
2488 $dom->append_content('☃');
2489 is "$dom", '0☃', 'right result';
2490 is $dom->parse('<!DOCTYPE 0>'), '<!DOCTYPE 0>', 'successful roundtrip';
2491 is $dom->parse('<!--0-->'), '<!--0-->', 'successful roundtrip';
2492 is $dom->parse('<![CDATA[0]]>'), '<![CDATA[0]]>', 'successful roundtrip';
2493 is $dom->parse('<?0?>'), '<?0?>', 'successful roundtrip';
2496 $dom = DOM::Tiny->new('<div />< div ><pre />test</div >123');
2497 is $dom->at('div > div > pre')->text, 'test', 'right text';
2498 is "$dom", '<div><div><pre>test</pre></div>123</div>', 'right result';
2499 $dom = DOM::Tiny->new('<p /><svg><circle /><circle /></svg>');
2500 is $dom->find('p > svg > circle')->size, 2, 'two circles';
2501 is "$dom", '<p><svg><circle></circle><circle></circle></svg></p>',
2505 $dom = DOM::Tiny->new('<image src="foo.png">test');
2506 is $dom->at('img')->{src}, 'foo.png', 'right attribute';
2507 is "$dom", '<img src="foo.png">test', 'right result';
2510 $dom = DOM::Tiny->new('<title> <p>test<</title>');
2511 is $dom->at('title')->text, ' <p>test<', 'right text';
2512 is "$dom", '<title> <p>test<</title>', 'right result';
2515 $dom = DOM::Tiny->new('<textarea id="a"> <p>test<</textarea>');
2516 is $dom->at('textarea#a')->text, ' <p>test<', 'right text';
2517 is "$dom", '<textarea id="a"> <p>test<</textarea>', 'right result';
2520 $dom = DOM::Tiny->new(<<EOF);
2522 <!-- bad idea -- HTML5 -->
2524 <!-- bad idea -- HTML4 -- >
2526 is $dom->tree->[1][1], ' HTML5 ', 'right comment';
2527 is $dom->tree->[3][1], ' bad idea -- HTML5 ', 'right comment';
2528 is $dom->tree->[5][1], ' HTML4 ', 'right comment';
2529 is $dom->tree->[7][1], ' bad idea -- HTML4 ', 'right comment';
2532 skip 'Regex subexpression recursion causes SIGSEGV on 5.8', 1 unless $] >= 5.010000;
2533 # Huge number of attributes
2534 $dom = DOM::Tiny->new('<div ' . ('a=b ' x 32768) . '>Test</div>');
2535 is $dom->at('div[a=b]')->text, 'Test', 'right text';
2538 # Huge number of nested tags
2539 my $huge = ('<a>' x 100) . 'works' . ('</a>' x 100);
2540 $dom = DOM::Tiny->new($huge);
2541 is $dom->all_text, 'works', 'right text';
2542 is "$dom", $huge, 'right result';
2545 is +JSON::PP->new->convert_blessed->encode([DOM::Tiny->new('<a></a>')]), '["<a></a>"]', 'right result';