584ec36fa31f1b339b1ad8e428ede7e4f11c4d97
[catagits/HTML-Zoom.git] / t / actions.t
1 use strict;
2 use warnings FATAL => 'all';
3 use Test::More;
4
5 use HTML::Zoom::Parser::BuiltIn;
6 use HTML::Zoom::Producer::BuiltIn;
7 use HTML::Zoom::SelectorParser;
8 use HTML::Zoom::FilterBuilder;
9 use HTML::Zoom::FilterStream;
10
11 my $tmpl = <<END;
12 <body>
13   <div class="main">
14     <span class="hilight name">Bob</span>
15     <span class="career">Builder</span>
16     <hr />
17   </div>
18 </body>
19 END
20
21 sub src_stream { HTML::Zoom::Parser::BuiltIn->new->html_to_stream($tmpl); }
22
23 sub html_sink { HTML::Zoom::Producer::BuiltIn->new->html_from_stream($_[0]) }
24
25 my $fb = HTML::Zoom::FilterBuilder->new;
26
27 my $sp = HTML::Zoom::SelectorParser->new;
28
29 sub filter {
30   my ($stream, $sel, $cb) = @_;
31   return HTML::Zoom::FilterStream->new({
32     stream => $stream,
33     match => $sp->parse_selector($sel),
34     filter => do { local $_ = $fb; $cb->($fb) }
35   });
36 }
37
38 sub run_for (&;$) {
39   my $cb = shift;
40   (html_sink
41     (filter
42       src_stream,
43       (shift or '.main'),
44       $cb
45     )
46   )
47 }
48
49 my ($expect, @ev);
50
51 ($expect = $tmpl) =~ s/class="main"/class="foo"/;
52
53 is(
54   run_for { $_->set_attribute({ name => 'class', value => 'foo' }) },
55   $expect,
56   'set attribute on existing attribute'
57 );
58
59 ($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/;
60
61 is(
62   run_for { $_->set_attribute({ name => 'foo', value => 'bar' }) },
63   $expect,
64   'set attribute on non existing attribute'
65 );
66
67 ($expect = $tmpl) =~ s/class="main"/class="main foo"/;
68
69 is(
70   run_for { $_->add_to_attribute({ name => 'class', value => 'foo' }) },
71   $expect,
72   'add attribute on existing attribute'
73 );
74
75 ($expect = $tmpl) =~ s/class="main"/class="main" foo="bar"/;
76
77 is(
78   run_for { $_->add_to_attribute({ name => 'foo', value => 'bar' }) },
79   $expect,
80   'add attribute on non existing attribute'
81 );
82
83 ($expect = $tmpl) =~ s/ class="main"//;
84
85 is(
86   run_for { $_->remove_attribute({ name => 'class' }) },
87   $expect,
88   'remove attribute on existing attribute'
89 );
90
91 is(
92   run_for { $_->remove_attribute({ name => 'foo' }) },
93   $tmpl,
94   'remove attribute on non existing attribute'
95 );
96
97 ($expect = $tmpl) =~ s/(?=<div)/O HAI/;
98
99 my $ohai = [ { type => 'TEXT', raw => 'O HAI' } ];
100
101 is(
102   run_for { $_->add_before($ohai) },
103   $expect,
104   'add_before ok'
105 );
106
107 ($expect = $tmpl) =~ s/(?<=<\/div>)/O HAI/;
108
109 is(
110   run_for { $_->add_after($ohai) },
111   $expect,
112   'add_after ok'
113 );
114
115 ($expect = $tmpl) =~ s/(?<=class="main">)/O HAI/;
116
117 is(
118   run_for { $_->prepend_content($ohai) },
119   $expect,
120   'prepend_content ok'
121 );
122
123 ($expect = $tmpl) =~ s/<hr \/>/<hr>O HAI<\/hr>/;
124
125 is(
126   (run_for { $_->prepend_content($ohai) } 'hr'),
127   $expect,
128   'prepend_content ok with in place close'
129 );
130
131 is(
132   run_for { $_->replace($ohai) },
133 '<body>
134   O HAI
135 </body>
136 ',
137   'replace ok'
138 );
139
140 @ev = ();
141
142 is(
143   run_for { $_->collect({ into => \@ev }) },
144   '<body>
145   
146 </body>
147 ',
148   'collect removes without passthrough'
149 );
150
151 is(
152   HTML::Zoom::Producer::BuiltIn->html_from_events(\@ev),
153   '<div class="main">
154     <span class="hilight name">Bob</span>
155     <span class="career">Builder</span>
156     <hr />
157   </div>',
158   'collect collected right events'
159 );
160
161 @ev = ();
162
163 is(
164   run_for { $_->collect({ into => \@ev, content => 1 }) },
165   '<body>
166   <div class="main"></div>
167 </body>
168 ',
169   'collect w/content removes correctly'
170 );
171
172 is(
173   HTML::Zoom::Producer::BuiltIn->html_from_events(\@ev),
174   '
175     <span class="hilight name">Bob</span>
176     <span class="career">Builder</span>
177     <hr />
178   ',
179   'collect w/content collects correctly'
180 );
181
182 is(
183   run_for { $_->replace($ohai, { content => 1 }) },
184   '<body>
185   <div class="main">O HAI</div>
186 </body>
187 ',
188   'replace w/content'
189 );
190
191 ($expect = $tmpl) =~ s/(?=<\/div>)/O HAI/;
192
193 is(
194   run_for { $_->append_content($ohai) },
195   $expect,
196   'append content ok'
197 );
198
199 my $r_content = sub { my $r = shift; sub { $_->replace($r, { content => 1 }) } };
200
201 is(
202   run_for {
203     $_->repeat(
204       [
205         sub {
206           filter
207             filter($_ => '.name' => $r_content->('mst'))
208             => '.career' => $r_content->('Chainsaw Wielder')
209         },
210         sub {
211           filter
212             filter($_ => '.name' => $r_content->('mdk'))
213             => '.career' => $r_content->('Adminion')
214         },
215       ]
216     )
217   },
218   q{<body>
219   <div class="main">
220     <span class="hilight name">mst</span>
221     <span class="career">Chainsaw Wielder</span>
222     <hr />
223   </div><div class="main">
224     <span class="hilight name">mdk</span>
225     <span class="career">Adminion</span>
226     <hr />
227   </div>
228 </body>
229 },
230   'repeat ok'
231 );
232
233 is(
234   run_for {
235     $_->repeat_content(
236       [
237         sub {
238           filter
239             filter($_ => '.name' => $r_content->('mst'))
240             => '.career' => $r_content->('Chainsaw Wielder')
241         },
242         sub {
243           filter
244             filter($_ => '.name' => $r_content->('mdk'))
245             => '.career' => $r_content->('Adminion')
246         },
247       ]
248     )
249   },
250   q{<body>
251   <div class="main">
252     <span class="hilight name">mst</span>
253     <span class="career">Chainsaw Wielder</span>
254     <hr />
255   
256     <span class="hilight name">mdk</span>
257     <span class="career">Adminion</span>
258     <hr />
259   </div>
260 </body>
261 },
262   'repeat_content ok'
263 );
264
265 is(
266   run_for {
267     my @between;
268     $_->repeat_content(
269       [
270         sub {
271           HTML::Zoom::ArrayStream->new({ array => [
272             (filter
273               filter($_ => '.name' => $r_content->('mst'))
274               => '.career' => $r_content->('Chainsaw Wielder')),
275             HTML::Zoom::ArrayStream->new({ array => \@between })
276           ] })->flatten
277         },
278         sub {
279           filter
280             filter($_ => '.name' => $r_content->('mdk'))
281             => '.career' => $r_content->('Adminion')
282         },
283       ],
284       { filter => sub {
285           filter $_[0] => 'hr' => sub { $_->collect({ into => \@between }) }
286         }
287       }
288     )
289   },
290   q{<body>
291   <div class="main">
292     <span class="hilight name">mst</span>
293     <span class="career">Chainsaw Wielder</span>
294     <hr />
295     <span class="hilight name">mdk</span>
296     <span class="career">Adminion</span>
297     
298   </div>
299 </body>
300 },
301   'repeat_content with filter ok'
302 );
303
304 is(
305   run_for {
306     my @between;
307     $_->repeat_content(
308       [
309         sub {
310           filter
311             filter($_ => '.name' => $r_content->('mst'))
312             => '.career' => $r_content->('Chainsaw Wielder')
313         },
314         sub {
315           filter
316             filter($_ => '.name' => $r_content->('mdk'))
317             => '.career' => $r_content->('Adminion')
318         },
319       ],
320       { repeat_between => 'hr' }
321     )
322   },
323   q{<body>
324   <div class="main">
325     <span class="hilight name">mst</span>
326     <span class="career">Chainsaw Wielder</span>
327     <hr />
328     <span class="hilight name">mdk</span>
329     <span class="career">Adminion</span>
330     
331   </div>
332 </body>
333 },
334   'repeat_content using repeat_between ok'
335 );
336
337 done_testing;