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