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