Commit | Line | Data |
d6512b50 |
1 | use strict; |
2 | use warnings; |
3 | use utf8; |
4 | use Test::More; |
5 | use DOM::Tiny; |
dbff35d4 |
6 | use JSON::PP (); |
d6512b50 |
7 | |
8 | # Empty |
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'); |
15 | |
16 | # Simple (basics) |
17 | my $dom |
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'; |
20 | my @div; |
21 | push @div, $dom->find('div[id]')->map('text')->each; |
22 | is_deeply \@div, [qw(A B)], 'found all div elements with id'; |
23 | @div = (); |
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>', |
29 | 'right result'; |
30 | |
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)], |
34 | 'right result'; |
35 | is $dom->tap(sub { $_->at('#b')->remove }), '<div id="a">A</div>', |
36 | 'right result'; |
37 | |
38 | # Build tree from scratch |
39 | is(DOM::Tiny->new->append_content('<p>')->at('p')->append_content('0')->text, |
40 | '0', 'right text'); |
41 | |
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> |
45 | EOF |
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> |
77 | EOF |
78 | |
79 | # Select based on parent |
80 | $dom = DOM::Tiny->new(<<EOF); |
81 | <body> |
82 | <div>test1</div> |
83 | <div><div>test2</div></div> |
84 | <body> |
85 | EOF |
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'; |
93 | |
94 | # A bit of everything (basic navigation) |
95 | $dom = DOM::Tiny->new->parse(<<EOF); |
96 | <!doctype foo> |
97 | <foo bar="ba<z"> |
98 | test |
99 | <simple class="working">easy</simple> |
100 | <test foo="bar" id="test" /> |
101 | <!-- lala --> |
102 | works well |
103 | <![CDATA[ yada yada]]> |
104 | <?boom lalalala ?> |
105 | <a little bit broken> |
106 | < very broken |
107 | <br /> |
108 | more text |
109 | </foo> |
110 | EOF |
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'; |
118 | <!DOCTYPE foo> |
119 | <foo bar="ba<z"> |
120 | test |
121 | <simple class="working">easy</simple> |
122 | <test foo="bar" id="test"></test> |
123 | <!-- lala --> |
124 | works well |
125 | <![CDATA[ yada yada]]> |
126 | <?boom lalalala ?> |
127 | <a bit broken little> |
128 | < very broken |
129 | <br> |
130 | more text |
131 | </a></foo> |
132 | EOF |
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>', |
143 | 'stringified right'; |
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'], |
161 | 'right results'; |
162 | ok !$dom->at('simple')->ancestors->first->xml, 'XML mode not active'; |
163 | |
164 | # Nodes |
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', |
170 | 'right content'; |
171 | is $dom->at('p')->child_nodes->last->preceding_nodes->last->content, '123', |
172 | 'right content'; |
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', |
180 | 'right content'; |
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 ', |
190 | 'right content'; |
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', |
217 | 'right content'; |
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'; |
221 | |
222 | # Modify nodes |
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', |
232 | 'right content'; |
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', |
236 | 'right tag'; |
237 | is $dom->at('script')->child_nodes->first->next->content, 'fce', |
238 | 'right content'; |
239 | is $dom->at('script')->child_nodes->first->previous, undef, 'no siblings'; |
240 | is $dom->at('script')->child_nodes->[2]->previous->content, 'fce', |
241 | 'right content'; |
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><b>:)</b>a</i><b>fce</b>1<b>d</b></script>', 'right result'; |
247 | is $dom->at('b')->child_nodes->first->ancestors->map('tag')->join(','), |
248 | 'b,i,script', 'right result'; |
249 | is $dom->at('b')->child_nodes->first->append_content('g')->content, ':)g', |
250 | 'right content'; |
251 | is $dom->at('b')->child_nodes->first->prepend_content('h')->content, 'h:)g', |
252 | 'right content'; |
253 | is "$dom", '<script><i><b>h:)g</b>a</i><b>fce</b>1<b>d</b></script>', |
254 | 'right result'; |
255 | is $dom->at('script > b:last-of-type')->append('<!--y-->') |
256 | ->following_nodes->first->content, 'y', 'right content'; |
257 | is $dom->at('i')->prepend('z')->preceding_nodes->first->content, 'z', |
258 | 'right content'; |
259 | is $dom->at('i')->following->last->text, 'd', 'right text'; |
260 | is $dom->at('i')->following->size, 2, 'right number of following elements'; |
261 | is $dom->at('i')->following('b:last-of-type')->first->text, 'd', 'right text'; |
262 | is $dom->at('i')->following('b:last-of-type')->size, 1, |
263 | 'right number of following elements'; |
264 | is $dom->following->size, 0, 'no following elements'; |
265 | is $dom->at('script > b:last-of-type')->preceding->first->tag, 'i', 'right tag'; |
266 | is $dom->at('script > b:last-of-type')->preceding->size, 2, |
267 | 'right number of preceding elements'; |
268 | is $dom->at('script > b:last-of-type')->preceding('b')->first->tag, 'b', |
269 | 'right tag'; |
270 | is $dom->at('script > b:last-of-type')->preceding('b')->size, 1, |
271 | 'right number of preceding elements'; |
272 | is $dom->preceding->size, 0, 'no preceding elements'; |
273 | is "$dom", '<script>z<i><b>h:)g</b>a</i><b>fce</b>1<b>d</b><!--y--></script>', |
274 | 'right result'; |
275 | |
276 | # XML nodes |
277 | $dom = DOM::Tiny->new->xml(1)->parse('<b>test<image /></b>'); |
278 | ok $dom->at('b')->child_nodes->first->xml, 'XML mode active'; |
279 | ok $dom->at('b')->child_nodes->first->replace('<br>')->child_nodes->first->xml, |
280 | 'XML mode active'; |
281 | is "$dom", '<b><br /><image /></b>', 'right result'; |
282 | |
283 | # Treating nodes as elements |
284 | $dom = DOM::Tiny->new('foo<b>bar</b>baz'); |
285 | is $dom->child_nodes->first->child_nodes->size, 0, 'no nodes'; |
286 | is $dom->child_nodes->first->descendant_nodes->size, 0, 'no nodes'; |
287 | is $dom->child_nodes->first->children->size, 0, 'no children'; |
288 | is $dom->child_nodes->first->strip->parent, 'foo<b>bar</b>baz', 'no changes'; |
289 | is $dom->child_nodes->first->at('b'), undef, 'no result'; |
290 | is $dom->child_nodes->first->find('*')->size, 0, 'no results'; |
291 | ok !$dom->child_nodes->first->matches('*'), 'no match'; |
292 | is_deeply $dom->child_nodes->first->attr, {}, 'no attributes'; |
293 | is $dom->child_nodes->first->namespace, undef, 'no namespace'; |
294 | is $dom->child_nodes->first->tag, undef, 'no tag'; |
295 | is $dom->child_nodes->first->text, '', 'no text'; |
296 | is $dom->child_nodes->first->all_text, '', 'no text'; |
297 | |
298 | # Class and ID |
299 | $dom = DOM::Tiny->new('<div id="id" class="class">a</div>'); |
300 | is $dom->at('div#id.class')->text, 'a', 'right text'; |
301 | |
302 | # Deep nesting (parent combinator) |
303 | $dom = DOM::Tiny->new(<<EOF); |
304 | <html> |
305 | <head> |
306 | <title>Foo</title> |
307 | </head> |
308 | <body> |
309 | <div id="container"> |
310 | <div id="header"> |
311 | <div id="logo">Hello World</div> |
312 | <div id="buttons"> |
313 | <p id="foo">Foo</p> |
314 | </div> |
315 | </div> |
316 | <form> |
317 | <div id="buttons"> |
318 | <p id="bar">Bar</p> |
319 | </div> |
320 | </form> |
321 | <div id="content">More stuff</div> |
322 | </div> |
323 | </body> |
324 | </html> |
325 | EOF |
326 | my $p = $dom->find('body > #container > div p[id]'); |
327 | is $p->[0]->attr('id'), 'foo', 'right id attribute'; |
328 | is $p->[1], undef, 'no second result'; |
329 | is $p->size, 1, 'right number of elements'; |
330 | my @p; |
331 | @div = (); |
332 | $dom->find('div')->each(sub { push @div, $_->attr('id') }); |
333 | $dom->find('p')->each(sub { push @p, $_->attr('id') }); |
334 | is_deeply \@p, [qw(foo bar)], 'found all p elements'; |
335 | my $ids = [qw(container header logo buttons buttons content)]; |
336 | is_deeply \@div, $ids, 'found all div elements'; |
337 | is_deeply [$dom->at('p')->ancestors->map('tag')->each], |
338 | [qw(div div div body html)], 'right results'; |
339 | is_deeply [$dom->at('html')->ancestors->each], [], 'no results'; |
340 | is_deeply [$dom->ancestors->each], [], 'no results'; |
341 | |
342 | # Script tag |
343 | $dom = DOM::Tiny->new(<<EOF); |
344 | <script charset="utf-8">alert('lalala');</script> |
345 | EOF |
346 | is $dom->at('script')->text, "alert('lalala');", 'right script content'; |
347 | |
348 | # HTML5 (unquoted values) |
349 | $dom = DOM::Tiny->new( |
350 | '<div id = test foo ="bar" class=tset bar=/baz/ baz=//>works</div>'); |
351 | is $dom->at('#test')->text, 'works', 'right text'; |
352 | is $dom->at('div')->text, 'works', 'right text'; |
353 | is $dom->at('[foo=bar][foo="bar"]')->text, 'works', 'right text'; |
354 | is $dom->at('[foo="ba"]'), undef, 'no result'; |
355 | is $dom->at('[foo=bar]')->text, 'works', 'right text'; |
356 | is $dom->at('[foo=ba]'), undef, 'no result'; |
357 | is $dom->at('.tset')->text, 'works', 'right text'; |
358 | is $dom->at('[bar=/baz/]')->text, 'works', 'right text'; |
359 | is $dom->at('[baz=//]')->text, 'works', 'right text'; |
360 | |
361 | # HTML1 (single quotes, uppercase tags and whitespace in attributes) |
362 | $dom = DOM::Tiny->new(q{<DIV id = 'test' foo ='bar' class= "tset">works</DIV>}); |
363 | is $dom->at('#test')->text, 'works', 'right text'; |
364 | is $dom->at('div')->text, 'works', 'right text'; |
365 | is $dom->at('[foo="bar"]')->text, 'works', 'right text'; |
366 | is $dom->at('[foo="ba"]'), undef, 'no result'; |
367 | is $dom->at('[foo=bar]')->text, 'works', 'right text'; |
368 | is $dom->at('[foo=ba]'), undef, 'no result'; |
369 | is $dom->at('.tset')->text, 'works', 'right text'; |
370 | |
371 | # Already decoded Unicode snowman and quotes in selector |
372 | $dom = DOM::Tiny->new('<div id="snow'm"an">☃</div>'); |
373 | is $dom->at('[id="snow\'m\"an"]')->text, '☃', 'right text'; |
374 | is $dom->at('[id="snow\'m\22 an"]')->text, '☃', 'right text'; |
375 | is $dom->at('[id="snow\'m\000022an"]')->text, '☃', 'right text'; |
376 | is $dom->at('[id="snow\'m\22an"]'), undef, 'no result'; |
377 | is $dom->at('[id="snow\'m\21 an"]'), undef, 'no result'; |
378 | is $dom->at('[id="snow\'m\000021an"]'), undef, 'no result'; |
379 | is $dom->at('[id="snow\'m\000021 an"]'), undef, 'no result'; |
380 | is $dom->at("[id='snow\\'m\"an']")->text, '☃', 'right text'; |
381 | is $dom->at("[id='snow\\27m\"an']")->text, '☃', 'right text'; |
382 | |
383 | # Unicode and escaped selectors |
384 | my $html |
385 | = '<html><div id="☃x">Snowman</div><div class="x ♥">Heart</div></html>'; |
386 | $dom = DOM::Tiny->new($html); |
387 | is $dom->at("#\\\n\\002603x")->text, 'Snowman', 'right text'; |
388 | is $dom->at('#\\2603 x')->text, 'Snowman', 'right text'; |
389 | is $dom->at("#\\\n\\2603 x")->text, 'Snowman', 'right text'; |
390 | is $dom->at(qq{[id="\\\n\\2603 x"]})->text, 'Snowman', 'right text'; |
391 | is $dom->at(qq{[id="\\\n\\002603x"]})->text, 'Snowman', 'right text'; |
392 | is $dom->at(qq{[id="\\\\2603 x"]})->text, 'Snowman', 'right text'; |
393 | is $dom->at("html #\\\n\\002603x")->text, 'Snowman', 'right text'; |
394 | is $dom->at('html #\\2603 x')->text, 'Snowman', 'right text'; |
395 | is $dom->at("html #\\\n\\2603 x")->text, 'Snowman', 'right text'; |
396 | is $dom->at(qq{html [id="\\\n\\2603 x"]})->text, 'Snowman', 'right text'; |
397 | is $dom->at(qq{html [id="\\\n\\002603x"]})->text, 'Snowman', 'right text'; |
398 | is $dom->at(qq{html [id="\\\\2603 x"]})->text, 'Snowman', 'right text'; |
399 | is $dom->at('#☃x')->text, 'Snowman', 'right text'; |
400 | is $dom->at('div#☃x')->text, 'Snowman', 'right text'; |
401 | is $dom->at('html div#☃x')->text, 'Snowman', 'right text'; |
402 | is $dom->at('[id^="☃"]')->text, 'Snowman', 'right text'; |
403 | is $dom->at('div[id^="☃"]')->text, 'Snowman', 'right text'; |
404 | is $dom->at('html div[id^="☃"]')->text, 'Snowman', 'right text'; |
405 | is $dom->at('html > div[id^="☃"]')->text, 'Snowman', 'right text'; |
406 | is $dom->at('[id^=☃]')->text, 'Snowman', 'right text'; |
407 | is $dom->at('div[id^=☃]')->text, 'Snowman', 'right text'; |
408 | is $dom->at('html div[id^=☃]')->text, 'Snowman', 'right text'; |
409 | is $dom->at('html > div[id^=☃]')->text, 'Snowman', 'right text'; |
410 | is $dom->at(".\\\n\\002665")->text, 'Heart', 'right text'; |
411 | is $dom->at('.\\2665')->text, 'Heart', 'right text'; |
412 | is $dom->at("html .\\\n\\002665")->text, 'Heart', 'right text'; |
413 | is $dom->at('html .\\2665')->text, 'Heart', 'right text'; |
414 | is $dom->at(qq{html [class\$="\\\n\\002665"]})->text, 'Heart', 'right text'; |
415 | is $dom->at(qq{html [class\$="\\2665"]})->text, 'Heart', 'right text'; |
416 | is $dom->at(qq{[class\$="\\\n\\002665"]})->text, 'Heart', 'right text'; |
417 | is $dom->at(qq{[class\$="\\2665"]})->text, 'Heart', 'right text'; |
418 | is $dom->at('.x')->text, 'Heart', 'right text'; |
419 | is $dom->at('html .x')->text, 'Heart', 'right text'; |
420 | is $dom->at('.♥')->text, 'Heart', 'right text'; |
421 | is $dom->at('html .♥')->text, 'Heart', 'right text'; |
422 | is $dom->at('div.♥')->text, 'Heart', 'right text'; |
423 | is $dom->at('html div.♥')->text, 'Heart', 'right text'; |
424 | is $dom->at('[class$="♥"]')->text, 'Heart', 'right text'; |
425 | is $dom->at('div[class$="♥"]')->text, 'Heart', 'right text'; |
426 | is $dom->at('html div[class$="♥"]')->text, 'Heart', 'right text'; |
427 | is $dom->at('html > div[class$="♥"]')->text, 'Heart', 'right text'; |
428 | is $dom->at('[class$=♥]')->text, 'Heart', 'right text'; |
429 | is $dom->at('div[class$=♥]')->text, 'Heart', 'right text'; |
430 | is $dom->at('html div[class$=♥]')->text, 'Heart', 'right text'; |
431 | is $dom->at('html > div[class$=♥]')->text, 'Heart', 'right text'; |
432 | is $dom->at('[class~="♥"]')->text, 'Heart', 'right text'; |
433 | is $dom->at('div[class~="♥"]')->text, 'Heart', 'right text'; |
434 | is $dom->at('html div[class~="♥"]')->text, 'Heart', 'right text'; |
435 | is $dom->at('html > div[class~="♥"]')->text, 'Heart', 'right text'; |
436 | is $dom->at('[class~=♥]')->text, 'Heart', 'right text'; |
437 | is $dom->at('div[class~=♥]')->text, 'Heart', 'right text'; |
438 | is $dom->at('html div[class~=♥]')->text, 'Heart', 'right text'; |
439 | is $dom->at('html > div[class~=♥]')->text, 'Heart', 'right text'; |
440 | is $dom->at('[class~="x"]')->text, 'Heart', 'right text'; |
441 | is $dom->at('div[class~="x"]')->text, 'Heart', 'right text'; |
442 | is $dom->at('html div[class~="x"]')->text, 'Heart', 'right text'; |
443 | is $dom->at('html > div[class~="x"]')->text, 'Heart', 'right text'; |
444 | is $dom->at('[class~=x]')->text, 'Heart', 'right text'; |
445 | is $dom->at('div[class~=x]')->text, 'Heart', 'right text'; |
446 | is $dom->at('html div[class~=x]')->text, 'Heart', 'right text'; |
447 | is $dom->at('html > div[class~=x]')->text, 'Heart', 'right text'; |
448 | is $dom->at('html'), $html, 'right result'; |
449 | is $dom->at('#☃x')->parent, $html, 'right result'; |
450 | is $dom->at('#☃x')->root, $html, 'right result'; |
451 | is $dom->children('html')->first, $html, 'right result'; |
452 | is $dom->to_string, $html, 'right result'; |
453 | is $dom->content, $html, 'right result'; |
454 | |
455 | # Looks remotely like HTML |
456 | $dom = DOM::Tiny->new( |
457 | '<!DOCTYPE H "-/W/D HT 4/E">☃<title class=test>♥</title>☃'); |
458 | is $dom->at('title')->text, '♥', 'right text'; |
459 | is $dom->at('*')->text, '♥', 'right text'; |
460 | is $dom->at('.test')->text, '♥', 'right text'; |
461 | |
462 | # Replace elements |
463 | $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>'); |
464 | is $dom->at('p')->replace('<foo>bar</foo>'), '<div>foo<foo>bar</foo>bar</div>', |
465 | 'right result'; |
466 | is "$dom", '<div>foo<foo>bar</foo>bar</div>', 'right result'; |
467 | $dom->at('foo')->replace(DOM::Tiny->new('text')); |
468 | is "$dom", '<div>footextbar</div>', 'right result'; |
469 | $dom = DOM::Tiny->new('<div>foo</div><div>bar</div>'); |
470 | $dom->find('div')->each(sub { shift->replace('<p>test</p>') }); |
471 | is "$dom", '<p>test</p><p>test</p>', 'right result'; |
472 | $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>'); |
473 | is $dom->replace('♥'), '♥', 'right result'; |
474 | is "$dom", '♥', 'right result'; |
475 | $dom->replace('<div>foo<p>lalala</p>bar</div>'); |
476 | is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result'; |
477 | is $dom->at('p')->replace(''), '<div>foobar</div>', 'right result'; |
478 | is "$dom", '<div>foobar</div>', 'right result'; |
479 | is $dom->replace(''), '', 'no result'; |
480 | is "$dom", '', 'no result'; |
481 | $dom->replace('<div>foo<p>lalala</p>bar</div>'); |
482 | is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result'; |
483 | $dom->find('p')->map(replace => ''); |
484 | is "$dom", '<div>foobar</div>', 'right result'; |
485 | $dom = DOM::Tiny->new('<div>♥</div>'); |
486 | $dom->at('div')->content('☃'); |
487 | is "$dom", '<div>☃</div>', 'right result'; |
488 | $dom = DOM::Tiny->new('<div>♥</div>'); |
489 | $dom->at('div')->content("\x{2603}"); |
490 | is $dom->to_string, '<div>☃</div>', 'right result'; |
491 | is $dom->at('div')->replace('<p>♥</p>')->root, '<p>♥</p>', 'right result'; |
492 | is $dom->to_string, '<p>♥</p>', 'right result'; |
493 | is $dom->replace('<b>whatever</b>')->root, '<b>whatever</b>', 'right result'; |
494 | is $dom->to_string, '<b>whatever</b>', 'right result'; |
495 | $dom->at('b')->prepend('<p>foo</p>')->append('<p>bar</p>'); |
496 | is "$dom", '<p>foo</p><b>whatever</b><p>bar</p>', 'right result'; |
497 | is $dom->find('p')->map('remove')->first->root->at('b')->text, 'whatever', |
498 | 'right result'; |
499 | is "$dom", '<b>whatever</b>', 'right result'; |
500 | is $dom->at('b')->strip, 'whatever', 'right result'; |
501 | is $dom->strip, 'whatever', 'right result'; |
502 | is $dom->remove, '', 'right result'; |
503 | $dom->replace('A<div>B<p>C<b>D<i><u>E</u></i>F</b>G</p><div>H</div></div>I'); |
504 | is $dom->find(':not(div):not(i):not(u)')->map('strip')->first->root, |
505 | 'A<div>BCD<i><u>E</u></i>FG<div>H</div></div>I', 'right result'; |
506 | is $dom->at('i')->to_string, '<i><u>E</u></i>', 'right result'; |
507 | $dom = DOM::Tiny->new('<div><div>A</div><div>B</div>C</div>'); |
508 | is $dom->at('div')->at('div')->text, 'A', 'right text'; |
509 | $dom->at('div')->find('div')->map('strip'); |
510 | is "$dom", '<div>ABC</div>', 'right result'; |
511 | |
512 | # Replace element content |
513 | $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>'); |
514 | is $dom->at('p')->content('bar'), '<p>bar</p>', 'right result'; |
515 | is "$dom", '<div>foo<p>bar</p>bar</div>', 'right result'; |
516 | $dom->at('p')->content(DOM::Tiny->new('text')); |
517 | is "$dom", '<div>foo<p>text</p>bar</div>', 'right result'; |
518 | $dom = DOM::Tiny->new('<div>foo</div><div>bar</div>'); |
519 | $dom->find('div')->each(sub { shift->content('<p>test</p>') }); |
520 | is "$dom", '<div><p>test</p></div><div><p>test</p></div>', 'right result'; |
521 | $dom->find('p')->each(sub { shift->content('') }); |
522 | is "$dom", '<div><p></p></div><div><p></p></div>', 'right result'; |
523 | $dom = DOM::Tiny->new('<div><p id="☃" /></div>'); |
524 | $dom->at('#☃')->content('♥'); |
525 | is "$dom", '<div><p id="☃">♥</p></div>', 'right result'; |
526 | $dom = DOM::Tiny->new('<div>foo<p>lalala</p>bar</div>'); |
527 | $dom->content('♥'); |
528 | is "$dom", '♥', 'right result'; |
529 | is $dom->content('<div>foo<p>lalala</p>bar</div>'), |
530 | '<div>foo<p>lalala</p>bar</div>', 'right result'; |
531 | is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result'; |
532 | is $dom->content(''), '', 'no result'; |
533 | is "$dom", '', 'no result'; |
534 | $dom->content('<div>foo<p>lalala</p>bar</div>'); |
535 | is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result'; |
536 | is $dom->at('p')->content(''), '<p></p>', 'right result'; |
537 | |
538 | # Mixed search and tree walk |
539 | $dom = DOM::Tiny->new(<<EOF); |
540 | <table> |
541 | <tr> |
542 | <td>text1</td> |
543 | <td>text2</td> |
544 | </tr> |
545 | </table> |
546 | EOF |
547 | my @data; |
548 | for my $tr ($dom->find('table tr')->each) { |
549 | for my $td (@{$tr->children}) { |
550 | push @data, $td->tag, $td->all_text; |
551 | } |
552 | } |
553 | is $data[0], 'td', 'right tag'; |
554 | is $data[1], 'text1', 'right text'; |
555 | is $data[2], 'td', 'right tag'; |
556 | is $data[3], 'text2', 'right text'; |
557 | is $data[4], undef, 'no tag'; |
558 | |
559 | # RSS |
560 | $dom = DOM::Tiny->new(<<EOF); |
561 | <?xml version="1.0" encoding="UTF-8"?> |
562 | <rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"> |
563 | <channel> |
564 | <title>Test Blog</title> |
565 | <link>http://blog.example.com</link> |
566 | <description>lalala</description> |
567 | <generator>DOM::Tiny</generator> |
568 | <item> |
569 | <pubDate>Mon, 12 Jul 2010 20:42:00</pubDate> |
570 | <title>Works!</title> |
571 | <link>http://blog.example.com/test</link> |
572 | <guid>http://blog.example.com/test</guid> |
573 | <description> |
574 | <![CDATA[<p>trololololo>]]> |
575 | </description> |
576 | <my:extension foo:id="works"> |
577 | <![CDATA[ |
578 | [awesome]] |
579 | ]]> |
580 | </my:extension> |
581 | </item> |
582 | </channel> |
583 | </rss> |
584 | EOF |
585 | ok $dom->xml, 'XML mode detected'; |
586 | is $dom->find('rss')->[0]->attr('version'), '2.0', 'right version'; |
587 | is_deeply [$dom->at('title')->ancestors->map('tag')->each], [qw(channel rss)], |
588 | 'right results'; |
589 | is $dom->at('extension')->attr('foo:id'), 'works', 'right id'; |
590 | like $dom->at('#works')->text, qr/\[awesome\]\]/, 'right text'; |
591 | like $dom->at('[id="works"]')->text, qr/\[awesome\]\]/, 'right text'; |
592 | is $dom->find('description')->[1]->text, '<p>trololololo>', 'right text'; |
593 | is $dom->at('pubDate')->text, 'Mon, 12 Jul 2010 20:42:00', 'right text'; |
594 | like $dom->at('[id*="ork"]')->text, qr/\[awesome\]\]/, 'right text'; |
595 | like $dom->at('[id*="orks"]')->text, qr/\[awesome\]\]/, 'right text'; |
596 | like $dom->at('[id*="work"]')->text, qr/\[awesome\]\]/, 'right text'; |
597 | like $dom->at('[id*="or"]')->text, qr/\[awesome\]\]/, 'right text'; |
598 | ok $dom->at('rss')->xml, 'XML mode active'; |
599 | ok $dom->at('extension')->parent->xml, 'XML mode active'; |
600 | ok $dom->at('extension')->root->xml, 'XML mode active'; |
601 | ok $dom->children('rss')->first->xml, 'XML mode active'; |
602 | ok $dom->at('title')->ancestors->first->xml, 'XML mode active'; |
603 | |
604 | # Namespace |
605 | $dom = DOM::Tiny->new(<<EOF); |
606 | <?xml version="1.0"?> |
607 | <bk:book xmlns='uri:default-ns' |
608 | xmlns:bk='uri:book-ns' |
609 | xmlns:isbn='uri:isbn-ns'> |
610 | <bk:title>Programming Perl</bk:title> |
611 | <comment>rocks!</comment> |
612 | <nons xmlns=''> |
613 | <section>Nothing</section> |
614 | </nons> |
615 | <meta xmlns='uri:meta-ns'> |
616 | <isbn:number>978-0596000271</isbn:number> |
617 | </meta> |
618 | </bk:book> |
619 | EOF |
620 | ok $dom->xml, 'XML mode detected'; |
621 | is $dom->namespace, undef, 'no namespace'; |
622 | is $dom->at('book comment')->namespace, 'uri:default-ns', 'right namespace'; |
623 | is $dom->at('book comment')->text, 'rocks!', 'right text'; |
624 | is $dom->at('book nons section')->namespace, '', 'no namespace'; |
625 | is $dom->at('book nons section')->text, 'Nothing', 'right text'; |
626 | is $dom->at('book meta number')->namespace, 'uri:isbn-ns', 'right namespace'; |
627 | is $dom->at('book meta number')->text, '978-0596000271', 'right text'; |
628 | is $dom->children('bk\:book')->first->{xmlns}, 'uri:default-ns', |
629 | 'right attribute'; |
630 | is $dom->children('book')->first->{xmlns}, 'uri:default-ns', 'right attribute'; |
631 | is $dom->children('k\:book')->first, undef, 'no result'; |
632 | is $dom->children('ook')->first, undef, 'no result'; |
633 | is $dom->at('k\:book'), undef, 'no result'; |
634 | is $dom->at('ook'), undef, 'no result'; |
635 | is $dom->at('[xmlns\:bk]')->{'xmlns:bk'}, 'uri:book-ns', 'right attribute'; |
636 | is $dom->at('[bk]')->{'xmlns:bk'}, 'uri:book-ns', 'right attribute'; |
637 | is $dom->at('[bk]')->attr('xmlns:bk'), 'uri:book-ns', 'right attribute'; |
638 | is $dom->at('[bk]')->attr('s:bk'), undef, 'no attribute'; |
639 | is $dom->at('[bk]')->attr('bk'), undef, 'no attribute'; |
640 | is $dom->at('[bk]')->attr('k'), undef, 'no attribute'; |
641 | is $dom->at('[s\:bk]'), undef, 'no result'; |
642 | is $dom->at('[k]'), undef, 'no result'; |
643 | is $dom->at('number')->ancestors('meta')->first->{xmlns}, 'uri:meta-ns', |
644 | 'right attribute'; |
645 | ok $dom->at('nons')->matches('book > nons'), 'element did match'; |
646 | ok !$dom->at('title')->matches('book > nons > section'), |
647 | 'element did not match'; |
648 | |
649 | # Dots |
650 | $dom = DOM::Tiny->new(<<EOF); |
651 | <?xml version="1.0"?> |
652 | <foo xmlns:foo.bar="uri:first"> |
653 | <bar xmlns:fooxbar="uri:second"> |
654 | <foo.bar:baz>First</fooxbar:baz> |
655 | <fooxbar:ya.da>Second</foo.bar:ya.da> |
656 | </bar> |
657 | </foo> |
658 | EOF |
659 | is $dom->at('foo bar baz')->text, 'First', 'right text'; |
660 | is $dom->at('baz')->namespace, 'uri:first', 'right namespace'; |
661 | is $dom->at('foo bar ya\.da')->text, 'Second', 'right text'; |
662 | is $dom->at('ya\.da')->namespace, 'uri:second', 'right namespace'; |
663 | is $dom->at('foo')->namespace, undef, 'no namespace'; |
664 | is $dom->at('[xml\.s]'), undef, 'no result'; |
665 | is $dom->at('b\.z'), undef, 'no result'; |
666 | |
667 | # Yadis |
668 | $dom = DOM::Tiny->new(<<'EOF'); |
669 | <?xml version="1.0" encoding="UTF-8"?> |
670 | <XRDS xmlns="xri://$xrds"> |
671 | <XRD xmlns="xri://$xrd*($v*2.0)"> |
672 | <Service> |
673 | <Type>http://o.r.g/sso/2.0</Type> |
674 | </Service> |
675 | <Service> |
676 | <Type>http://o.r.g/sso/1.0</Type> |
677 | </Service> |
678 | </XRD> |
679 | </XRDS> |
680 | EOF |
681 | ok $dom->xml, 'XML mode detected'; |
682 | is $dom->at('XRDS')->namespace, 'xri://$xrds', 'right namespace'; |
683 | is $dom->at('XRD')->namespace, 'xri://$xrd*($v*2.0)', 'right namespace'; |
684 | my $s = $dom->find('XRDS XRD Service'); |
685 | is $s->[0]->at('Type')->text, 'http://o.r.g/sso/2.0', 'right text'; |
686 | is $s->[0]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace'; |
687 | is $s->[1]->at('Type')->text, 'http://o.r.g/sso/1.0', 'right text'; |
688 | is $s->[1]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace'; |
689 | is $s->[2], undef, 'no result'; |
690 | is $s->size, 2, 'right number of elements'; |
691 | |
692 | # Yadis (roundtrip with namespace) |
693 | my $yadis = <<'EOF'; |
694 | <?xml version="1.0" encoding="UTF-8"?> |
695 | <xrds:XRDS xmlns="xri://$xrd*($v*2.0)" xmlns:xrds="xri://$xrds"> |
696 | <XRD> |
697 | <Service> |
698 | <Type>http://o.r.g/sso/3.0</Type> |
699 | </Service> |
700 | <xrds:Service> |
701 | <Type>http://o.r.g/sso/4.0</Type> |
702 | </xrds:Service> |
703 | </XRD> |
704 | <XRD> |
705 | <Service> |
706 | <Type test="23">http://o.r.g/sso/2.0</Type> |
707 | </Service> |
708 | <Service> |
709 | <Type Test="23" test="24">http://o.r.g/sso/1.0</Type> |
710 | </Service> |
711 | </XRD> |
712 | </xrds:XRDS> |
713 | EOF |
714 | $dom = DOM::Tiny->new($yadis); |
715 | ok $dom->xml, 'XML mode detected'; |
716 | is $dom->at('XRDS')->namespace, 'xri://$xrds', 'right namespace'; |
717 | is $dom->at('XRD')->namespace, 'xri://$xrd*($v*2.0)', 'right namespace'; |
718 | $s = $dom->find('XRDS XRD Service'); |
719 | is $s->[0]->at('Type')->text, 'http://o.r.g/sso/3.0', 'right text'; |
720 | is $s->[0]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace'; |
721 | is $s->[1]->at('Type')->text, 'http://o.r.g/sso/4.0', 'right text'; |
722 | is $s->[1]->namespace, 'xri://$xrds', 'right namespace'; |
723 | is $s->[2]->at('Type')->text, 'http://o.r.g/sso/2.0', 'right text'; |
724 | is $s->[2]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace'; |
725 | is $s->[3]->at('Type')->text, 'http://o.r.g/sso/1.0', 'right text'; |
726 | is $s->[3]->namespace, 'xri://$xrd*($v*2.0)', 'right namespace'; |
727 | is $s->[4], undef, 'no result'; |
728 | is $s->size, 4, 'right number of elements'; |
729 | is $dom->at('[Test="23"]')->text, 'http://o.r.g/sso/1.0', 'right text'; |
730 | is $dom->at('[test="23"]')->text, 'http://o.r.g/sso/2.0', 'right text'; |
731 | is $dom->find('xrds\:Service > Type')->[0]->text, 'http://o.r.g/sso/4.0', |
732 | 'right text'; |
733 | is $dom->find('xrds\:Service > Type')->[1], undef, 'no result'; |
734 | is $dom->find('xrds\3AService > Type')->[0]->text, 'http://o.r.g/sso/4.0', |
735 | 'right text'; |
736 | is $dom->find('xrds\3AService > Type')->[1], undef, 'no result'; |
737 | is $dom->find('xrds\3A Service > Type')->[0]->text, 'http://o.r.g/sso/4.0', |
738 | 'right text'; |
739 | is $dom->find('xrds\3A Service > Type')->[1], undef, 'no result'; |
740 | is $dom->find('xrds\00003AService > Type')->[0]->text, 'http://o.r.g/sso/4.0', |
741 | 'right text'; |
742 | is $dom->find('xrds\00003AService > Type')->[1], undef, 'no result'; |
743 | is $dom->find('xrds\00003A Service > Type')->[0]->text, 'http://o.r.g/sso/4.0', |
744 | 'right text'; |
745 | is $dom->find('xrds\00003A Service > Type')->[1], undef, 'no result'; |
746 | is "$dom", $yadis, 'successful roundtrip'; |
747 | |
748 | # Result and iterator order |
749 | $dom = DOM::Tiny->new('<a><b>1</b></a><b>2</b><b>3</b>'); |
750 | my @numbers; |
751 | $dom->find('b')->each(sub { push @numbers, pop, shift->text }); |
752 | is_deeply \@numbers, [1, 1, 2, 2, 3, 3], 'right order'; |
753 | |
754 | # Attributes on multiple lines |
755 | $dom = DOM::Tiny->new("<div test=23 id='a' \n class='x' foo=bar />"); |
756 | is $dom->at('div.x')->attr('test'), 23, 'right attribute'; |
757 | is $dom->at('[foo="bar"]')->attr('class'), 'x', 'right attribute'; |
758 | is $dom->at('div')->attr(baz => undef)->root->to_string, |
759 | '<div baz class="x" foo="bar" id="a" test="23"></div>', 'right result'; |
760 | |
761 | # Markup characters in attribute values |
762 | $dom = DOM::Tiny->new(qq{<div id="<a>" \n test='='>Test<div id='><' /></div>}); |
763 | is $dom->at('div[id="<a>"]')->attr->{test}, '=', 'right attribute'; |
764 | is $dom->at('[id="<a>"]')->text, 'Test', 'right text'; |
765 | is $dom->at('[id="><"]')->attr->{id}, '><', 'right attribute'; |
766 | |
767 | # Empty attributes |
768 | $dom = DOM::Tiny->new(qq{<div test="" test2='' />}); |
769 | is $dom->at('div')->attr->{test}, '', 'empty attribute value'; |
770 | is $dom->at('div')->attr->{test2}, '', 'empty attribute value'; |
771 | is $dom->at('[test]')->tag, 'div', 'right tag'; |
772 | is $dom->at('[test2]')->tag, 'div', 'right tag'; |
773 | is $dom->at('[test3]'), undef, 'no result'; |
774 | is $dom->at('[test=""]')->tag, 'div', 'right tag'; |
775 | is $dom->at('[test2=""]')->tag, 'div', 'right tag'; |
776 | is $dom->at('[test3=""]'), undef, 'no result'; |
777 | |
927f1351 |
778 | # Multi-line attribute |
779 | $dom = DOM::Tiny->new(qq{<div class="line1\nline2" />}); |
780 | is $dom->at('div')->attr->{class}, "line1\nline2", 'multi-line attribute value'; |
781 | is $dom->at('.line1')->tag, 'div', 'right tag'; |
782 | is $dom->at('.line2')->tag, 'div', 'right tag'; |
783 | is $dom->at('.line3'), undef, 'no result'; |
d6512b50 |
784 | |
785 | # Whitespaces before closing bracket |
786 | $dom = DOM::Tiny->new('<div >content</div>'); |
787 | ok $dom->at('div'), 'tag found'; |
788 | is $dom->at('div')->text, 'content', 'right text'; |
789 | is $dom->at('div')->content, 'content', 'right text'; |
790 | |
791 | # Class with hyphen |
792 | $dom = DOM::Tiny->new('<div class="a">A</div><div class="a-1">A1</div>'); |
793 | @div = (); |
794 | $dom->find('.a')->each(sub { push @div, shift->text }); |
795 | is_deeply \@div, ['A'], 'found first element only'; |
796 | @div = (); |
797 | $dom->find('.a-1')->each(sub { push @div, shift->text }); |
798 | is_deeply \@div, ['A1'], 'found last element only'; |
799 | |
800 | # Defined but false text |
801 | $dom = DOM::Tiny->new( |
802 | '<div><div id="a">A</div><div id="b">B</div></div><div id="0">0</div>'); |
803 | @div = (); |
804 | $dom->find('div[id]')->each(sub { push @div, shift->text }); |
805 | is_deeply \@div, [qw(A B 0)], 'found all div elements with id'; |
806 | |
807 | # Empty tags |
808 | $dom = DOM::Tiny->new('<hr /><br/><br id="br"/><br />'); |
809 | is "$dom", '<hr><br><br id="br"><br>', 'right result'; |
810 | is $dom->at('br')->content, '', 'empty result'; |
811 | |
812 | # Inner XML |
813 | $dom = DOM::Tiny->new('<a>xxx<x>x</x>xxx</a>'); |
814 | is $dom->at('a')->content, 'xxx<x>x</x>xxx', 'right result'; |
815 | is $dom->content, '<a>xxx<x>x</x>xxx</a>', 'right result'; |
816 | |
817 | # Multiple selectors |
818 | $dom = DOM::Tiny->new( |
819 | '<div id="a">A</div><div id="b">B</div><div id="c">C</div><p>D</p>'); |
820 | @div = (); |
821 | $dom->find('p, div')->each(sub { push @div, shift->text }); |
822 | is_deeply \@div, [qw(A B C D)], 'found all elements'; |
823 | @div = (); |
824 | $dom->find('#a, #c')->each(sub { push @div, shift->text }); |
825 | is_deeply \@div, [qw(A C)], 'found all div elements with the right ids'; |
826 | @div = (); |
827 | $dom->find('div#a, div#b')->each(sub { push @div, shift->text }); |
828 | is_deeply \@div, [qw(A B)], 'found all div elements with the right ids'; |
829 | @div = (); |
830 | $dom->find('div[id="a"], div[id="c"]')->each(sub { push @div, shift->text }); |
831 | is_deeply \@div, [qw(A C)], 'found all div elements with the right ids'; |
832 | $dom = DOM::Tiny->new( |
833 | '<div id="☃">A</div><div id="b">B</div><div id="♥x">C</div>'); |
834 | @div = (); |
835 | $dom->find('#☃, #♥x')->each(sub { push @div, shift->text }); |
836 | is_deeply \@div, [qw(A C)], 'found all div elements with the right ids'; |
837 | @div = (); |
838 | $dom->find('div#☃, div#b')->each(sub { push @div, shift->text }); |
839 | is_deeply \@div, [qw(A B)], 'found all div elements with the right ids'; |
840 | @div = (); |
841 | $dom->find('div[id="☃"], div[id="♥x"]') |
842 | ->each(sub { push @div, shift->text }); |
843 | is_deeply \@div, [qw(A C)], 'found all div elements with the right ids'; |
844 | |
845 | # Multiple attributes |
846 | $dom = DOM::Tiny->new(<<EOF); |
847 | <div foo="bar" bar="baz">A</div> |
848 | <div foo="bar">B</div> |
849 | <div foo="bar" bar="baz">C</div> |
850 | <div foo="baz" bar="baz">D</div> |
851 | EOF |
852 | @div = (); |
853 | $dom->find('div[foo="bar"][bar="baz"]')->each(sub { push @div, shift->text }); |
854 | is_deeply \@div, [qw(A C)], 'found all div elements with the right atributes'; |
855 | @div = (); |
856 | $dom->find('div[foo^="b"][foo$="r"]')->each(sub { push @div, shift->text }); |
857 | is_deeply \@div, [qw(A B C)], 'found all div elements with the right atributes'; |
858 | is $dom->at('[foo="bar"]')->previous, undef, 'no previous sibling'; |
859 | is $dom->at('[foo="bar"]')->next->text, 'B', 'right text'; |
860 | is $dom->at('[foo="bar"]')->next->previous->text, 'A', 'right text'; |
861 | is $dom->at('[foo="bar"]')->next->next->next->next, undef, 'no next sibling'; |
862 | |
863 | # Pseudo-classes |
864 | $dom = DOM::Tiny->new(<<EOF); |
865 | <form action="/foo"> |
866 | <input type="text" name="user" value="test" /> |
867 | <input type="checkbox" checked="checked" name="groovy"> |
868 | <select name="a"> |
869 | <option value="b">b</option> |
870 | <optgroup label="c"> |
871 | <option value="d">d</option> |
872 | <option selected="selected" value="e">E</option> |
873 | <option value="f">f</option> |
874 | </optgroup> |
875 | <option value="g">g</option> |
876 | <option selected value="h">H</option> |
877 | </select> |
878 | <input type="submit" value="Ok!" /> |
879 | <input type="checkbox" checked name="I"> |
880 | <p id="content">test 123</p> |
881 | <p id="no_content"><? test ?><!-- 123 --></p> |
882 | </form> |
883 | EOF |
884 | is $dom->find(':root')->[0]->tag, 'form', 'right tag'; |
885 | is $dom->find('*:root')->[0]->tag, 'form', 'right tag'; |
886 | is $dom->find('form:root')->[0]->tag, 'form', 'right tag'; |
887 | is $dom->find(':root')->[1], undef, 'no result'; |
888 | is $dom->find(':checked')->[0]->attr->{name}, 'groovy', 'right name'; |
889 | is $dom->find('option:checked')->[0]->attr->{value}, 'e', 'right value'; |
890 | is $dom->find(':checked')->[1]->text, 'E', 'right text'; |
891 | is $dom->find('*:checked')->[1]->text, 'E', 'right text'; |
892 | is $dom->find(':checked')->[2]->text, 'H', 'right name'; |
893 | is $dom->find(':checked')->[3]->attr->{name}, 'I', 'right name'; |
894 | is $dom->find(':checked')->[4], undef, 'no result'; |
895 | is $dom->find('option[selected]')->[0]->attr->{value}, 'e', 'right value'; |
896 | is $dom->find('option[selected]')->[1]->text, 'H', 'right text'; |
897 | is $dom->find('option[selected]')->[2], undef, 'no result'; |
898 | is $dom->find(':checked[value="e"]')->[0]->text, 'E', 'right text'; |
899 | is $dom->find('*:checked[value="e"]')->[0]->text, 'E', 'right text'; |
900 | is $dom->find('option:checked[value="e"]')->[0]->text, 'E', 'right text'; |
901 | is $dom->at('optgroup option:checked[value="e"]')->text, 'E', 'right text'; |
902 | is $dom->at('select option:checked[value="e"]')->text, 'E', 'right text'; |
903 | is $dom->at('select :checked[value="e"]')->text, 'E', 'right text'; |
904 | is $dom->at('optgroup > :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->find(':checked[value="e"]')->[1], undef, 'no result'; |
908 | is $dom->find(':empty')->[0]->attr->{name}, 'user', 'right name'; |
909 | is $dom->find('input:empty')->[0]->attr->{name}, 'user', 'right name'; |
910 | is $dom->at(':empty[type^="ch"]')->attr->{name}, 'groovy', 'right name'; |
911 | is $dom->at('p')->attr->{id}, 'content', 'right attribute'; |
912 | is $dom->at('p:empty')->attr->{id}, 'no_content', 'right attribute'; |
913 | |
914 | # More pseudo-classes |
915 | $dom = DOM::Tiny->new(<<EOF); |
916 | <ul> |
917 | <li>A</li> |
918 | <li>B</li> |
919 | <li>C</li> |
920 | <li>D</li> |
921 | <li>E</li> |
922 | <li>F</li> |
923 | <li>G</li> |
924 | <li>H</li> |
925 | </ul> |
926 | EOF |
927 | my @li; |
928 | $dom->find('li:nth-child(odd)')->each(sub { push @li, shift->text }); |
929 | is_deeply \@li, [qw(A C E G)], 'found all odd li elements'; |
930 | @li = (); |
931 | $dom->find('li:NTH-CHILD(ODD)')->each(sub { push @li, shift->text }); |
932 | is_deeply \@li, [qw(A C E G)], 'found all odd li elements'; |
933 | @li = (); |
934 | $dom->find('li:nth-last-child(odd)')->each(sub { push @li, shift->text }); |
935 | is_deeply \@li, [qw(B D F H)], 'found all odd li elements'; |
936 | is $dom->find(':nth-child(odd)')->[0]->tag, 'ul', 'right tag'; |
937 | is $dom->find(':nth-child(odd)')->[1]->text, 'A', 'right text'; |
938 | is $dom->find(':nth-child(1)')->[0]->tag, 'ul', 'right tag'; |
939 | is $dom->find(':nth-child(1)')->[1]->text, 'A', 'right text'; |
940 | is $dom->find(':nth-last-child(odd)')->[0]->tag, 'ul', 'right tag'; |
941 | is $dom->find(':nth-last-child(odd)')->last->text, 'H', 'right text'; |
942 | is $dom->find(':nth-last-child(1)')->[0]->tag, 'ul', 'right tag'; |
943 | is $dom->find(':nth-last-child(1)')->[1]->text, 'H', 'right text'; |
944 | @li = (); |
945 | $dom->find('li:nth-child(2n+1)')->each(sub { push @li, shift->text }); |
946 | is_deeply \@li, [qw(A C E G)], 'found all odd li elements'; |
947 | @li = (); |
948 | $dom->find('li:nth-child(2n + 1)')->each(sub { push @li, shift->text }); |
949 | is_deeply \@li, [qw(A C E G)], 'found all odd li elements'; |
950 | @li = (); |
951 | $dom->find('li:nth-last-child(2n+1)')->each(sub { push @li, shift->text }); |
952 | is_deeply \@li, [qw(B D F H)], 'found all odd li elements'; |
953 | @li = (); |
954 | $dom->find('li:nth-child(even)')->each(sub { push @li, shift->text }); |
955 | is_deeply \@li, [qw(B D F H)], 'found all even li elements'; |
956 | @li = (); |
957 | $dom->find('li:NTH-CHILD(EVEN)')->each(sub { push @li, shift->text }); |
958 | is_deeply \@li, [qw(B D F H)], 'found all even li elements'; |
959 | @li = (); |
960 | $dom->find('li:nth-last-child( even )')->each(sub { push @li, shift->text }); |
961 | is_deeply \@li, [qw(A C E G)], 'found all even li elements'; |
962 | @li = (); |
963 | $dom->find('li:nth-child(2n+2)')->each(sub { push @li, shift->text }); |
964 | is_deeply \@li, [qw(B D F H)], 'found all even li elements'; |
965 | @li = (); |
966 | $dom->find('li:nTh-chILd(2N+2)')->each(sub { push @li, shift->text }); |
967 | is_deeply \@li, [qw(B D F H)], 'found all even li elements'; |
968 | @li = (); |
969 | $dom->find('li:nth-child( 2n + 2 )')->each(sub { push @li, shift->text }); |
970 | is_deeply \@li, [qw(B D F H)], 'found all even li elements'; |
971 | @li = (); |
972 | $dom->find('li:nth-last-child(2n+2)')->each(sub { push @li, shift->text }); |
973 | is_deeply \@li, [qw(A C E G)], 'found all even li elements'; |
974 | @li = (); |
975 | $dom->find('li:nth-child(4n+1)')->each(sub { push @li, shift->text }); |
976 | is_deeply \@li, [qw(A E)], 'found the right li elements'; |
977 | @li = (); |
978 | $dom->find('li:nth-last-child(4n+1)')->each(sub { push @li, shift->text }); |
979 | is_deeply \@li, [qw(D H)], 'found the right li elements'; |
980 | @li = (); |
981 | $dom->find('li:nth-child(4n+4)')->each(sub { push @li, shift->text }); |
982 | is_deeply \@li, [qw(D H)], 'found the right li element'; |
983 | @li = (); |
984 | $dom->find('li:nth-last-child(4n+4)')->each(sub { push @li, shift->text }); |
985 | is_deeply \@li, [qw(A E)], 'found the right li element'; |
986 | @li = (); |
987 | $dom->find('li:nth-child(4n)')->each(sub { push @li, shift->text }); |
988 | is_deeply \@li, [qw(D H)], 'found the right li element'; |
989 | @li = (); |
990 | $dom->find('li:nth-child( 4n )')->each(sub { push @li, shift->text }); |
991 | is_deeply \@li, [qw(D H)], 'found the right li element'; |
992 | @li = (); |
993 | $dom->find('li:nth-last-child(4n)')->each(sub { push @li, shift->text }); |
994 | is_deeply \@li, [qw(A E)], 'found the right li element'; |
995 | @li = (); |
996 | $dom->find('li:nth-child(5n-2)')->each(sub { push @li, shift->text }); |
997 | is_deeply \@li, [qw(C H)], 'found the right li element'; |
998 | @li = (); |
999 | $dom->find('li:nth-child( 5n - 2 )')->each(sub { push @li, shift->text }); |
1000 | is_deeply \@li, [qw(C H)], 'found the right li element'; |
1001 | @li = (); |
1002 | $dom->find('li:nth-last-child(5n-2)')->each(sub { push @li, shift->text }); |
1003 | is_deeply \@li, [qw(A F)], 'found the right li element'; |
1004 | @li = (); |
1005 | $dom->find('li:nth-child(-n+3)')->each(sub { push @li, shift->text }); |
1006 | is_deeply \@li, [qw(A B C)], 'found first three li elements'; |
1007 | @li = (); |
1008 | $dom->find('li:nth-child( -n + 3 )')->each(sub { push @li, shift->text }); |
1009 | is_deeply \@li, [qw(A B C)], 'found first three li elements'; |
1010 | @li = (); |
1011 | $dom->find('li:nth-last-child(-n+3)')->each(sub { push @li, shift->text }); |
1012 | is_deeply \@li, [qw(F G H)], 'found last three li elements'; |
1013 | @li = (); |
1014 | $dom->find('li:nth-child(-1n+3)')->each(sub { push @li, shift->text }); |
1015 | is_deeply \@li, [qw(A B C)], 'found first three li elements'; |
1016 | @li = (); |
1017 | $dom->find('li:nth-last-child(-1n+3)')->each(sub { push @li, shift->text }); |
1018 | is_deeply \@li, [qw(F G H)], 'found first three li elements'; |
1019 | @li = (); |
1020 | $dom->find('li:nth-child(3n)')->each(sub { push @li, shift->text }); |
1021 | is_deeply \@li, [qw(C F)], 'found every third li elements'; |
1022 | @li = (); |
1023 | $dom->find('li:nth-last-child(3n)')->each(sub { push @li, shift->text }); |
1024 | is_deeply \@li, [qw(C F)], 'found every third li elements'; |
1025 | @li = (); |
1026 | $dom->find('li:NTH-LAST-CHILD(3N)')->each(sub { push @li, shift->text }); |
1027 | is_deeply \@li, [qw(C F)], 'found every third li elements'; |
1028 | @li = (); |
1029 | $dom->find('li:Nth-Last-Child(3N)')->each(sub { push @li, shift->text }); |
1030 | is_deeply \@li, [qw(C F)], 'found every third li elements'; |
1031 | @li = (); |
1032 | $dom->find('li:nth-child(3)')->each(sub { push @li, shift->text }); |
1033 | is_deeply \@li, ['C'], 'found third li element'; |
1034 | @li = (); |
1035 | $dom->find('li:nth-last-child(3)')->each(sub { push @li, shift->text }); |
1036 | is_deeply \@li, ['F'], 'found third last li element'; |
1037 | @li = (); |
1038 | $dom->find('li:nth-child(1n+0)')->each(sub { push @li, shift->text }); |
1039 | is_deeply \@li, [qw(A B C D E F G)], 'found first three li elements'; |
1040 | @li = (); |
1041 | $dom->find('li:nth-child(n+0)')->each(sub { push @li, shift->text }); |
1042 | is_deeply \@li, [qw(A B C D E F G)], 'found first three li elements'; |
1043 | @li = (); |
1044 | $dom->find('li:NTH-CHILD(N+0)')->each(sub { push @li, shift->text }); |
1045 | is_deeply \@li, [qw(A B C D E F G)], 'found first three li elements'; |
1046 | @li = (); |
1047 | $dom->find('li:Nth-Child(N+0)')->each(sub { push @li, shift->text }); |
1048 | is_deeply \@li, [qw(A B C D E F G)], 'found first three li elements'; |
1049 | @li = (); |
1050 | $dom->find('li:nth-child(n)')->each(sub { push @li, shift->text }); |
1051 | is_deeply \@li, [qw(A B C D E F G)], 'found first three li elements'; |
1052 | |
1053 | # Even more pseudo-classes |
1054 | $dom = DOM::Tiny->new(<<EOF); |
1055 | <ul> |
1056 | <li>A</li> |
1057 | <p>B</p> |
1058 | <li class="test ♥">C</li> |
1059 | <p>D</p> |
1060 | <li>E</li> |
1061 | <li>F</li> |
1062 | <p>G</p> |
1063 | <li>H</li> |
1064 | <li>I</li> |
1065 | </ul> |
1066 | <div> |
1067 | <div class="☃">J</div> |
1068 | </div> |
1069 | <div> |
1070 | <a href="http://www.w3.org/DOM/">DOM</a> |
1071 | <div class="☃">K</div> |
1072 | <a href="http://www.w3.org/DOM/">DOM</a> |
1073 | </div> |
1074 | EOF |
1075 | my @e; |
1076 | $dom->find('ul :nth-child(odd)')->each(sub { push @e, shift->text }); |
1077 | is_deeply \@e, [qw(A C E G I)], 'found all odd elements'; |
1078 | @e = (); |
1079 | $dom->find('li:nth-of-type(odd)')->each(sub { push @e, shift->text }); |
1080 | is_deeply \@e, [qw(A E H)], 'found all odd li elements'; |
1081 | @e = (); |
1082 | $dom->find('li:nth-last-of-type( odd )')->each(sub { push @e, shift->text }); |
1083 | is_deeply \@e, [qw(C F I)], 'found all odd li elements'; |
1084 | @e = (); |
1085 | $dom->find('p:nth-of-type(odd)')->each(sub { push @e, shift->text }); |
1086 | is_deeply \@e, [qw(B G)], 'found all odd p elements'; |
1087 | @e = (); |
1088 | $dom->find('p:nth-last-of-type(odd)')->each(sub { push @e, shift->text }); |
1089 | is_deeply \@e, [qw(B G)], 'found all odd li elements'; |
1090 | @e = (); |
1091 | $dom->find('ul :nth-child(1)')->each(sub { push @e, shift->text }); |
1092 | is_deeply \@e, ['A'], 'found first child'; |
1093 | @e = (); |
1094 | $dom->find('ul :first-child')->each(sub { push @e, shift->text }); |
1095 | is_deeply \@e, ['A'], 'found first child'; |
1096 | @e = (); |
1097 | $dom->find('p:nth-of-type(1)')->each(sub { push @e, shift->text }); |
1098 | is_deeply \@e, ['B'], 'found first child'; |
1099 | @e = (); |
1100 | $dom->find('p:first-of-type')->each(sub { push @e, shift->text }); |
1101 | is_deeply \@e, ['B'], 'found first child'; |
1102 | @e = (); |
1103 | $dom->find('li:nth-of-type(1)')->each(sub { push @e, shift->text }); |
1104 | is_deeply \@e, ['A'], 'found first child'; |
1105 | @e = (); |
1106 | $dom->find('li:first-of-type')->each(sub { push @e, shift->text }); |
1107 | is_deeply \@e, ['A'], 'found first child'; |
1108 | @e = (); |
1109 | $dom->find('ul :nth-last-child(-n+1)')->each(sub { push @e, shift->text }); |
1110 | is_deeply \@e, ['I'], 'found last child'; |
1111 | @e = (); |
1112 | $dom->find('ul :last-child')->each(sub { push @e, shift->text }); |
1113 | is_deeply \@e, ['I'], 'found last child'; |
1114 | @e = (); |
1115 | $dom->find('p:nth-last-of-type(-n+1)')->each(sub { push @e, shift->text }); |
1116 | is_deeply \@e, ['G'], 'found last child'; |
1117 | @e = (); |
1118 | $dom->find('p:last-of-type')->each(sub { push @e, shift->text }); |
1119 | is_deeply \@e, ['G'], 'found last child'; |
1120 | @e = (); |
1121 | $dom->find('li:nth-last-of-type(-n+1)')->each(sub { push @e, shift->text }); |
1122 | is_deeply \@e, ['I'], 'found last child'; |
1123 | @e = (); |
1124 | $dom->find('li:last-of-type')->each(sub { push @e, shift->text }); |
1125 | is_deeply \@e, ['I'], 'found last child'; |
1126 | @e = (); |
1127 | $dom->find('ul :nth-child(-n+3):not(li)')->each(sub { push @e, shift->text }); |
1128 | is_deeply \@e, ['B'], 'found first p element'; |
1129 | @e = (); |
1130 | $dom->find('ul :nth-child(-n+3):not(:first-child)') |
1131 | ->each(sub { push @e, shift->text }); |
1132 | is_deeply \@e, [qw(B C)], 'found second and third element'; |
1133 | @e = (); |
1134 | $dom->find('ul :nth-child(-n+3):not(.♥)')->each(sub { push @e, shift->text }); |
1135 | is_deeply \@e, [qw(A B)], 'found first and second element'; |
1136 | @e = (); |
1137 | $dom->find('ul :nth-child(-n+3):not([class$="♥"])') |
1138 | ->each(sub { push @e, shift->text }); |
1139 | is_deeply \@e, [qw(A B)], 'found first and second element'; |
1140 | @e = (); |
1141 | $dom->find('ul :nth-child(-n+3):not(li[class$="♥"])') |
1142 | ->each(sub { push @e, shift->text }); |
1143 | is_deeply \@e, [qw(A B)], 'found first and second element'; |
1144 | @e = (); |
1145 | $dom->find('ul :nth-child(-n+3):not([class$="♥"][class^="test"])') |
1146 | ->each(sub { push @e, shift->text }); |
1147 | is_deeply \@e, [qw(A B)], 'found first and second element'; |
1148 | @e = (); |
1149 | $dom->find('ul :nth-child(-n+3):not(*[class$="♥"])') |
1150 | ->each(sub { push @e, shift->text }); |
1151 | is_deeply \@e, [qw(A B)], 'found first and second element'; |
1152 | @e = (); |
1153 | $dom->find('ul :nth-child(-n+3):not(:nth-child(-n+2))') |
1154 | ->each(sub { push @e, shift->text }); |
1155 | is_deeply \@e, ['C'], 'found third element'; |
1156 | @e = (); |
1157 | $dom->find('ul :nth-child(-n+3):not(:nth-child(1)):not(:nth-child(2))') |
1158 | ->each(sub { push @e, shift->text }); |
1159 | is_deeply \@e, ['C'], 'found third element'; |
1160 | @e = (); |
1161 | $dom->find(':only-child')->each(sub { push @e, shift->text }); |
1162 | is_deeply \@e, ['J'], 'found only child'; |
1163 | @e = (); |
1164 | $dom->find('div :only-of-type')->each(sub { push @e, shift->text }); |
1165 | is_deeply \@e, [qw(J K)], 'found only child'; |
1166 | @e = (); |
1167 | $dom->find('div:only-child')->each(sub { push @e, shift->text }); |
1168 | is_deeply \@e, ['J'], 'found only child'; |
1169 | @e = (); |
1170 | $dom->find('div div:only-of-type')->each(sub { push @e, shift->text }); |
1171 | is_deeply \@e, [qw(J K)], 'found only child'; |
1172 | |
1173 | # Sibling combinator |
1174 | $dom = DOM::Tiny->new(<<EOF); |
1175 | <ul> |
1176 | <li>A</li> |
1177 | <p>B</p> |
1178 | <li>C</li> |
1179 | </ul> |
1180 | <h1>D</h1> |
1181 | <p id="♥">E</p> |
1182 | <p id="☃">F<b>H</b></p> |
1183 | <div>G</div> |
1184 | EOF |
1185 | is $dom->at('li ~ p')->text, 'B', 'right text'; |
1186 | is $dom->at('li + p')->text, 'B', 'right text'; |
1187 | is $dom->at('h1 ~ p ~ p')->text, 'F', 'right text'; |
1188 | is $dom->at('h1 + p ~ p')->text, 'F', 'right text'; |
1189 | is $dom->at('h1 ~ p + p')->text, 'F', 'right text'; |
1190 | is $dom->at('h1 + p + p')->text, 'F', 'right text'; |
1191 | is $dom->at('h1 + p+p')->text, 'F', 'right text'; |
1192 | is $dom->at('ul > li ~ li')->text, 'C', 'right text'; |
1193 | is $dom->at('ul li ~ li')->text, 'C', 'right text'; |
1194 | is $dom->at('ul>li~li')->text, 'C', 'right text'; |
1195 | is $dom->at('ul li li'), undef, 'no result'; |
1196 | is $dom->at('ul ~ li ~ li'), undef, 'no result'; |
1197 | is $dom->at('ul + li ~ li'), undef, 'no result'; |
1198 | is $dom->at('ul > li + li'), undef, 'no result'; |
1199 | is $dom->at('h1 ~ div')->text, 'G', 'right text'; |
1200 | is $dom->at('h1 + div'), undef, 'no result'; |
1201 | is $dom->at('p + div')->text, 'G', 'right text'; |
1202 | is $dom->at('ul + h1 + p + p + div')->text, 'G', 'right text'; |
1203 | is $dom->at('ul + h1 ~ p + div')->text, 'G', 'right text'; |
1204 | is $dom->at('h1 ~ #♥')->text, 'E', 'right text'; |
1205 | is $dom->at('h1 + #♥')->text, 'E', 'right text'; |
1206 | is $dom->at('#♥~#☃')->text, 'F', 'right text'; |
1207 | is $dom->at('#♥+#☃')->text, 'F', 'right text'; |
1208 | is $dom->at('#♥+#☃>b')->text, 'H', 'right text'; |
1209 | is $dom->at('#♥ > #☃'), undef, 'no result'; |
1210 | is $dom->at('#♥ #☃'), undef, 'no result'; |
1211 | is $dom->at('#♥ + #☃ + :nth-last-child(1)')->text, 'G', 'right text'; |
1212 | is $dom->at('#♥ ~ #☃ + :nth-last-child(1)')->text, 'G', 'right text'; |
1213 | is $dom->at('#♥ + #☃ ~ :nth-last-child(1)')->text, 'G', 'right text'; |
1214 | is $dom->at('#♥ ~ #☃ ~ :nth-last-child(1)')->text, 'G', 'right text'; |
1215 | is $dom->at('#♥ + :nth-last-child(2)')->text, 'F', 'right text'; |
1216 | is $dom->at('#♥ ~ :nth-last-child(2)')->text, 'F', 'right text'; |
1217 | is $dom->at('#♥ + #☃ + *:nth-last-child(1)')->text, 'G', 'right text'; |
1218 | is $dom->at('#♥ ~ #☃ + *:nth-last-child(1)')->text, 'G', 'right text'; |
1219 | is $dom->at('#♥ + #☃ ~ *:nth-last-child(1)')->text, 'G', 'right text'; |
1220 | is $dom->at('#♥ ~ #☃ ~ *:nth-last-child(1)')->text, 'G', 'right text'; |
1221 | is $dom->at('#♥ + *:nth-last-child(2)')->text, 'F', 'right text'; |
1222 | is $dom->at('#♥ ~ *:nth-last-child(2)')->text, 'F', 'right text'; |
1223 | |
1224 | # Adding nodes |
1225 | $dom = DOM::Tiny->new(<<EOF); |
1226 | <ul> |
1227 | <li>A</li> |
1228 | <p>B</p> |
1229 | <li>C</li> |
1230 | </ul> |
1231 | <div>D</div> |
1232 | EOF |
1233 | $dom->at('li')->append('<p>A1</p>23'); |
1234 | is "$dom", <<EOF, 'right result'; |
1235 | <ul> |
1236 | <li>A</li><p>A1</p>23 |
1237 | <p>B</p> |
1238 | <li>C</li> |
1239 | </ul> |
1240 | <div>D</div> |
1241 | EOF |
1242 | $dom->at('li')->prepend('24')->prepend('<div>A-1</div>25'); |
1243 | is "$dom", <<EOF, 'right result'; |
1244 | <ul> |
1245 | 24<div>A-1</div>25<li>A</li><p>A1</p>23 |
1246 | <p>B</p> |
1247 | <li>C</li> |
1248 | </ul> |
1249 | <div>D</div> |
1250 | EOF |
1251 | is $dom->at('div')->text, 'A-1', 'right text'; |
1252 | is $dom->at('iv'), undef, 'no result'; |
1253 | $dom->prepend('l')->prepend('alal')->prepend('a'); |
1254 | is "$dom", <<EOF, 'no change'; |
1255 | <ul> |
1256 | 24<div>A-1</div>25<li>A</li><p>A1</p>23 |
1257 | <p>B</p> |
1258 | <li>C</li> |
1259 | </ul> |
1260 | <div>D</div> |
1261 | EOF |
1262 | $dom->append('lalala'); |
1263 | is "$dom", <<EOF, 'no change'; |
1264 | <ul> |
1265 | 24<div>A-1</div>25<li>A</li><p>A1</p>23 |
1266 | <p>B</p> |
1267 | <li>C</li> |
1268 | </ul> |
1269 | <div>D</div> |
1270 | EOF |
1271 | $dom->find('div')->each(sub { shift->append('works') }); |
1272 | is "$dom", <<EOF, 'right result'; |
1273 | <ul> |
1274 | 24<div>A-1</div>works25<li>A</li><p>A1</p>23 |
1275 | <p>B</p> |
1276 | <li>C</li> |
1277 | </ul> |
1278 | <div>D</div>works |
1279 | EOF |
1280 | $dom->at('li')->prepend_content('A3<p>A2</p>')->prepend_content('A4'); |
1281 | is $dom->at('li')->text, 'A4A3 A', 'right text'; |
1282 | is "$dom", <<EOF, 'right result'; |
1283 | <ul> |
1284 | 24<div>A-1</div>works25<li>A4A3<p>A2</p>A</li><p>A1</p>23 |
1285 | <p>B</p> |
1286 | <li>C</li> |
1287 | </ul> |
1288 | <div>D</div>works |
1289 | EOF |
1290 | $dom->find('li')->[1]->append_content('<p>C2</p>C3')->append_content(' C4') |
1291 | ->append_content('C5'); |
1292 | is $dom->find('li')->[1]->text, 'C C3 C4C5', 'right text'; |
1293 | is "$dom", <<EOF, 'right result'; |
1294 | <ul> |
1295 | 24<div>A-1</div>works25<li>A4A3<p>A2</p>A</li><p>A1</p>23 |
1296 | <p>B</p> |
1297 | <li>C<p>C2</p>C3 C4C5</li> |
1298 | </ul> |
1299 | <div>D</div>works |
1300 | EOF |
1301 | |
1302 | # Optional "head" and "body" tags |
1303 | $dom = DOM::Tiny->new(<<EOF); |
1304 | <html> |
1305 | <head> |
1306 | <title>foo</title> |
1307 | <body>bar |
1308 | EOF |
1309 | is $dom->at('html > head > title')->text, 'foo', 'right text'; |
1310 | is $dom->at('html > body')->text, 'bar', 'right text'; |
1311 | |
1312 | # Optional "li" tag |
1313 | $dom = DOM::Tiny->new(<<EOF); |
1314 | <ul> |
1315 | <li> |
1316 | <ol> |
1317 | <li>F |
1318 | <li>G |
1319 | </ol> |
1320 | <li>A</li> |
1321 | <LI>B |
1322 | <li>C</li> |
1323 | <li>D |
1324 | <li>E |
1325 | </ul> |
1326 | EOF |
1327 | is $dom->find('ul > li > ol > li')->[0]->text, 'F', 'right text'; |
1328 | is $dom->find('ul > li > ol > li')->[1]->text, 'G', 'right text'; |
1329 | is $dom->find('ul > li')->[1]->text, 'A', 'right text'; |
1330 | is $dom->find('ul > li')->[2]->text, 'B', 'right text'; |
1331 | is $dom->find('ul > li')->[3]->text, 'C', 'right text'; |
1332 | is $dom->find('ul > li')->[4]->text, 'D', 'right text'; |
1333 | is $dom->find('ul > li')->[5]->text, 'E', 'right text'; |
1334 | |
1335 | # Optional "p" tag |
1336 | $dom = DOM::Tiny->new(<<EOF); |
1337 | <div> |
1338 | <p>A</p> |
1339 | <P>B |
1340 | <p>C</p> |
1341 | <p>D<div>X</div> |
1342 | <p>E<img src="foo.png"> |
1343 | <p>F<br>G |
1344 | <p>H |
1345 | </div> |
1346 | EOF |
1347 | is $dom->find('div > p')->[0]->text, 'A', 'right text'; |
1348 | is $dom->find('div > p')->[1]->text, 'B', 'right text'; |
1349 | is $dom->find('div > p')->[2]->text, 'C', 'right text'; |
1350 | is $dom->find('div > p')->[3]->text, 'D', 'right text'; |
1351 | is $dom->find('div > p')->[4]->text, 'E', 'right text'; |
1352 | is $dom->find('div > p')->[5]->text, 'F G', 'right text'; |
1353 | is $dom->find('div > p')->[6]->text, 'H', 'right text'; |
1354 | is $dom->find('div > p > p')->[0], undef, 'no results'; |
1355 | is $dom->at('div > p > img')->attr->{src}, 'foo.png', 'right attribute'; |
1356 | is $dom->at('div > div')->text, 'X', 'right text'; |
1357 | |
1358 | # Optional "dt" and "dd" tags |
1359 | $dom = DOM::Tiny->new(<<EOF); |
1360 | <dl> |
1361 | <dt>A</dt> |
1362 | <DD>B |
1363 | <dt>C</dt> |
1364 | <dd>D |
1365 | <dt>E |
1366 | <dd>F |
1367 | </dl> |
1368 | EOF |
1369 | is $dom->find('dl > dt')->[0]->text, 'A', 'right text'; |
1370 | is $dom->find('dl > dd')->[0]->text, 'B', 'right text'; |
1371 | is $dom->find('dl > dt')->[1]->text, 'C', 'right text'; |
1372 | is $dom->find('dl > dd')->[1]->text, 'D', 'right text'; |
1373 | is $dom->find('dl > dt')->[2]->text, 'E', 'right text'; |
1374 | is $dom->find('dl > dd')->[2]->text, 'F', 'right text'; |
1375 | |
1376 | # Optional "rp" and "rt" tags |
1377 | $dom = DOM::Tiny->new(<<EOF); |
1378 | <ruby> |
1379 | <rp>A</rp> |
1380 | <RT>B |
1381 | <rp>C</rp> |
1382 | <rt>D |
1383 | <rp>E |
1384 | <rt>F |
1385 | </ruby> |
1386 | EOF |
1387 | is $dom->find('ruby > rp')->[0]->text, 'A', 'right text'; |
1388 | is $dom->find('ruby > rt')->[0]->text, 'B', 'right text'; |
1389 | is $dom->find('ruby > rp')->[1]->text, 'C', 'right text'; |
1390 | is $dom->find('ruby > rt')->[1]->text, 'D', 'right text'; |
1391 | is $dom->find('ruby > rp')->[2]->text, 'E', 'right text'; |
1392 | is $dom->find('ruby > rt')->[2]->text, 'F', 'right text'; |
1393 | |
1394 | # Optional "optgroup" and "option" tags |
1395 | $dom = DOM::Tiny->new(<<EOF); |
1396 | <div> |
1397 | <optgroup>A |
1398 | <option id="foo">B |
1399 | <option>C</option> |
1400 | <option>D |
1401 | <OPTGROUP>E |
1402 | <option>F |
1403 | <optgroup>G |
1404 | <option>H |
1405 | </div> |
1406 | EOF |
1407 | is $dom->find('div > optgroup')->[0]->text, 'A', 'right text'; |
1408 | is $dom->find('div > optgroup > #foo')->[0]->text, 'B', 'right text'; |
1409 | is $dom->find('div > optgroup > option')->[1]->text, 'C', 'right text'; |
1410 | is $dom->find('div > optgroup > option')->[2]->text, 'D', 'right text'; |
1411 | is $dom->find('div > optgroup')->[1]->text, 'E', 'right text'; |
1412 | is $dom->find('div > optgroup > option')->[3]->text, 'F', 'right text'; |
1413 | is $dom->find('div > optgroup')->[2]->text, 'G', 'right text'; |
1414 | is $dom->find('div > optgroup > option')->[4]->text, 'H', 'right text'; |
1415 | |
1416 | # Optional "colgroup" tag |
1417 | $dom = DOM::Tiny->new(<<EOF); |
1418 | <table> |
1419 | <col id=morefail> |
1420 | <col id=fail> |
1421 | <colgroup> |
1422 | <col id=foo> |
1423 | <col class=foo> |
1424 | <colgroup> |
1425 | <col id=bar> |
1426 | </table> |
1427 | EOF |
1428 | is $dom->find('table > col')->[0]->attr->{id}, 'morefail', 'right attribute'; |
1429 | is $dom->find('table > col')->[1]->attr->{id}, 'fail', 'right attribute'; |
1430 | is $dom->find('table > colgroup > col')->[0]->attr->{id}, 'foo', |
1431 | 'right attribute'; |
1432 | is $dom->find('table > colgroup > col')->[1]->attr->{class}, 'foo', |
1433 | 'right attribute'; |
1434 | is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar', |
1435 | 'right attribute'; |
1436 | |
1437 | # Optional "thead", "tbody", "tfoot", "tr", "th" and "td" tags |
1438 | $dom = DOM::Tiny->new(<<EOF); |
1439 | <table> |
1440 | <thead> |
1441 | <tr> |
1442 | <th>A</th> |
1443 | <th>D |
1444 | <tfoot> |
1445 | <tr> |
1446 | <td>C |
1447 | <tbody> |
1448 | <tr> |
1449 | <td>B |
1450 | </table> |
1451 | EOF |
1452 | is $dom->at('table > thead > tr > th')->text, 'A', 'right text'; |
1453 | is $dom->find('table > thead > tr > th')->[1]->text, 'D', 'right text'; |
1454 | is $dom->at('table > tbody > tr > td')->text, 'B', 'right text'; |
1455 | is $dom->at('table > tfoot > tr > td')->text, 'C', 'right text'; |
1456 | |
1457 | # Optional "colgroup", "thead", "tbody", "tr", "th" and "td" tags |
1458 | $dom = DOM::Tiny->new(<<EOF); |
1459 | <table> |
1460 | <col id=morefail> |
1461 | <col id=fail> |
1462 | <colgroup> |
1463 | <col id=foo /> |
1464 | <col class=foo> |
1465 | <colgroup> |
1466 | <col id=bar> |
1467 | </colgroup> |
1468 | <thead> |
1469 | <tr> |
1470 | <th>A</th> |
1471 | <th>D |
1472 | <tbody> |
1473 | <tr> |
1474 | <td>B |
1475 | <tbody> |
1476 | <tr> |
1477 | <td>E |
1478 | </table> |
1479 | EOF |
1480 | is $dom->find('table > col')->[0]->attr->{id}, 'morefail', 'right attribute'; |
1481 | is $dom->find('table > col')->[1]->attr->{id}, 'fail', 'right attribute'; |
1482 | is $dom->find('table > colgroup > col')->[0]->attr->{id}, 'foo', |
1483 | 'right attribute'; |
1484 | is $dom->find('table > colgroup > col')->[1]->attr->{class}, 'foo', |
1485 | 'right attribute'; |
1486 | is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar', |
1487 | 'right attribute'; |
1488 | is $dom->at('table > thead > tr > th')->text, 'A', 'right text'; |
1489 | is $dom->find('table > thead > tr > th')->[1]->text, 'D', 'right text'; |
1490 | is $dom->at('table > tbody > tr > td')->text, 'B', 'right text'; |
1491 | is $dom->find('table > tbody > tr > td')->map('text')->join("\n"), "B\nE", |
1492 | 'right text'; |
1493 | |
1494 | # Optional "colgroup", "tbody", "tr", "th" and "td" tags |
1495 | $dom = DOM::Tiny->new(<<EOF); |
1496 | <table> |
1497 | <colgroup> |
1498 | <col id=foo /> |
1499 | <col class=foo> |
1500 | <colgroup> |
1501 | <col id=bar> |
1502 | </colgroup> |
1503 | <tbody> |
1504 | <tr> |
1505 | <td>B |
1506 | </table> |
1507 | EOF |
1508 | is $dom->find('table > colgroup > col')->[0]->attr->{id}, 'foo', |
1509 | 'right attribute'; |
1510 | is $dom->find('table > colgroup > col')->[1]->attr->{class}, 'foo', |
1511 | 'right attribute'; |
1512 | is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar', |
1513 | 'right attribute'; |
1514 | is $dom->at('table > tbody > tr > td')->text, 'B', 'right text'; |
1515 | |
1516 | # Optional "tr" and "td" tags |
1517 | $dom = DOM::Tiny->new(<<EOF); |
1518 | <table> |
1519 | <tr> |
1520 | <td>A |
1521 | <td>B</td> |
1522 | <tr> |
1523 | <td>C |
1524 | </tr> |
1525 | <tr> |
1526 | <td>D |
1527 | </table> |
1528 | EOF |
1529 | is $dom->find('table > tr > td')->[0]->text, 'A', 'right text'; |
1530 | is $dom->find('table > tr > td')->[1]->text, 'B', 'right text'; |
1531 | is $dom->find('table > tr > td')->[2]->text, 'C', 'right text'; |
1532 | is $dom->find('table > tr > td')->[3]->text, 'D', 'right text'; |
1533 | |
1534 | # Real world table |
1535 | $dom = DOM::Tiny->new(<<EOF); |
1536 | <html> |
1537 | <head> |
1538 | <title>Real World!</title> |
1539 | <body> |
1540 | <p>Just a test |
1541 | <table class=RealWorld> |
1542 | <thead> |
1543 | <tr> |
1544 | <th class=one>One |
1545 | <th class=two>Two |
1546 | <th class=three>Three |
1547 | <th class=four>Four |
1548 | <tbody> |
1549 | <tr> |
1550 | <td class=alpha>Alpha |
1551 | <td class=beta>Beta |
1552 | <td class=gamma><a href="#gamma">Gamma</a> |
1553 | <td class=delta>Delta |
1554 | <tr> |
1555 | <td class=alpha>Alpha Two |
1556 | <td class=beta>Beta Two |
1557 | <td class=gamma><a href="#gamma-two">Gamma Two</a> |
1558 | <td class=delta>Delta Two |
1559 | </table> |
1560 | EOF |
1561 | is $dom->find('html > head > title')->[0]->text, 'Real World!', 'right text'; |
1562 | is $dom->find('html > body > p')->[0]->text, 'Just a test', 'right text'; |
1563 | is $dom->find('p')->[0]->text, 'Just a test', 'right text'; |
1564 | is $dom->find('thead > tr > .three')->[0]->text, 'Three', 'right text'; |
1565 | is $dom->find('thead > tr > .four')->[0]->text, 'Four', 'right text'; |
1566 | is $dom->find('tbody > tr > .beta')->[0]->text, 'Beta', 'right text'; |
1567 | is $dom->find('tbody > tr > .gamma')->[0]->text, '', 'no text'; |
1568 | is $dom->find('tbody > tr > .gamma > a')->[0]->text, 'Gamma', 'right text'; |
1569 | is $dom->find('tbody > tr > .alpha')->[1]->text, 'Alpha Two', 'right text'; |
1570 | is $dom->find('tbody > tr > .gamma > a')->[1]->text, 'Gamma Two', 'right text'; |
1571 | my @following |
1572 | = $dom->find('tr > td:nth-child(1)')->map(following => ':nth-child(even)') |
1573 | ->flatten->map('all_text')->each; |
1574 | is_deeply \@following, ['Beta', 'Delta', 'Beta Two', 'Delta Two'], |
1575 | 'right results'; |
1576 | |
1577 | # Real world list |
1578 | $dom = DOM::Tiny->new(<<EOF); |
1579 | <html> |
1580 | <head> |
1581 | <title>Real World!</title> |
1582 | <body> |
1583 | <ul> |
1584 | <li> |
1585 | Test |
1586 | <br> |
1587 | 123 |
1588 | <p> |
1589 | |
1590 | <li> |
1591 | Test |
1592 | <br> |
1593 | 321 |
1594 | <p> |
1595 | <li> |
1596 | Test |
1597 | 3 |
1598 | 2 |
1599 | 1 |
1600 | <p> |
1601 | </ul> |
1602 | EOF |
1603 | is $dom->find('html > head > title')->[0]->text, 'Real World!', 'right text'; |
1604 | is $dom->find('body > ul > li')->[0]->text, 'Test 123', 'right text'; |
1605 | is $dom->find('body > ul > li > p')->[0]->text, '', 'no text'; |
1606 | is $dom->find('body > ul > li')->[1]->text, 'Test 321', 'right text'; |
1607 | is $dom->find('body > ul > li > p')->[1]->text, '', 'no text'; |
1608 | is $dom->find('body > ul > li')->[1]->all_text, 'Test 321', 'right text'; |
1609 | is $dom->find('body > ul > li > p')->[1]->all_text, '', 'no text'; |
1610 | is $dom->find('body > ul > li')->[2]->text, 'Test 3 2 1', 'right text'; |
1611 | is $dom->find('body > ul > li > p')->[2]->text, '', 'no text'; |
1612 | is $dom->find('body > ul > li')->[2]->all_text, 'Test 3 2 1', 'right text'; |
1613 | is $dom->find('body > ul > li > p')->[2]->all_text, '', 'no text'; |
1614 | |
1615 | # Advanced whitespace trimming (punctuation) |
1616 | $dom = DOM::Tiny->new(<<EOF); |
1617 | <html> |
1618 | <head> |
1619 | <title>Real World!</title> |
1620 | <body> |
1621 | <div>foo <strong>bar</strong>.</div> |
1622 | <div>foo<strong>, bar</strong>baz<strong>; yada</strong>.</div> |
1623 | <div>foo<strong>: bar</strong>baz<strong>? yada</strong>!</div> |
1624 | EOF |
1625 | is $dom->find('html > head > title')->[0]->text, 'Real World!', 'right text'; |
1626 | is $dom->find('body > div')->[0]->all_text, 'foo bar.', 'right text'; |
1627 | is $dom->find('body > div')->[1]->all_text, 'foo, bar baz; yada.', 'right text'; |
1628 | is $dom->find('body > div')->[1]->text, 'foo baz.', 'right text'; |
1629 | is $dom->find('body > div')->[2]->all_text, 'foo: bar baz? yada!', 'right text'; |
1630 | is $dom->find('body > div')->[2]->text, 'foo baz!', 'right text'; |
1631 | |
1632 | # Real world JavaScript and CSS |
1633 | $dom = DOM::Tiny->new(<<EOF); |
1634 | <html> |
1635 | <head> |
1636 | <style test=works>#style { foo: style('<test>'); }</style> |
1637 | <script> |
1638 | if (a < b) { |
1639 | alert('<123>'); |
1640 | } |
1641 | </script> |
1642 | < sCriPt two="23" >if (b > c) { alert('&<ohoh>') }< / scRiPt > |
1643 | <body>Foo!</body> |
1644 | EOF |
1645 | is $dom->find('html > body')->[0]->text, 'Foo!', 'right text'; |
1646 | is $dom->find('html > head > style')->[0]->text, |
1647 | "#style { foo: style('<test>'); }", 'right text'; |
1648 | is $dom->find('html > head > script')->[0]->text, |
1649 | "\n if (a < b) {\n alert('<123>');\n }\n ", 'right text'; |
1650 | is $dom->find('html > head > script')->[1]->text, |
1651 | "if (b > c) { alert('&<ohoh>') }", 'right text'; |
1652 | |
1653 | # More real world JavaScript |
1654 | $dom = DOM::Tiny->new(<<EOF); |
1655 | <!DOCTYPE html> |
1656 | <html> |
1657 | <head> |
1658 | <title>Foo</title> |
1659 | <script src="/js/one.js"></script> |
1660 | <script src="/js/two.js"></script> |
1661 | <script src="/js/three.js"></script> |
1662 | </head> |
1663 | <body>Bar</body> |
1664 | </html> |
1665 | EOF |
1666 | is $dom->at('title')->text, 'Foo', 'right text'; |
1667 | is $dom->find('html > head > script')->[0]->attr('src'), '/js/one.js', |
1668 | 'right attribute'; |
1669 | is $dom->find('html > head > script')->[1]->attr('src'), '/js/two.js', |
1670 | 'right attribute'; |
1671 | is $dom->find('html > head > script')->[2]->attr('src'), '/js/three.js', |
1672 | 'right attribute'; |
1673 | is $dom->find('html > head > script')->[2]->text, '', 'no text'; |
1674 | is $dom->at('html > body')->text, 'Bar', 'right text'; |
1675 | |
1676 | # Even more real world JavaScript |
1677 | $dom = DOM::Tiny->new(<<EOF); |
1678 | <!DOCTYPE html> |
1679 | <html> |
1680 | <head> |
1681 | <title>Foo</title> |
1682 | <script src="/js/one.js"></script> |
1683 | <script src="/js/two.js"></script> |
1684 | <script src="/js/three.js"> |
1685 | </head> |
1686 | <body>Bar</body> |
1687 | </html> |
1688 | EOF |
1689 | is $dom->at('title')->text, 'Foo', 'right text'; |
1690 | is $dom->find('html > head > script')->[0]->attr('src'), '/js/one.js', |
1691 | 'right attribute'; |
1692 | is $dom->find('html > head > script')->[1]->attr('src'), '/js/two.js', |
1693 | 'right attribute'; |
1694 | is $dom->find('html > head > script')->[2]->attr('src'), '/js/three.js', |
1695 | 'right attribute'; |
1696 | is $dom->find('html > head > script')->[2]->text, '', 'no text'; |
1697 | is $dom->at('html > body')->text, 'Bar', 'right text'; |
1698 | |
1699 | # Inline DTD |
1700 | $dom = DOM::Tiny->new(<<EOF); |
1701 | <?xml version="1.0"?> |
1702 | <!-- This is a Test! --> |
1703 | <!DOCTYPE root [ |
1704 | <!ELEMENT root (#PCDATA)> |
1705 | <!ATTLIST root att CDATA #REQUIRED> |
1706 | ]> |
1707 | <root att="test"> |
1708 | <![CDATA[<hello>world</hello>]]> |
1709 | </root> |
1710 | EOF |
1711 | ok $dom->xml, 'XML mode detected'; |
1712 | is $dom->at('root')->attr('att'), 'test', 'right attribute'; |
1713 | is $dom->tree->[5][1], ' root [ |
1714 | <!ELEMENT root (#PCDATA)> |
1715 | <!ATTLIST root att CDATA #REQUIRED> |
1716 | ]', 'right doctype'; |
1717 | is $dom->at('root')->text, '<hello>world</hello>', 'right text'; |
1718 | $dom = DOM::Tiny->new(<<EOF); |
1719 | <!doctype book |
1720 | SYSTEM "usr.dtd" |
1721 | [ |
1722 | <!ENTITY test "yeah"> |
1723 | ]> |
1724 | <foo /> |
1725 | EOF |
1726 | is $dom->tree->[1][1], ' book |
1727 | SYSTEM "usr.dtd" |
1728 | [ |
1729 | <!ENTITY test "yeah"> |
1730 | ]', 'right doctype'; |
1731 | ok !$dom->xml, 'XML mode not detected'; |
1732 | is $dom->at('foo'), '<foo></foo>', 'right element'; |
1733 | $dom = DOM::Tiny->new(<<EOF); |
1734 | <?xml version="1.0" encoding = 'utf-8'?> |
1735 | <!DOCTYPE foo [ |
1736 | <!ELEMENT foo ANY> |
1737 | <!ATTLIST foo xml:lang CDATA #IMPLIED> |
1738 | <!ENTITY % e SYSTEM "myentities.ent"> |
1739 | %myentities; |
1740 | ] > |
1741 | <foo xml:lang="de">Check!</fOo> |
1742 | EOF |
1743 | ok $dom->xml, 'XML mode detected'; |
1744 | is $dom->tree->[3][1], ' foo [ |
1745 | <!ELEMENT foo ANY> |
1746 | <!ATTLIST foo xml:lang CDATA #IMPLIED> |
1747 | <!ENTITY % e SYSTEM "myentities.ent"> |
1748 | %myentities; |
1749 | ] ', 'right doctype'; |
1750 | is $dom->at('foo')->attr->{'xml:lang'}, 'de', 'right attribute'; |
1751 | is $dom->at('foo')->text, 'Check!', 'right text'; |
1752 | $dom = DOM::Tiny->new(<<EOF); |
1753 | <!DOCTYPE TESTSUITE PUBLIC "my.dtd" 'mhhh' [ |
1754 | <!ELEMENT foo ANY> |
1755 | <!ATTLIST foo bar ENTITY 'true'> |
1756 | <!ENTITY system_entities SYSTEM 'systems.xml'> |
1757 | <!ENTITY leertaste ' '> |
1758 | <!-- This is a comment --> |
1759 | <!NOTATION hmmm SYSTEM "hmmm"> |
1760 | ] > |
1761 | <?check for-nothing?> |
1762 | <foo bar='false'>&leertaste;!!!</foo> |
1763 | EOF |
1764 | is $dom->tree->[1][1], ' TESTSUITE PUBLIC "my.dtd" \'mhhh\' [ |
1765 | <!ELEMENT foo ANY> |
1766 | <!ATTLIST foo bar ENTITY \'true\'> |
1767 | <!ENTITY system_entities SYSTEM \'systems.xml\'> |
1768 | <!ENTITY leertaste \' \'> |
1769 | <!-- This is a comment --> |
1770 | <!NOTATION hmmm SYSTEM "hmmm"> |
1771 | ] ', 'right doctype'; |
1772 | is $dom->at('foo')->attr('bar'), 'false', 'right attribute'; |
1773 | |
1774 | # Broken "font" block and useless end tags |
1775 | $dom = DOM::Tiny->new(<<EOF); |
1776 | <html> |
1777 | <head><title>Test</title></head> |
1778 | <body> |
1779 | <table> |
1780 | <tr><td><font>test</td></font></tr> |
1781 | </tr> |
1782 | </table> |
1783 | </body> |
1784 | </html> |
1785 | EOF |
1786 | is $dom->at('html > head > title')->text, 'Test', 'right text'; |
1787 | is $dom->at('html body table tr td > font')->text, 'test', 'right text'; |
1788 | |
1789 | # Different broken "font" block |
1790 | $dom = DOM::Tiny->new(<<EOF); |
1791 | <html> |
1792 | <head><title>Test</title></head> |
1793 | <body> |
1794 | <font> |
1795 | <table> |
1796 | <tr> |
1797 | <td>test1<br></td></font> |
1798 | <td>test2<br> |
1799 | </table> |
1800 | </body> |
1801 | </html> |
1802 | EOF |
1803 | is $dom->at('html > head > title')->text, 'Test', 'right text'; |
1804 | is $dom->find('html > body > font > table > tr > td')->[0]->text, 'test1', |
1805 | 'right text'; |
1806 | is $dom->find('html > body > font > table > tr > td')->[1]->text, 'test2', |
1807 | 'right text'; |
1808 | |
1809 | # Broken "font" and "div" blocks |
1810 | $dom = DOM::Tiny->new(<<EOF); |
1811 | <html> |
1812 | <head><title>Test</title></head> |
1813 | <body> |
1814 | <font> |
1815 | <div>test1<br> |
1816 | <div>test2<br></font> |
1817 | </div> |
1818 | </body> |
1819 | </html> |
1820 | EOF |
1821 | is $dom->at('html head title')->text, 'Test', 'right text'; |
1822 | is $dom->at('html body font > div')->text, 'test1', 'right text'; |
1823 | is $dom->at('html body font > div > div')->text, 'test2', 'right text'; |
1824 | |
1825 | # Broken "div" blocks |
1826 | $dom = DOM::Tiny->new(<<EOF); |
1827 | <html> |
1828 | <head><title>Test</title></head> |
1829 | <body> |
1830 | <div> |
1831 | <table> |
1832 | <tr><td><div>test</td></div></tr> |
1833 | </div> |
1834 | </table> |
1835 | </body> |
1836 | </html> |
1837 | EOF |
1838 | is $dom->at('html head title')->text, 'Test', 'right text'; |
1839 | is $dom->at('html body div table tr td > div')->text, 'test', 'right text'; |
1840 | |
1841 | # And another broken "font" block |
1842 | $dom = DOM::Tiny->new(<<EOF); |
1843 | <html> |
1844 | <head><title>Test</title></head> |
1845 | <body> |
1846 | <table> |
1847 | <tr> |
1848 | <td><font><br>te<br>st<br>1</td></font> |
1849 | <td>x1<td><img>tes<br>t2</td> |
1850 | <td>x2<td><font>t<br>est3</font></td> |
1851 | </tr> |
1852 | </table> |
1853 | </body> |
1854 | </html> |
1855 | EOF |
1856 | is $dom->at('html > head > title')->text, 'Test', 'right text'; |
1857 | is $dom->find('html body table tr > td > font')->[0]->text, 'te st 1', |
1858 | 'right text'; |
1859 | is $dom->find('html body table tr > td')->[1]->text, 'x1', 'right text'; |
1860 | is $dom->find('html body table tr > td')->[2]->text, 'tes t2', 'right text'; |
1861 | is $dom->find('html body table tr > td')->[3]->text, 'x2', 'right text'; |
1862 | is $dom->find('html body table tr > td')->[5], undef, 'no result'; |
1863 | is $dom->find('html body table tr > td')->size, 5, 'right number of elements'; |
1864 | is $dom->find('html body table tr > td > font')->[1]->text, 't est3', |
1865 | 'right text'; |
1866 | is $dom->find('html body table tr > td > font')->[2], undef, 'no result'; |
1867 | is $dom->find('html body table tr > td > font')->size, 2, |
1868 | 'right number of elements'; |
1869 | is $dom, <<EOF, 'right result'; |
1870 | <html> |
1871 | <head><title>Test</title></head> |
1872 | <body> |
1873 | <table> |
1874 | <tr> |
1875 | <td><font><br>te<br>st<br>1</font></td> |
1876 | <td>x1</td><td><img>tes<br>t2</td> |
1877 | <td>x2</td><td><font>t<br>est3</font></td> |
1878 | </tr> |
1879 | </table> |
1880 | </body> |
1881 | </html> |
1882 | EOF |
1883 | |
1884 | # A collection of wonderful screwups |
1885 | $dom = DOM::Tiny->new(<<'EOF'); |
1886 | <!DOCTYPE html> |
1887 | <html lang="en"> |
1888 | <head><title>Wonderful Screwups</title></head> |
1889 | <body id="screw-up"> |
1890 | <div> |
1891 | <div class="ewww"> |
1892 | <a href="/test" target='_blank'><img src="/test.png"></a> |
1893 | <a href='/real bad' screwup: http://localhost/bad' target='_blank'> |
1894 | <img src="/test2.png"> |
1895 | </div> |
1896 | </mt:If> |
1897 | </div> |
1898 | <b>>la<>la<<>>la<</b> |
1899 | </body> |
1900 | </html> |
1901 | EOF |
1902 | is $dom->at('#screw-up > b')->text, '>la<>la<<>>la<', 'right text'; |
1903 | is $dom->at('#screw-up .ewww > a > img')->attr('src'), '/test.png', |
1904 | 'right attribute'; |
1905 | is $dom->find('#screw-up .ewww > a > img')->[1]->attr('src'), '/test2.png', |
1906 | 'right attribute'; |
1907 | is $dom->find('#screw-up .ewww > a > img')->[2], undef, 'no result'; |
1908 | is $dom->find('#screw-up .ewww > a > img')->size, 2, 'right number of elements'; |
1909 | |
1910 | # Broken "br" tag |
1911 | $dom = DOM::Tiny->new('<br< abc abc abc abc abc abc abc abc<p>Test</p>'); |
1912 | is $dom->at('p')->text, 'Test', 'right text'; |
1913 | |
1914 | # Modifying an XML document |
1915 | $dom = DOM::Tiny->new(<<'EOF'); |
1916 | <?xml version='1.0' encoding='UTF-8'?> |
1917 | <XMLTest /> |
1918 | EOF |
1919 | ok $dom->xml, 'XML mode detected'; |
1920 | $dom->at('XMLTest')->content('<Element />'); |
1921 | my $element = $dom->at('Element'); |
1922 | is $element->tag, 'Element', 'right tag'; |
1923 | ok $element->xml, 'XML mode active'; |
1924 | $element = $dom->at('XMLTest')->children->[0]; |
1925 | is $element->tag, 'Element', 'right child'; |
1926 | is $element->parent->tag, 'XMLTest', 'right parent'; |
1927 | ok $element->root->xml, 'XML mode active'; |
1928 | $dom->replace('<XMLTest2 /><XMLTest3 just="works" />'); |
1929 | ok $dom->xml, 'XML mode active'; |
1930 | $dom->at('XMLTest2')->{foo} = undef; |
1931 | is $dom, '<XMLTest2 foo="foo" /><XMLTest3 just="works" />', 'right result'; |
1932 | |
1933 | # Ensure HTML semantics |
1934 | ok !DOM::Tiny->new->xml(undef)->parse('<?xml version="1.0"?>')->xml, |
1935 | 'XML mode not detected'; |
1936 | $dom |
1937 | = DOM::Tiny->new->xml(0)->parse('<?xml version="1.0"?><br><div>Test</div>'); |
1938 | is $dom->at('div:root')->text, 'Test', 'right text'; |
1939 | |
1940 | # Ensure XML semantics |
1941 | ok !!DOM::Tiny->new->xml(1)->parse('<foo />')->xml, 'XML mode active'; |
1942 | $dom = DOM::Tiny->new(<<'EOF'); |
1943 | <?xml version='1.0' encoding='UTF-8'?> |
1944 | <script> |
1945 | <table> |
1946 | <td> |
1947 | <tr><thead>foo<thead></tr> |
1948 | </td> |
1949 | <td> |
1950 | <tr><thead>bar<thead></tr> |
1951 | </td> |
1952 | </table> |
1953 | </script> |
1954 | EOF |
1955 | is $dom->find('table > td > tr > thead')->[0]->text, 'foo', 'right text'; |
1956 | is $dom->find('script > table > td > tr > thead')->[1]->text, 'bar', |
1957 | 'right text'; |
1958 | is $dom->find('table > td > tr > thead')->[2], undef, 'no result'; |
1959 | is $dom->find('table > td > tr > thead')->size, 2, 'right number of elements'; |
1960 | |
1961 | # Ensure XML semantics again |
1962 | $dom = DOM::Tiny->new->xml(1)->parse(<<'EOF'); |
1963 | <table> |
1964 | <td> |
1965 | <tr><thead>foo<thead></tr> |
1966 | </td> |
1967 | <td> |
1968 | <tr><thead>bar<thead></tr> |
1969 | </td> |
1970 | </table> |
1971 | EOF |
1972 | is $dom->find('table > td > tr > thead')->[0]->text, 'foo', 'right text'; |
1973 | is $dom->find('table > td > tr > thead')->[1]->text, 'bar', 'right text'; |
1974 | is $dom->find('table > td > tr > thead')->[2], undef, 'no result'; |
1975 | is $dom->find('table > td > tr > thead')->size, 2, 'right number of elements'; |
1976 | |
1977 | # Nested tables |
1978 | $dom = DOM::Tiny->new(<<'EOF'); |
1979 | <table id="foo"> |
1980 | <tr> |
1981 | <td> |
1982 | <table id="bar"> |
1983 | <tr> |
1984 | <td>baz</td> |
1985 | </tr> |
1986 | </table> |
1987 | </td> |
1988 | </tr> |
1989 | </table> |
1990 | EOF |
1991 | is $dom->find('#foo > tr > td > #bar > tr >td')->[0]->text, 'baz', 'right text'; |
1992 | is $dom->find('table > tr > td > table > tr >td')->[0]->text, 'baz', |
1993 | 'right text'; |
1994 | |
1995 | # Nested find |
1996 | $dom->parse(<<EOF); |
1997 | <c> |
1998 | <a>foo</a> |
1999 | <b> |
2000 | <a>bar</a> |
2001 | <c> |
2002 | <a>baz</a> |
2003 | <d> |
2004 | <a>yada</a> |
2005 | </d> |
2006 | </c> |
2007 | </b> |
2008 | </c> |
2009 | EOF |
2010 | my @results; |
2011 | $dom->find('b')->each( |
2012 | sub { |
2013 | $_->find('a')->each(sub { push @results, $_->text }); |
2014 | } |
2015 | ); |
2016 | is_deeply \@results, [qw(bar baz yada)], 'right results'; |
2017 | @results = (); |
2018 | $dom->find('a')->each(sub { push @results, $_->text }); |
2019 | is_deeply \@results, [qw(foo bar baz yada)], 'right results'; |
2020 | @results = (); |
2021 | $dom->find('b')->each( |
2022 | sub { |
2023 | $_->find('c a')->each(sub { push @results, $_->text }); |
2024 | } |
2025 | ); |
2026 | is_deeply \@results, [qw(baz yada)], 'right results'; |
2027 | is $dom->at('b')->at('a')->text, 'bar', 'right text'; |
2028 | is $dom->at('c > b > a')->text, 'bar', 'right text'; |
2029 | is $dom->at('b')->at('c > b > a'), undef, 'no result'; |
2030 | |
2031 | # Direct hash access to attributes in XML mode |
2032 | $dom = DOM::Tiny->new->xml(1)->parse(<<EOF); |
2033 | <a id="one"> |
2034 | <B class="two" test> |
2035 | foo |
2036 | <c id="three">bar</c> |
2037 | <c ID="four">baz</c> |
2038 | </B> |
2039 | </a> |
2040 | EOF |
2041 | ok $dom->xml, 'XML mode active'; |
2042 | is $dom->at('a')->{id}, 'one', 'right attribute'; |
2043 | is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes'; |
2044 | is $dom->at('a')->at('B')->text, 'foo', 'right text'; |
2045 | is $dom->at('B')->{class}, 'two', 'right attribute'; |
2046 | is_deeply [sort keys %{$dom->at('a B')}], [qw(class test)], 'right attributes'; |
2047 | is $dom->find('a B c')->[0]->text, 'bar', 'right text'; |
2048 | is $dom->find('a B c')->[0]{id}, 'three', 'right attribute'; |
2049 | is_deeply [sort keys %{$dom->find('a B c')->[0]}], ['id'], 'right attributes'; |
2050 | is $dom->find('a B c')->[1]->text, 'baz', 'right text'; |
2051 | is $dom->find('a B c')->[1]{ID}, 'four', 'right attribute'; |
2052 | is_deeply [sort keys %{$dom->find('a B c')->[1]}], ['ID'], 'right attributes'; |
2053 | is $dom->find('a B c')->[2], undef, 'no result'; |
2054 | is $dom->find('a B c')->size, 2, 'right number of elements'; |
2055 | @results = (); |
2056 | $dom->find('a B c')->each(sub { push @results, $_->text }); |
2057 | is_deeply \@results, [qw(bar baz)], 'right results'; |
2058 | is $dom->find('a B c')->join("\n"), |
2059 | qq{<c id="three">bar</c>\n<c ID="four">baz</c>}, 'right result'; |
2060 | is_deeply [keys %$dom], [], 'root has no attributes'; |
2061 | is $dom->find('#nothing')->join, '', 'no result'; |
2062 | |
2063 | # Direct hash access to attributes in HTML mode |
2064 | $dom = DOM::Tiny->new(<<EOF); |
2065 | <a id="one"> |
2066 | <B class="two" test> |
2067 | foo |
2068 | <c id="three">bar</c> |
2069 | <c ID="four">baz</c> |
2070 | </B> |
2071 | </a> |
2072 | EOF |
2073 | ok !$dom->xml, 'XML mode not active'; |
2074 | is $dom->at('a')->{id}, 'one', 'right attribute'; |
2075 | is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes'; |
2076 | is $dom->at('a')->at('b')->text, 'foo', 'right text'; |
2077 | is $dom->at('b')->{class}, 'two', 'right attribute'; |
2078 | is_deeply [sort keys %{$dom->at('a b')}], [qw(class test)], 'right attributes'; |
2079 | is $dom->find('a b c')->[0]->text, 'bar', 'right text'; |
2080 | is $dom->find('a b c')->[0]{id}, 'three', 'right attribute'; |
2081 | is_deeply [sort keys %{$dom->find('a b c')->[0]}], ['id'], 'right attributes'; |
2082 | is $dom->find('a b c')->[1]->text, 'baz', 'right text'; |
2083 | is $dom->find('a b c')->[1]{id}, 'four', 'right attribute'; |
2084 | is_deeply [sort keys %{$dom->find('a b c')->[1]}], ['id'], 'right attributes'; |
2085 | is $dom->find('a b c')->[2], undef, 'no result'; |
2086 | is $dom->find('a b c')->size, 2, 'right number of elements'; |
2087 | @results = (); |
2088 | $dom->find('a b c')->each(sub { push @results, $_->text }); |
2089 | is_deeply \@results, [qw(bar baz)], 'right results'; |
2090 | is $dom->find('a b c')->join("\n"), |
2091 | qq{<c id="three">bar</c>\n<c id="four">baz</c>}, 'right result'; |
2092 | is_deeply [keys %$dom], [], 'root has no attributes'; |
2093 | is $dom->find('#nothing')->join, '', 'no result'; |
2094 | |
2095 | # Append and prepend content |
2096 | $dom = DOM::Tiny->new('<a><b>Test<c /></b></a>'); |
2097 | $dom->at('b')->append_content('<d />'); |
2098 | is $dom->children->[0]->tag, 'a', 'right tag'; |
2099 | is $dom->all_text, 'Test', 'right text'; |
2100 | is $dom->at('c')->parent->tag, 'b', 'right tag'; |
2101 | is $dom->at('d')->parent->tag, 'b', 'right tag'; |
2102 | $dom->at('b')->prepend_content('<e>DOM</e>'); |
2103 | is $dom->at('e')->parent->tag, 'b', 'right tag'; |
2104 | is $dom->all_text, 'DOM Test', 'right text'; |
2105 | |
2106 | # Wrap elements |
2107 | $dom = DOM::Tiny->new('<a>Test</a>'); |
2108 | is $dom->wrap('<b></b>')->type, 'root', 'right type'; |
2109 | is "$dom", '<b><a>Test</a></b>', 'right result'; |
2110 | is $dom->at('b')->strip->at('a')->wrap('A')->tag, 'a', 'right tag'; |
2111 | is "$dom", '<a>Test</a>', 'right result'; |
2112 | is $dom->at('a')->wrap('<b></b>')->tag, 'a', 'right tag'; |
2113 | is "$dom", '<b><a>Test</a></b>', 'right result'; |
2114 | is $dom->at('a')->wrap('C<c><d>D</d><e>E</e></c>F')->parent->tag, 'd', |
2115 | 'right tag'; |
2116 | is "$dom", '<b>C<c><d>D<a>Test</a></d><e>E</e></c>F</b>', 'right result'; |
2117 | |
2118 | # Wrap content |
2119 | $dom = DOM::Tiny->new('<a>Test</a>'); |
2120 | is $dom->at('a')->wrap_content('A')->tag, 'a', 'right tag'; |
2121 | is "$dom", '<a>Test</a>', 'right result'; |
2122 | is $dom->wrap_content('<b></b>')->type, 'root', 'right type'; |
2123 | is "$dom", '<b><a>Test</a></b>', 'right result'; |
2124 | is $dom->at('b')->strip->at('a')->tag('e:a')->wrap_content('1<b c="d"></b>') |
2125 | ->tag, 'e:a', 'right tag'; |
2126 | is "$dom", '<e:a>1<b c="d">Test</b></e:a>', 'right result'; |
2127 | is $dom->at('a')->wrap_content('C<c><d>D</d><e>E</e></c>F')->parent->type, |
2128 | 'root', 'right type'; |
2129 | is "$dom", '<e:a>C<c><d>D1<b c="d">Test</b></d><e>E</e></c>F</e:a>', |
2130 | 'right result'; |
2131 | |
2132 | # Broken "div" in "td" |
2133 | $dom = DOM::Tiny->new(<<EOF); |
2134 | <table> |
2135 | <tr> |
2136 | <td><div id="A"></td> |
2137 | <td><div id="B"></td> |
2138 | </tr> |
2139 | </table> |
2140 | EOF |
2141 | is $dom->find('table tr td')->[0]->at('div')->{id}, 'A', 'right attribute'; |
2142 | is $dom->find('table tr td')->[1]->at('div')->{id}, 'B', 'right attribute'; |
2143 | is $dom->find('table tr td')->[2], undef, 'no result'; |
2144 | is $dom->find('table tr td')->size, 2, 'right number of elements'; |
2145 | is "$dom", <<EOF, 'right result'; |
2146 | <table> |
2147 | <tr> |
2148 | <td><div id="A"></div></td> |
2149 | <td><div id="B"></div></td> |
2150 | </tr> |
2151 | </table> |
2152 | EOF |
2153 | |
2154 | # Preformatted text |
2155 | $dom = DOM::Tiny->new(<<EOF); |
2156 | <div> |
2157 | looks |
2158 | <pre><code>like |
2159 | it |
2160 | really</code> |
2161 | </pre> |
2162 | works |
2163 | </div> |
2164 | EOF |
2165 | is $dom->text, '', 'no text'; |
2166 | is $dom->text(0), "\n", 'right text'; |
2167 | is $dom->all_text, "looks like\n it\n really\n works", 'right text'; |
2168 | is $dom->all_text(0), "\n looks\n like\n it\n really\n \n works\n\n", |
2169 | 'right text'; |
2170 | is $dom->at('div')->text, 'looks works', 'right text'; |
2171 | is $dom->at('div')->text(0), "\n looks\n \n works\n", 'right text'; |
2172 | is $dom->at('div')->all_text, "looks like\n it\n really\n works", |
2173 | 'right text'; |
2174 | is $dom->at('div')->all_text(0), |
2175 | "\n looks\n like\n it\n really\n \n works\n", 'right text'; |
2176 | is $dom->at('div pre')->text, "\n ", 'right text'; |
2177 | is $dom->at('div pre')->text(0), "\n ", 'right text'; |
2178 | is $dom->at('div pre')->all_text, "like\n it\n really\n ", 'right text'; |
2179 | is $dom->at('div pre')->all_text(0), "like\n it\n really\n ", 'right text'; |
2180 | is $dom->at('div pre code')->text, "like\n it\n really", 'right text'; |
2181 | is $dom->at('div pre code')->text(0), "like\n it\n really", 'right text'; |
2182 | is $dom->at('div pre code')->all_text, "like\n it\n really", 'right text'; |
2183 | is $dom->at('div pre code')->all_text(0), "like\n it\n really", |
2184 | 'right text'; |
2185 | |
2186 | # Form values |
2187 | $dom = DOM::Tiny->new(<<EOF); |
2188 | <form action="/foo"> |
2189 | <p>Test</p> |
2190 | <input type="text" name="a" value="A" /> |
2191 | <input type="checkbox" checked name="b" value="B"> |
2192 | <input type="radio" checked name="c" value="C"> |
2193 | <select multiple name="f"> |
2194 | <option value="F">G</option> |
2195 | <optgroup> |
2196 | <option>H</option> |
2197 | <option selected>I</option> |
2198 | </optgroup> |
2199 | <option value="J" selected>K</option> |
2200 | </select> |
2201 | <select name="n"><option>N</option></select> |
2202 | <select multiple name="q"><option>Q</option></select> |
2203 | <select name="d"> |
2204 | <option selected>R</option> |
2205 | <option selected>D</option> |
2206 | </select> |
2207 | <textarea name="m">M</textarea> |
2208 | <button name="o" value="O">No!</button> |
2209 | <input type="submit" name="p" value="P" /> |
2210 | </form> |
2211 | EOF |
2212 | is $dom->at('p')->val, undef, 'no value'; |
2213 | is $dom->at('input')->val, 'A', 'right value'; |
2214 | is $dom->at('input:checked')->val, 'B', 'right value'; |
2215 | is $dom->at('input:checked[type=radio]')->val, 'C', 'right value'; |
2216 | is_deeply $dom->at('select')->val, ['I', 'J'], 'right values'; |
2217 | is $dom->at('select option')->val, 'F', 'right value'; |
2218 | is $dom->at('select optgroup option:not([selected])')->val, 'H', 'right value'; |
2219 | is $dom->find('select')->[1]->at('option')->val, 'N', 'right value'; |
2220 | is $dom->find('select')->[1]->val, undef, 'no value'; |
2221 | is_deeply $dom->find('select')->[2]->val, undef, 'no value'; |
2222 | is $dom->find('select')->[2]->at('option')->val, 'Q', 'right value'; |
2223 | is_deeply $dom->find('select')->last->val, 'D', 'right value'; |
2224 | is_deeply $dom->find('select')->last->at('option')->val, 'R', 'right value'; |
2225 | is $dom->at('textarea')->val, 'M', 'right value'; |
2226 | is $dom->at('button')->val, 'O', 'right value'; |
2227 | is $dom->find('form input')->last->val, 'P', 'right value'; |
2228 | |
2229 | # PoCo example with whitespace sensitive text |
2230 | $dom = DOM::Tiny->new(<<EOF); |
2231 | <?xml version="1.0" encoding="UTF-8"?> |
2232 | <response> |
2233 | <entry> |
2234 | <id>1286823</id> |
2235 | <displayName>Homer Simpson</displayName> |
2236 | <addresses> |
2237 | <type>home</type> |
2238 | <formatted><![CDATA[742 Evergreen Terrace |
2239 | Springfield, VT 12345 USA]]></formatted> |
2240 | </addresses> |
2241 | </entry> |
2242 | <entry> |
2243 | <id>1286822</id> |
2244 | <displayName>Marge Simpson</displayName> |
2245 | <addresses> |
2246 | <type>home</type> |
2247 | <formatted>742 Evergreen Terrace |
2248 | Springfield, VT 12345 USA</formatted> |
2249 | </addresses> |
2250 | </entry> |
2251 | </response> |
2252 | EOF |
2253 | is $dom->find('entry')->[0]->at('displayName')->text, 'Homer Simpson', |
2254 | 'right text'; |
2255 | is $dom->find('entry')->[0]->at('id')->text, '1286823', 'right text'; |
2256 | is $dom->find('entry')->[0]->at('addresses')->children('type')->[0]->text, |
2257 | 'home', 'right text'; |
2258 | is $dom->find('entry')->[0]->at('addresses formatted')->text, |
2259 | "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text'; |
2260 | is $dom->find('entry')->[0]->at('addresses formatted')->text(0), |
2261 | "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text'; |
2262 | is $dom->find('entry')->[1]->at('displayName')->text, 'Marge Simpson', |
2263 | 'right text'; |
2264 | is $dom->find('entry')->[1]->at('id')->text, '1286822', 'right text'; |
2265 | is $dom->find('entry')->[1]->at('addresses')->children('type')->[0]->text, |
2266 | 'home', 'right text'; |
2267 | is $dom->find('entry')->[1]->at('addresses formatted')->text, |
2268 | '742 Evergreen Terrace Springfield, VT 12345 USA', 'right text'; |
2269 | is $dom->find('entry')->[1]->at('addresses formatted')->text(0), |
2270 | "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text'; |
2271 | is $dom->find('entry')->[2], undef, 'no result'; |
2272 | is $dom->find('entry')->size, 2, 'right number of elements'; |
2273 | |
2274 | # Find attribute with hyphen in name and value |
2275 | $dom = DOM::Tiny->new(<<EOF); |
2276 | <html> |
2277 | <head><meta http-equiv="content-type" content="text/html"></head> |
2278 | </html> |
2279 | EOF |
2280 | is $dom->find('[http-equiv]')->[0]{content}, 'text/html', 'right attribute'; |
2281 | is $dom->find('[http-equiv]')->[1], undef, 'no result'; |
2282 | is $dom->find('[http-equiv="content-type"]')->[0]{content}, 'text/html', |
2283 | 'right attribute'; |
2284 | is $dom->find('[http-equiv="content-type"]')->[1], undef, 'no result'; |
2285 | is $dom->find('[http-equiv^="content-"]')->[0]{content}, 'text/html', |
2286 | 'right attribute'; |
2287 | is $dom->find('[http-equiv^="content-"]')->[1], undef, 'no result'; |
2288 | is $dom->find('head > [http-equiv$="-type"]')->[0]{content}, 'text/html', |
2289 | 'right attribute'; |
2290 | is $dom->find('head > [http-equiv$="-type"]')->[1], undef, 'no result'; |
2291 | |
2292 | # Find "0" attribute value |
2293 | $dom = DOM::Tiny->new(<<EOF); |
2294 | <a accesskey="0">Zero</a> |
2295 | <a accesskey="1">O&gTn>e</a> |
2296 | EOF |
2297 | is $dom->find('a[accesskey]')->[0]->text, 'Zero', 'right text'; |
2298 | is $dom->find('a[accesskey]')->[1]->text, 'O&gTn>e', 'right text'; |
2299 | is $dom->find('a[accesskey]')->[2], undef, 'no result'; |
2300 | is $dom->find('a[accesskey=0]')->[0]->text, 'Zero', 'right text'; |
2301 | is $dom->find('a[accesskey=0]')->[1], undef, 'no result'; |
2302 | is $dom->find('a[accesskey^=0]')->[0]->text, 'Zero', 'right text'; |
2303 | is $dom->find('a[accesskey^=0]')->[1], undef, 'no result'; |
2304 | is $dom->find('a[accesskey$=0]')->[0]->text, 'Zero', 'right text'; |
2305 | is $dom->find('a[accesskey$=0]')->[1], undef, 'no result'; |
2306 | is $dom->find('a[accesskey~=0]')->[0]->text, 'Zero', 'right text'; |
2307 | is $dom->find('a[accesskey~=0]')->[1], undef, 'no result'; |
2308 | is $dom->find('a[accesskey*=0]')->[0]->text, 'Zero', 'right text'; |
2309 | is $dom->find('a[accesskey*=0]')->[1], undef, 'no result'; |
2310 | is $dom->find('a[accesskey=1]')->[0]->text, 'O&gTn>e', 'right text'; |
2311 | is $dom->find('a[accesskey=1]')->[1], undef, 'no result'; |
2312 | is $dom->find('a[accesskey^=1]')->[0]->text, 'O&gTn>e', 'right text'; |
2313 | is $dom->find('a[accesskey^=1]')->[1], undef, 'no result'; |
2314 | is $dom->find('a[accesskey$=1]')->[0]->text, 'O&gTn>e', 'right text'; |
2315 | is $dom->find('a[accesskey$=1]')->[1], undef, 'no result'; |
2316 | is $dom->find('a[accesskey~=1]')->[0]->text, 'O&gTn>e', 'right text'; |
2317 | is $dom->find('a[accesskey~=1]')->[1], undef, 'no result'; |
2318 | is $dom->find('a[accesskey*=1]')->[0]->text, 'O&gTn>e', 'right text'; |
2319 | is $dom->find('a[accesskey*=1]')->[1], undef, 'no result'; |
2320 | is $dom->at('a[accesskey*="."]'), undef, 'no result'; |
2321 | |
2322 | # Empty attribute value |
2323 | $dom = DOM::Tiny->new(<<EOF); |
2324 | <foo bar=> |
2325 | test |
2326 | </foo> |
2327 | <bar>after</bar> |
2328 | EOF |
2329 | is $dom->tree->[0], 'root', 'right type'; |
2330 | is $dom->tree->[1][0], 'tag', 'right type'; |
2331 | is $dom->tree->[1][1], 'foo', 'right tag'; |
2332 | is_deeply $dom->tree->[1][2], {bar => ''}, 'right attributes'; |
2333 | is $dom->tree->[1][4][0], 'text', 'right type'; |
2334 | is $dom->tree->[1][4][1], "\n test\n", 'right text'; |
2335 | is $dom->tree->[3][0], 'tag', 'right type'; |
2336 | is $dom->tree->[3][1], 'bar', 'right tag'; |
2337 | is $dom->tree->[3][4][0], 'text', 'right type'; |
2338 | is $dom->tree->[3][4][1], 'after', 'right text'; |
2339 | is "$dom", <<EOF, 'right result'; |
2340 | <foo bar=""> |
2341 | test |
2342 | </foo> |
2343 | <bar>after</bar> |
2344 | EOF |
2345 | |
2346 | # Case-insensitive attribute values |
2347 | $dom = DOM::Tiny->new(<<EOF); |
2348 | <p class="foo">A</p> |
2349 | <p class="foo bAr">B</p> |
2350 | <p class="FOO">C</p> |
2351 | EOF |
2352 | is $dom->find('.foo')->map('text')->join(','), 'A,B', 'right result'; |
2353 | is $dom->find('.FOO')->map('text')->join(','), 'C', 'right result'; |
2354 | is $dom->find('[class=foo]')->map('text')->join(','), 'A', 'right result'; |
2355 | is $dom->find('[class=foo i]')->map('text')->join(','), 'A,C', 'right result'; |
2356 | is $dom->find('[class="foo" i]')->map('text')->join(','), 'A,C', 'right result'; |
2357 | is $dom->find('[class="foo bar"]')->size, 0, 'no results'; |
2358 | is $dom->find('[class="foo bar" i]')->map('text')->join(','), 'B', |
2359 | 'right result'; |
2360 | is $dom->find('[class~=foo]')->map('text')->join(','), 'A,B', 'right result'; |
2361 | is $dom->find('[class~=foo i]')->map('text')->join(','), 'A,B,C', |
2362 | 'right result'; |
2363 | is $dom->find('[class*=f]')->map('text')->join(','), 'A,B', 'right result'; |
2364 | is $dom->find('[class*=f i]')->map('text')->join(','), 'A,B,C', 'right result'; |
2365 | is $dom->find('[class^=F]')->map('text')->join(','), 'C', 'right result'; |
2366 | is $dom->find('[class^=F i]')->map('text')->join(','), 'A,B,C', 'right result'; |
2367 | is $dom->find('[class$=O]')->map('text')->join(','), 'C', 'right result'; |
2368 | is $dom->find('[class$=O i]')->map('text')->join(','), 'A,C', 'right result'; |
2369 | |
2370 | # Nested description lists |
2371 | $dom = DOM::Tiny->new(<<EOF); |
2372 | <dl> |
2373 | <dt>A</dt> |
2374 | <DD> |
2375 | <dl> |
2376 | <dt>B |
2377 | <dd>C |
2378 | </dl> |
2379 | </dd> |
2380 | </dl> |
2381 | EOF |
2382 | is $dom->find('dl > dd > dl > dt')->[0]->text, 'B', 'right text'; |
2383 | is $dom->find('dl > dd > dl > dd')->[0]->text, 'C', 'right text'; |
2384 | is $dom->find('dl > dt')->[0]->text, 'A', 'right text'; |
2385 | |
2386 | # Nested lists |
2387 | $dom = DOM::Tiny->new(<<EOF); |
2388 | <div> |
2389 | <ul> |
2390 | <li> |
2391 | A |
2392 | <ul> |
2393 | <li>B</li> |
2394 | C |
2395 | </ul> |
2396 | </li> |
2397 | </ul> |
2398 | </div> |
2399 | EOF |
2400 | is $dom->find('div > ul > li')->[0]->text, 'A', 'right text'; |
2401 | is $dom->find('div > ul > li')->[1], undef, 'no result'; |
2402 | is $dom->find('div > ul li')->[0]->text, 'A', 'right text'; |
2403 | is $dom->find('div > ul li')->[1]->text, 'B', 'right text'; |
2404 | is $dom->find('div > ul li')->[2], undef, 'no result'; |
2405 | is $dom->find('div > ul ul')->[0]->text, 'C', 'right text'; |
2406 | is $dom->find('div > ul ul')->[1], undef, 'no result'; |
2407 | |
2408 | # Unusual order |
2409 | $dom |
2410 | = DOM::Tiny->new('<a href="http://example.com" id="foo" class="bar">Ok!</a>'); |
2411 | is $dom->at('a:not([href$=foo])[href^=h]')->text, 'Ok!', 'right text'; |
2412 | is $dom->at('a:not([href$=example.com])[href^=h]'), undef, 'no result'; |
2413 | is $dom->at('a[href^=h]#foo.bar')->text, 'Ok!', 'right text'; |
2414 | is $dom->at('a[href^=h]#foo.baz'), undef, 'no result'; |
2415 | is $dom->at('a[href^=h]#foo:not(b)')->text, 'Ok!', 'right text'; |
2416 | is $dom->at('a[href^=h]#foo:not(a)'), undef, 'no result'; |
2417 | is $dom->at('[href^=h].bar:not(b)[href$=m]#foo')->text, 'Ok!', 'right text'; |
2418 | is $dom->at('[href^=h].bar:not(b)[href$=m]#bar'), undef, 'no result'; |
2419 | is $dom->at(':not(b)#foo#foo')->text, 'Ok!', 'right text'; |
2420 | is $dom->at(':not(b)#foo#bar'), undef, 'no result'; |
2421 | is $dom->at(':not([href^=h]#foo#bar)')->text, 'Ok!', 'right text'; |
2422 | is $dom->at(':not([href^=h]#foo#foo)'), undef, 'no result'; |
2423 | |
2424 | # Slash between attributes |
2425 | $dom = DOM::Tiny->new('<input /type=checkbox / value="/a/" checked/><br/>'); |
2426 | is_deeply $dom->at('input')->attr, |
2427 | {type => 'checkbox', value => '/a/', checked => undef}, 'right attributes'; |
2428 | is "$dom", '<input checked type="checkbox" value="/a/"><br>', 'right result'; |
2429 | |
2430 | # Dot and hash in class and id attributes |
2431 | $dom = DOM::Tiny->new('<p class="a#b.c">A</p><p id="a#b.c">B</p>'); |
2432 | is $dom->at('p.a\#b\.c')->text, 'A', 'right text'; |
2433 | is $dom->at(':not(p.a\#b\.c)')->text, 'B', 'right text'; |
2434 | is $dom->at('p#a\#b\.c')->text, 'B', 'right text'; |
2435 | is $dom->at(':not(p#a\#b\.c)')->text, 'A', 'right text'; |
2436 | |
2437 | # Extra whitespace |
2438 | $dom = DOM::Tiny->new('< span>a< /span><b >b</b><span >c</ span>'); |
2439 | is $dom->at('span')->text, 'a', 'right text'; |
2440 | is $dom->at('span + b')->text, 'b', 'right text'; |
2441 | is $dom->at('b + span')->text, 'c', 'right text'; |
2442 | is "$dom", '<span>a</span><b>b</b><span>c</span>', 'right result'; |
2443 | |
2444 | # Selectors with leading and trailing whitespace |
2445 | $dom = DOM::Tiny->new('<div id=foo><b>works</b></div>'); |
2446 | is $dom->at(' div b ')->text, 'works', 'right text'; |
2447 | is $dom->at(' :not( #foo ) ')->text, 'works', 'right text'; |
2448 | |
2449 | # "0" |
2450 | $dom = DOM::Tiny->new('0'); |
2451 | is "$dom", '0', 'right result'; |
2452 | $dom->append_content('☃'); |
2453 | is "$dom", '0☃', 'right result'; |
2454 | is $dom->parse('<!DOCTYPE 0>'), '<!DOCTYPE 0>', 'successful roundtrip'; |
2455 | is $dom->parse('<!--0-->'), '<!--0-->', 'successful roundtrip'; |
2456 | is $dom->parse('<![CDATA[0]]>'), '<![CDATA[0]]>', 'successful roundtrip'; |
2457 | is $dom->parse('<?0?>'), '<?0?>', 'successful roundtrip'; |
2458 | |
2459 | # Not self-closing |
2460 | $dom = DOM::Tiny->new('<div />< div ><pre />test</div >123'); |
2461 | is $dom->at('div > div > pre')->text, 'test', 'right text'; |
2462 | is "$dom", '<div><div><pre>test</pre></div>123</div>', 'right result'; |
2463 | $dom = DOM::Tiny->new('<p /><svg><circle /><circle /></svg>'); |
2464 | is $dom->find('p > svg > circle')->size, 2, 'two circles'; |
2465 | is "$dom", '<p><svg><circle></circle><circle></circle></svg></p>', |
2466 | 'right result'; |
2467 | |
2468 | # "image" |
2469 | $dom = DOM::Tiny->new('<image src="foo.png">test'); |
2470 | is $dom->at('img')->{src}, 'foo.png', 'right attribute'; |
2471 | is "$dom", '<img src="foo.png">test', 'right result'; |
2472 | |
2473 | # "title" |
2474 | $dom = DOM::Tiny->new('<title> <p>test<</title>'); |
2475 | is $dom->at('title')->text, ' <p>test<', 'right text'; |
2476 | is "$dom", '<title> <p>test<</title>', 'right result'; |
2477 | |
2478 | # "textarea" |
2479 | $dom = DOM::Tiny->new('<textarea id="a"> <p>test<</textarea>'); |
2480 | is $dom->at('textarea#a')->text, ' <p>test<', 'right text'; |
2481 | is "$dom", '<textarea id="a"> <p>test<</textarea>', 'right result'; |
2482 | |
2483 | # Comments |
2484 | $dom = DOM::Tiny->new(<<EOF); |
2485 | <!-- HTML5 --> |
2486 | <!-- bad idea -- HTML5 --> |
2487 | <!-- HTML4 -- > |
2488 | <!-- bad idea -- HTML4 -- > |
2489 | EOF |
2490 | is $dom->tree->[1][1], ' HTML5 ', 'right comment'; |
2491 | is $dom->tree->[3][1], ' bad idea -- HTML5 ', 'right comment'; |
2492 | is $dom->tree->[5][1], ' HTML4 ', 'right comment'; |
2493 | is $dom->tree->[7][1], ' bad idea -- HTML4 ', 'right comment'; |
2494 | |
2495 | # Huge number of attributes |
2496 | $dom = DOM::Tiny->new('<div ' . ('a=b ' x 32768) . '>Test</div>'); |
2497 | is $dom->at('div[a=b]')->text, 'Test', 'right text'; |
2498 | |
2499 | # Huge number of nested tags |
2500 | my $huge = ('<a>' x 100) . 'works' . ('</a>' x 100); |
2501 | $dom = DOM::Tiny->new($huge); |
2502 | is $dom->all_text, 'works', 'right text'; |
2503 | is "$dom", $huge, 'right result'; |
2504 | |
dbff35d4 |
2505 | # TO_JSON |
2506 | is +JSON::PP->new->convert_blessed->encode([DOM::Tiny->new('<a></a>')]), '["<a></a>"]', 'right result'; |
2507 | |
d6512b50 |
2508 | done_testing(); |