Commit | Line | Data |
1cf03540 |
1 | use strictures 1; |
456a815d |
2 | use Test::More; |
3 | |
4 | use HTML::Zoom::Parser::BuiltIn; |
5 | use HTML::Zoom::Producer::BuiltIn; |
6 | use HTML::Zoom::SelectorParser; |
7 | use HTML::Zoom::FilterBuilder; |
8 | use HTML::Zoom::FilterStream; |
9 | |
10 | my $tmpl = <<END; |
11 | <body> |
12 | <div class="main"> |
13 | <span class="hilight name">Bob</span> |
14 | <span class="career">Builder</span> |
15 | <hr /> |
16 | </div> |
17 | </body> |
18 | END |
19 | |
d80786d0 |
20 | sub src_stream { HTML::Zoom::Parser::BuiltIn->new->html_to_stream($tmpl); } |
456a815d |
21 | |
d80786d0 |
22 | sub html_sink { HTML::Zoom::Producer::BuiltIn->new->html_from_stream($_[0]) } |
456a815d |
23 | |
24 | my $fb = HTML::Zoom::FilterBuilder->new; |
25 | |
26 | my $sp = HTML::Zoom::SelectorParser->new; |
27 | |
28 | sub filter { |
29 | my ($stream, $sel, $cb) = @_; |
30 | return HTML::Zoom::FilterStream->new({ |
31 | stream => $stream, |
32 | match => $sp->parse_selector($sel), |
33 | filter => do { local $_ = $fb; $cb->($fb) } |
34 | }); |
35 | } |
36 | |
37 | sub run_for (&;$) { |
38 | my $cb = shift; |
39 | (html_sink |
40 | (filter |
41 | src_stream, |
42 | (shift or '.main'), |
43 | $cb |
44 | ) |
45 | ) |
46 | } |
47 | |
8f962884 |
48 | my ($expect, @ev); |
49 | |
50 | ($expect = $tmpl) =~ s/class="main"/class="foo"/; |
51 | |
52 | is( |
f0ddc273 |
53 | run_for { $_->set_attribute( 'class' => 'foo' ) }, |
8f962884 |
54 | $expect, |
55 | 'set attribute on existing attribute' |
56 | ); |
57 | |
58 | ($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/; |
59 | |
60 | is( |
f0ddc273 |
61 | run_for { $_->set_attribute( 'foo' => 'bar' ) }, |
8f962884 |
62 | $expect, |
63 | 'set attribute on non existing attribute' |
64 | ); |
65 | |
66 | ($expect = $tmpl) =~ s/class="main"/class="main foo"/; |
67 | |
68 | is( |
f0ddc273 |
69 | run_for { $_->add_to_attribute( 'class' => 'foo' ) }, |
8f962884 |
70 | $expect, |
71 | 'add attribute on existing attribute' |
72 | ); |
73 | |
74 | ($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/; |
75 | |
76 | is( |
f0ddc273 |
77 | run_for { $_->add_to_attribute( 'foo' => 'bar' ) }, |
8f962884 |
78 | $expect, |
79 | 'add attribute on non existing attribute' |
80 | ); |
81 | |
82 | ($expect = $tmpl) =~ s/ class="main"//; |
83 | |
84 | is( |
85 | run_for { $_->remove_attribute({ name => 'class' }) }, |
86 | $expect, |
87 | 'remove attribute on existing attribute' |
88 | ); |
89 | |
90 | is( |
91 | run_for { $_->remove_attribute({ name => 'foo' }) }, |
92 | $tmpl, |
93 | 'remove attribute on non existing attribute' |
94 | ); |
95 | |
5cac799e |
96 | ($expect = $tmpl) =~ s/ class="main"//; |
97 | |
98 | is( |
99 | run_for { |
100 | $_->transform_attribute({ |
101 | name => 'class', |
102 | code => sub { |
103 | my $a = shift; |
104 | return if $a eq 'main'; |
105 | return $a; |
106 | }, |
107 | }) |
108 | }, |
109 | $expect, |
110 | 'transform_attribute deletes the attr if code returns undef', |
111 | ); |
112 | |
113 | ($expect = $tmpl) =~ s/ class="main"/ class="moan"/; |
114 | |
115 | is( |
116 | run_for { |
117 | $_->transform_attribute({ |
118 | name => 'class', |
119 | code => sub { |
120 | ( my $b = shift ) =~ s/main/moan/; |
121 | $b |
122 | }, |
123 | }) |
124 | }, |
125 | $expect, |
126 | 'transform_attribute transforms something', |
127 | ); |
128 | |
129 | ($expect = $tmpl) =~ s/ class="main"/ class="main" noggin="zonk"/; |
130 | |
131 | is( |
132 | run_for { |
133 | $_->transform_attribute({ |
134 | name => 'noggin', |
135 | code => sub { 'zonk' }, |
136 | }) |
137 | }, |
138 | $expect, |
139 | 'transform_attribute adds attribute if not there before', |
140 | ); |
141 | |
142 | is( |
143 | run_for { |
144 | $_->transform_attribute({ |
145 | name => 'noggin', |
146 | code => sub { }, |
147 | }) |
148 | }, |
149 | $tmpl, |
150 | 'transform_attribute on nonexistent att does not add it if code returns undef', |
151 | ); |
152 | |
cd1ced5d |
153 | ($expect = $tmpl) =~ s{(</span>)}{ What ever$1}g; |
154 | |
155 | is( |
156 | ( run_for { |
157 | $_->transform_content( sub { "$_ What ever" } ), |
158 | } 'span' |
159 | ), |
160 | $expect, |
161 | 'transform_content works for multiple selected elements', |
162 | ); |
163 | |
164 | ($expect = $tmpl) =~ s{(<span class="career">Builder)}{$1 What ever}; |
165 | |
166 | is( |
167 | ( run_for { |
168 | $_->transform_content( sub { "$_ What ever" } ), |
169 | } 'span.career' |
170 | ), |
171 | $expect, |
172 | 'transform_content works for one specific selected element', |
173 | ); |
5cac799e |
174 | |
d70c59fa |
175 | $expect = do { |
176 | # done in a new context to keep test namespace clean |
177 | my $copy_of_tmpl = $tmpl; |
178 | my $repeatable = +( $copy_of_tmpl =~ m{(<div\sclass="main".+?</div>)}xms )[0]; |
179 | my $repeated = join q{}, map { |
180 | # don't modify the original one |
181 | my $item = $repeatable; |
182 | $item =~ s{(<span[^>]+>)}{$1$_. }xmsg; |
183 | $item |
184 | } 1 .. 2; |
185 | $copy_of_tmpl =~ s{<div.+?</div>}{$repeated}xms; |
186 | $copy_of_tmpl; |
187 | }; |
188 | |
189 | is( |
190 | ( run_for { |
191 | $_->repeat([ |
192 | sub { |
193 | $_->select('span')->transform_content( sub { "1. $_" } ); |
194 | }, |
195 | sub { |
196 | $_->select('span')->transform_content( sub { "2. $_" } ); |
197 | }, |
198 | ]); |
199 | } 'div.main' |
200 | ), |
201 | $expect, |
202 | 'check if transform_content works for repeatables', |
203 | ); |
204 | |
8f962884 |
205 | ($expect = $tmpl) =~ s/(?=<div)/O HAI/; |
456a815d |
206 | |
207 | my $ohai = [ { type => 'TEXT', raw => 'O HAI' } ]; |
208 | |
209 | is( |
210 | run_for { $_->add_before($ohai) }, |
211 | $expect, |
212 | 'add_before ok' |
213 | ); |
214 | |
215 | ($expect = $tmpl) =~ s/(?<=<\/div>)/O HAI/; |
216 | |
217 | is( |
218 | run_for { $_->add_after($ohai) }, |
219 | $expect, |
220 | 'add_after ok' |
221 | ); |
222 | |
223 | ($expect = $tmpl) =~ s/(?<=class="main">)/O HAI/; |
224 | |
225 | is( |
865bb5d2 |
226 | run_for { $_->prepend_content($ohai) }, |
456a815d |
227 | $expect, |
865bb5d2 |
228 | 'prepend_content ok' |
456a815d |
229 | ); |
230 | |
231 | ($expect = $tmpl) =~ s/<hr \/>/<hr>O HAI<\/hr>/; |
232 | |
233 | is( |
865bb5d2 |
234 | (run_for { $_->prepend_content($ohai) } 'hr'), |
456a815d |
235 | $expect, |
865bb5d2 |
236 | 'prepend_content ok with in place close' |
456a815d |
237 | ); |
238 | |
239 | is( |
240 | run_for { $_->replace($ohai) }, |
241 | '<body> |
242 | O HAI |
243 | </body> |
244 | ', |
245 | 'replace ok' |
246 | ); |
247 | |
8f962884 |
248 | @ev = (); |
c86c0ce2 |
249 | |
250 | is( |
251 | run_for { $_->collect({ into => \@ev }) }, |
252 | '<body> |
253 | |
254 | </body> |
255 | ', |
256 | 'collect removes without passthrough' |
257 | ); |
258 | |
259 | is( |
260 | HTML::Zoom::Producer::BuiltIn->html_from_events(\@ev), |
261 | '<div class="main"> |
262 | <span class="hilight name">Bob</span> |
263 | <span class="career">Builder</span> |
264 | <hr /> |
265 | </div>', |
266 | 'collect collected right events' |
267 | ); |
41153e87 |
268 | |
8f962884 |
269 | @ev = (); |
41153e87 |
270 | |
271 | is( |
865bb5d2 |
272 | run_for { $_->collect({ into => \@ev, content => 1 }) }, |
8f962884 |
273 | '<body> |
274 | <div class="main"></div> |
275 | </body> |
276 | ', |
865bb5d2 |
277 | 'collect w/content removes correctly' |
41153e87 |
278 | ); |
279 | |
280 | is( |
8f962884 |
281 | HTML::Zoom::Producer::BuiltIn->html_from_events(\@ev), |
282 | ' |
283 | <span class="hilight name">Bob</span> |
284 | <span class="career">Builder</span> |
285 | <hr /> |
286 | ', |
865bb5d2 |
287 | 'collect w/content collects correctly' |
41153e87 |
288 | ); |
289 | |
11cc25dd |
290 | is( |
865bb5d2 |
291 | run_for { $_->replace($ohai, { content => 1 }) }, |
11cc25dd |
292 | '<body> |
293 | <div class="main">O HAI</div> |
294 | </body> |
295 | ', |
865bb5d2 |
296 | 'replace w/content' |
11cc25dd |
297 | ); |
298 | |
a25b8d4d |
299 | ($expect = $tmpl) =~ s/(?=<\/div>)/O HAI/; |
300 | |
301 | is( |
865bb5d2 |
302 | run_for { $_->append_content($ohai) }, |
a25b8d4d |
303 | $expect, |
865bb5d2 |
304 | 'append content ok' |
a25b8d4d |
305 | ); |
306 | |
865bb5d2 |
307 | my $r_content = sub { my $r = shift; sub { $_->replace($r, { content => 1 }) } }; |
3cdbc13f |
308 | |
309 | is( |
310 | run_for { |
311 | $_->repeat( |
312 | [ |
313 | sub { |
314 | filter |
865bb5d2 |
315 | filter($_ => '.name' => $r_content->('mst')) |
316 | => '.career' => $r_content->('Chainsaw Wielder') |
3cdbc13f |
317 | }, |
318 | sub { |
319 | filter |
865bb5d2 |
320 | filter($_ => '.name' => $r_content->('mdk')) |
321 | => '.career' => $r_content->('Adminion') |
3cdbc13f |
322 | }, |
323 | ] |
324 | ) |
325 | }, |
326 | q{<body> |
327 | <div class="main"> |
328 | <span class="hilight name">mst</span> |
329 | <span class="career">Chainsaw Wielder</span> |
330 | <hr /> |
331 | </div><div class="main"> |
332 | <span class="hilight name">mdk</span> |
333 | <span class="career">Adminion</span> |
334 | <hr /> |
335 | </div> |
336 | </body> |
337 | }, |
338 | 'repeat ok' |
339 | ); |
340 | |
865bb5d2 |
341 | is( |
342 | run_for { |
343 | $_->repeat_content( |
344 | [ |
345 | sub { |
346 | filter |
347 | filter($_ => '.name' => $r_content->('mst')) |
348 | => '.career' => $r_content->('Chainsaw Wielder') |
349 | }, |
350 | sub { |
351 | filter |
352 | filter($_ => '.name' => $r_content->('mdk')) |
353 | => '.career' => $r_content->('Adminion') |
354 | }, |
355 | ] |
356 | ) |
357 | }, |
358 | q{<body> |
359 | <div class="main"> |
360 | <span class="hilight name">mst</span> |
361 | <span class="career">Chainsaw Wielder</span> |
362 | <hr /> |
363 | |
364 | <span class="hilight name">mdk</span> |
365 | <span class="career">Adminion</span> |
366 | <hr /> |
367 | </div> |
368 | </body> |
369 | }, |
370 | 'repeat_content ok' |
371 | ); |
3cdbc13f |
372 | |
dae33531 |
373 | is( |
374 | run_for { |
375 | my @between; |
376 | $_->repeat_content( |
377 | [ |
378 | sub { |
b5a48c47 |
379 | HTML::Zoom::ArrayStream->new({ array => [ |
dae33531 |
380 | (filter |
381 | filter($_ => '.name' => $r_content->('mst')) |
382 | => '.career' => $r_content->('Chainsaw Wielder')), |
b5a48c47 |
383 | HTML::Zoom::ArrayStream->new({ array => \@between }) |
384 | ] })->flatten |
dae33531 |
385 | }, |
386 | sub { |
387 | filter |
388 | filter($_ => '.name' => $r_content->('mdk')) |
389 | => '.career' => $r_content->('Adminion') |
390 | }, |
391 | ], |
392 | { filter => sub { |
393 | filter $_[0] => 'hr' => sub { $_->collect({ into => \@between }) } |
394 | } |
395 | } |
396 | ) |
397 | }, |
398 | q{<body> |
399 | <div class="main"> |
400 | <span class="hilight name">mst</span> |
401 | <span class="career">Chainsaw Wielder</span> |
402 | <hr /> |
403 | <span class="hilight name">mdk</span> |
404 | <span class="career">Adminion</span> |
405 | |
406 | </div> |
407 | </body> |
408 | }, |
409 | 'repeat_content with filter ok' |
410 | ); |
411 | |
f8ed299b |
412 | is( |
413 | run_for { |
414 | my @between; |
415 | $_->repeat_content( |
416 | [ |
417 | sub { |
418 | filter |
419 | filter($_ => '.name' => $r_content->('mst')) |
420 | => '.career' => $r_content->('Chainsaw Wielder') |
421 | }, |
422 | sub { |
423 | filter |
424 | filter($_ => '.name' => $r_content->('mdk')) |
425 | => '.career' => $r_content->('Adminion') |
426 | }, |
427 | ], |
428 | { repeat_between => 'hr' } |
429 | ) |
430 | }, |
431 | q{<body> |
432 | <div class="main"> |
433 | <span class="hilight name">mst</span> |
434 | <span class="career">Chainsaw Wielder</span> |
435 | <hr /> |
436 | <span class="hilight name">mdk</span> |
437 | <span class="career">Adminion</span> |
438 | |
439 | </div> |
440 | </body> |
441 | }, |
442 | 'repeat_content using repeat_between ok' |
443 | ); |
444 | |
456a815d |
445 | done_testing; |