merge helpers
[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>
3c53c439 12 <div name="cow" class="main">
456a815d 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
3c53c439 58($expect = $tmpl) =~ s/name="cow" class="main"/name="bar" class="foo"/;
59
60is(
c84b68f3 61 run_for { $_->set_attr({ 'class' => 'foo', 'name' => 'bar'}) },
3c53c439 62 $expect,
c84b68f3 63 'set attributes using hashref form (shorthand)'
3c53c439 64);
65
8f962884 66($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/;
67
68is(
f0ddc273 69 run_for { $_->set_attribute( 'foo' => 'bar' ) },
8f962884 70 $expect,
71 'set attribute on non existing attribute'
72);
73
74($expect = $tmpl) =~ s/class="main"/class="main foo"/;
75
76is(
f0ddc273 77 run_for { $_->add_to_attribute( 'class' => 'foo' ) },
8f962884 78 $expect,
79 'add attribute on existing attribute'
80);
81
bf07288f 82($expect = $tmpl) =~ s/class="main"/class="main foo"/;
83
84is(
85 run_for { $_->add_class('foo') },
86 $expect,
87 'add attribute on existing attribute (shorthand)'
88);
89
90($expect = $tmpl) =~ s/class="main"/class="main" id="foo"/;
91
92is(
93 run_for { $_->set_id('foo') },
94 $expect,
95 'set_id (shorthand)'
96);
97
8f962884 98($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/;
99
100is(
f0ddc273 101 run_for { $_->add_to_attribute( 'foo' => 'bar' ) },
8f962884 102 $expect,
103 'add attribute on non existing attribute'
104);
105
106($expect = $tmpl) =~ s/ class="main"//;
107
108is(
109 run_for { $_->remove_attribute({ name => 'class' }) },
110 $expect,
111 'remove attribute on existing attribute'
112);
113
114is(
115 run_for { $_->remove_attribute({ name => 'foo' }) },
116 $tmpl,
117 'remove attribute on non existing attribute'
118);
119
c84b68f3 120($expect = $tmpl) =~ s/class="main"/class=""/;
121
122is(
123 run_for { $_->remove_from_attribute({ class => 'main' }) },
124 $expect,
125 'remove name from attribute'
126);
127
5cac799e 128($expect = $tmpl) =~ s/ class="main"//;
129
130is(
131 run_for {
132 $_->transform_attribute({
133 name => 'class',
134 code => sub {
135 my $a = shift;
136 return if $a eq 'main';
137 return $a;
138 },
139 })
140 },
141 $expect,
142 'transform_attribute deletes the attr if code returns undef',
143 );
144
145($expect = $tmpl) =~ s/ class="main"/ class="moan"/;
146
147is(
148 run_for {
149 $_->transform_attribute({
150 name => 'class',
151 code => sub {
152 ( my $b = shift ) =~ s/main/moan/;
153 $b
154 },
155 })
156 },
157 $expect,
158 'transform_attribute transforms something',
159 );
160
161($expect = $tmpl) =~ s/ class="main"/ class="main" noggin="zonk"/;
162
163is(
164 run_for {
165 $_->transform_attribute({
166 name => 'noggin',
167 code => sub { 'zonk' },
168 })
169 },
170 $expect,
171 'transform_attribute adds attribute if not there before',
172 );
173
174is(
175 run_for {
176 $_->transform_attribute({
177 name => 'noggin',
178 code => sub { },
179 })
180 },
181 $tmpl,
182 'transform_attribute on nonexistent att does not add it if code returns undef',
183 );
184
185
8f962884 186($expect = $tmpl) =~ s/(?=<div)/O HAI/;
456a815d 187
188my $ohai = [ { type => 'TEXT', raw => 'O HAI' } ];
189
190is(
191 run_for { $_->add_before($ohai) },
192 $expect,
193 'add_before ok'
194);
195
196($expect = $tmpl) =~ s/(?<=<\/div>)/O HAI/;
197
198is(
199 run_for { $_->add_after($ohai) },
200 $expect,
201 'add_after ok'
202);
203
204($expect = $tmpl) =~ s/(?<=class="main">)/O HAI/;
205
206is(
865bb5d2 207 run_for { $_->prepend_content($ohai) },
456a815d 208 $expect,
865bb5d2 209 'prepend_content ok'
456a815d 210);
211
212($expect = $tmpl) =~ s/<hr \/>/<hr>O HAI<\/hr>/;
213
214is(
865bb5d2 215 (run_for { $_->prepend_content($ohai) } 'hr'),
456a815d 216 $expect,
865bb5d2 217 'prepend_content ok with in place close'
456a815d 218);
219
220is(
221 run_for { $_->replace($ohai) },
222'<body>
223 O HAI
224</body>
225',
226 'replace ok'
227);
228
8f962884 229@ev = ();
c86c0ce2 230
231is(
232 run_for { $_->collect({ into => \@ev }) },
233 '<body>
234
235</body>
236',
237 'collect removes without passthrough'
238);
239
240is(
241 HTML::Zoom::Producer::BuiltIn->html_from_events(\@ev),
3c53c439 242 '<div name="cow" class="main">
c86c0ce2 243 <span class="hilight name">Bob</span>
244 <span class="career">Builder</span>
245 <hr />
246 </div>',
247 'collect collected right events'
248);
41153e87 249
8f962884 250@ev = ();
41153e87 251
252is(
865bb5d2 253 run_for { $_->collect({ into => \@ev, content => 1 }) },
8f962884 254 '<body>
3c53c439 255 <div name="cow" class="main"></div>
8f962884 256</body>
257',
865bb5d2 258 'collect w/content removes correctly'
41153e87 259);
260
261is(
8f962884 262 HTML::Zoom::Producer::BuiltIn->html_from_events(\@ev),
263 '
264 <span class="hilight name">Bob</span>
265 <span class="career">Builder</span>
266 <hr />
267 ',
865bb5d2 268 'collect w/content collects correctly'
41153e87 269);
270
11cc25dd 271is(
865bb5d2 272 run_for { $_->replace($ohai, { content => 1 }) },
11cc25dd 273 '<body>
3c53c439 274 <div name="cow" class="main">O HAI</div>
11cc25dd 275</body>
276',
865bb5d2 277 'replace w/content'
11cc25dd 278);
279
a25b8d4d 280($expect = $tmpl) =~ s/(?=<\/div>)/O HAI/;
281
282is(
865bb5d2 283 run_for { $_->append_content($ohai) },
a25b8d4d 284 $expect,
865bb5d2 285 'append content ok'
a25b8d4d 286);
287
865bb5d2 288my $r_content = sub { my $r = shift; sub { $_->replace($r, { content => 1 }) } };
3cdbc13f 289
290is(
291 run_for {
292 $_->repeat(
293 [
294 sub {
295 filter
865bb5d2 296 filter($_ => '.name' => $r_content->('mst'))
297 => '.career' => $r_content->('Chainsaw Wielder')
3cdbc13f 298 },
299 sub {
300 filter
865bb5d2 301 filter($_ => '.name' => $r_content->('mdk'))
302 => '.career' => $r_content->('Adminion')
3cdbc13f 303 },
304 ]
305 )
306 },
307 q{<body>
3c53c439 308 <div name="cow" class="main">
3cdbc13f 309 <span class="hilight name">mst</span>
310 <span class="career">Chainsaw Wielder</span>
311 <hr />
3c53c439 312 </div><div name="cow" class="main">
3cdbc13f 313 <span class="hilight name">mdk</span>
314 <span class="career">Adminion</span>
315 <hr />
316 </div>
317</body>
318},
319 'repeat ok'
320);
321
865bb5d2 322is(
323 run_for {
324 $_->repeat_content(
325 [
326 sub {
327 filter
328 filter($_ => '.name' => $r_content->('mst'))
329 => '.career' => $r_content->('Chainsaw Wielder')
330 },
331 sub {
332 filter
333 filter($_ => '.name' => $r_content->('mdk'))
334 => '.career' => $r_content->('Adminion')
335 },
336 ]
337 )
338 },
339 q{<body>
3c53c439 340 <div name="cow" class="main">
865bb5d2 341 <span class="hilight name">mst</span>
342 <span class="career">Chainsaw Wielder</span>
343 <hr />
344
345 <span class="hilight name">mdk</span>
346 <span class="career">Adminion</span>
347 <hr />
348 </div>
349</body>
350},
351 'repeat_content ok'
352);
3cdbc13f 353
dae33531 354is(
355 run_for {
356 my @between;
357 $_->repeat_content(
358 [
359 sub {
b5a48c47 360 HTML::Zoom::ArrayStream->new({ array => [
dae33531 361 (filter
362 filter($_ => '.name' => $r_content->('mst'))
363 => '.career' => $r_content->('Chainsaw Wielder')),
b5a48c47 364 HTML::Zoom::ArrayStream->new({ array => \@between })
365 ] })->flatten
dae33531 366 },
367 sub {
368 filter
369 filter($_ => '.name' => $r_content->('mdk'))
370 => '.career' => $r_content->('Adminion')
371 },
372 ],
373 { filter => sub {
374 filter $_[0] => 'hr' => sub { $_->collect({ into => \@between }) }
375 }
376 }
377 )
378 },
379 q{<body>
3c53c439 380 <div name="cow" class="main">
dae33531 381 <span class="hilight name">mst</span>
382 <span class="career">Chainsaw Wielder</span>
383 <hr />
384 <span class="hilight name">mdk</span>
385 <span class="career">Adminion</span>
386
387 </div>
388</body>
389},
390 'repeat_content with filter ok'
391);
392
f8ed299b 393is(
394 run_for {
395 my @between;
396 $_->repeat_content(
397 [
398 sub {
399 filter
400 filter($_ => '.name' => $r_content->('mst'))
401 => '.career' => $r_content->('Chainsaw Wielder')
402 },
403 sub {
404 filter
405 filter($_ => '.name' => $r_content->('mdk'))
406 => '.career' => $r_content->('Adminion')
407 },
408 ],
409 { repeat_between => 'hr' }
410 )
411 },
412 q{<body>
3c53c439 413 <div name="cow" class="main">
f8ed299b 414 <span class="hilight name">mst</span>
415 <span class="career">Chainsaw Wielder</span>
416 <hr />
417 <span class="hilight name">mdk</span>
418 <span class="career">Adminion</span>
419
420 </div>
421</body>
422},
423 'repeat_content using repeat_between ok'
424);
425
456a815d 426done_testing;