added test for transform_content using repeatables
[catagits/HTML-Zoom.git] / t / actions.t
CommitLineData
1cf03540 1use strictures 1;
456a815d 2use Test::More;
3
4use HTML::Zoom::Parser::BuiltIn;
5use HTML::Zoom::Producer::BuiltIn;
6use HTML::Zoom::SelectorParser;
7use HTML::Zoom::FilterBuilder;
8use HTML::Zoom::FilterStream;
9
10my $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>
18END
19
d80786d0 20sub src_stream { HTML::Zoom::Parser::BuiltIn->new->html_to_stream($tmpl); }
456a815d 21
d80786d0 22sub html_sink { HTML::Zoom::Producer::BuiltIn->new->html_from_stream($_[0]) }
456a815d 23
24my $fb = HTML::Zoom::FilterBuilder->new;
25
26my $sp = HTML::Zoom::SelectorParser->new;
27
28sub 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
37sub 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 48my ($expect, @ev);
49
50($expect = $tmpl) =~ s/class="main"/class="foo"/;
51
52is(
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
60is(
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
68is(
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
76is(
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
84is(
85 run_for { $_->remove_attribute({ name => 'class' }) },
86 $expect,
87 'remove attribute on existing attribute'
88);
89
90is(
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
98is(
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
115is(
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
131is(
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
142is(
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
155is(
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
166is(
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
189is(
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
207my $ohai = [ { type => 'TEXT', raw => 'O HAI' } ];
208
209is(
210 run_for { $_->add_before($ohai) },
211 $expect,
212 'add_before ok'
213);
214
215($expect = $tmpl) =~ s/(?<=<\/div>)/O HAI/;
216
217is(
218 run_for { $_->add_after($ohai) },
219 $expect,
220 'add_after ok'
221);
222
223($expect = $tmpl) =~ s/(?<=class="main">)/O HAI/;
224
225is(
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
233is(
865bb5d2 234 (run_for { $_->prepend_content($ohai) } 'hr'),
456a815d 235 $expect,
865bb5d2 236 'prepend_content ok with in place close'
456a815d 237);
238
239is(
240 run_for { $_->replace($ohai) },
241'<body>
242 O HAI
243</body>
244',
245 'replace ok'
246);
247
8f962884 248@ev = ();
c86c0ce2 249
250is(
251 run_for { $_->collect({ into => \@ev }) },
252 '<body>
253
254</body>
255',
256 'collect removes without passthrough'
257);
258
259is(
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
271is(
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
280is(
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 290is(
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
301is(
865bb5d2 302 run_for { $_->append_content($ohai) },
a25b8d4d 303 $expect,
865bb5d2 304 'append content ok'
a25b8d4d 305);
306
865bb5d2 307my $r_content = sub { my $r = shift; sub { $_->replace($r, { content => 1 }) } };
3cdbc13f 308
309is(
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 341is(
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 373is(
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 412is(
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 445done_testing;