allow use of the new template system from static pages
[bse.git] / t / t20gen.t
1 #!perl -w
2 use strict;
3 use BSE::Test ();
4 use Test::More tests=>147;
5 use File::Spec;
6 use FindBin;
7 BEGIN {
8   my $cgidir = File::Spec->catdir(BSE::Test::base_dir, 'cgi-bin');
9   ok(chdir $cgidir, "switch to CGI directory");
10   push @INC, 'modules';
11 }
12 use BSE::API qw(bse_init bse_cfg bse_make_article);
13
14 bse_init(".");
15
16 my $cfg = bse_cfg();
17
18 use BSE::Util::SQL qw/sql_datetime/;
19 use DevHelp::Date qw(dh_strftime_sql_datetime);
20
21 sub template_test($$$$);
22 sub dyn_template_test($$$$);
23
24 my $parent = add_article
25   (
26    title=>'Parent', 
27    body=>'parent article doclink[shop|foo]',
28    lastModified => '2004-09-23 06:00:00',
29    threshold => 2,
30   );
31 ok($parent, "create section");
32 my @kids;
33 for my $name ('One', 'Two', 'Three') {
34   my $kid = add_article
35     (
36      title => $name, parentid => $parent->{id}, 
37      body => "b[$name] - alpha, beta, gamma, delta, epsilon",
38      summaryLength => 35,
39     );
40   ok($kid, "creating kid $name");
41   push(@kids, $kid);
42 }
43
44 my $grandkid = add_article
45   (
46    parentid => $kids[1]{id},
47    title => "Grandkid",
48    body => "grandkid",
49   );
50
51 my $base_securl = $cfg->entryVar("site", "secureurl");
52
53 # make parent a step child of itself
54 require BSE::Admin::StepParents;
55 BSE::Admin::StepParents->add($parent, $parent);
56
57 is($parent->section->{id}, $parent->{id}, "parent should be it's own section");
58 is($kids[0]->section->{id}, $parent->{id}, "kids section should be the parent");
59
60 my $top = Articles->getByPkey(1);
61 ok($top, "grabbing Home page");
62
63 template_test "cfg", $top, <<TEMPLATE, <<EXPECTED;
64 <:cfg "no such section" somekey "default / value":>
65 TEMPLATE
66 default / value
67 EXPECTED
68
69 template_test "formats", $top, <<TEMPLATE, <<EXPECTED;
70 <:arithmetic 10 |%05d:>
71 TEMPLATE
72 00010
73 EXPECTED
74
75 template_test "children_of", $top, <<TEMPLATE, <<EXPECTED;
76 <:iterator begin children_of $parent->{id}:><:
77 ofchild title:>
78 <:iterator end children_of:>
79 <:-.set myart = articles.getByPkey($parent->{id}):>
80 <:-.for a in [ myart.visible_kids ]:>
81 <:-= a.title |html :>
82 <:.end for-:>
83 TEMPLATE
84 Three
85 Two
86 One
87 Three
88 Two
89 One
90 EXPECTED
91
92 template_test "allkids_of", $top, <<TEMPLATE, <<EXPECTED;
93 <:iterator begin allkids_of $parent->{id}:><:
94 ofallkid title:>
95 <:iterator end allkids_of:>
96 TEMPLATE
97 Parent
98 Three
99 Two
100 One
101
102 EXPECTED
103
104 template_test "allkids_of filtered", $top, <<TEMPLATE, <<EXPECTED;
105 <:iterator begin allkids_of $parent->{id} filter: [title] =~ /o/i :><:
106 ofallkid title:>
107 <:iterator end allkids_of:>
108 TEMPLATE
109 Two
110 One
111
112 EXPECTED
113
114 my @kidids = map $_->{id}, @kids;
115 template_test "inlines", $top, <<TEMPLATE, <<EXPECTED;
116 <:iterator begin inlines @kidids:><:
117 inline title:><:iterator end inlines:>
118 TEMPLATE
119 OneTwoThree
120 EXPECTED
121
122 template_test "inlines filtered", $top, <<TEMPLATE, <<EXPECTED;
123 <:iterator begin inlines @kidids filter: [title] =~ /^T/ :><:
124 inline title:><:iterator end inlines:>
125 TEMPLATE
126 TwoThree
127 EXPECTED
128
129 template_test "ifancestor positive", $kids[0], <<TEMPLATE, <<EXPECTED;
130 <:ifAncestor $parent->{id}:>Yes<:or:>No<:eif:>
131 TEMPLATE
132 Yes
133 EXPECTED
134
135 template_test "ifancestor equal", $kids[0], <<TEMPLATE, <<EXPECTED;
136 <:ifAncestor $kids[0]{id}:>Yes<:or:>No<:eif:>
137 TEMPLATE
138 Yes
139 EXPECTED
140
141 template_test "ifancestor negative", $kids[0], <<TEMPLATE, <<EXPECTED;
142 <:ifAncestor $kids[1]{id}:>Yes<:or:>No<:eif:>
143 TEMPLATE
144 No
145 EXPECTED
146
147 template_test "children", $parent, <<TEMPLATE, <<EXPECTED;
148 <:iterator begin children:><:
149 child title:>
150 <:iterator end children:>
151 TEMPLATE
152 Three
153 Two
154 One
155
156 EXPECTED
157
158 template_test "embed children", $top, <<TEMPLATE, <<EXPECTED;
159 <:embed $parent->{id} test/children.tmpl:>
160 TEMPLATE
161 Three
162 Two
163 One
164
165
166 EXPECTED
167
168 # test some of the newer basic tags
169 template_test "add", $top, <<TEMPLATE, <<EXPECTED;
170 <:add 3 4:>
171 <:add 3 4 5:>
172 <:add 3 [add 4 5]:>
173 TEMPLATE
174 7
175 12
176 12
177 EXPECTED
178
179 template_test "concatenate", $top, <<TEMPLATE, <<EXPECTED;
180 <:concatenate one two:>
181 <:concatenate one "two " three:>
182 <:concatenate one [concatenate "two " three]:>
183 <:concatenate [concatenate "one" [concatenate "two" "three"]]:>
184 <:cat [cat "one" "two"] [concatenate "three" "four"]:>
185 TEMPLATE
186 onetwo
187 onetwo three
188 onetwo three
189 onetwothree
190 onetwothreefour
191 EXPECTED
192
193 template_test "cond", $top, <<TEMPLATE, <<EXPECTED;
194 <:cond 1 "true" false:>
195 <:cond 0 true false:>
196 <:cond "" true false:>
197 TEMPLATE
198 true
199 false
200 false
201 EXPECTED
202
203 template_test "match", $top, <<'TEMPLATE', <<EXPECTED;
204 <:match "abc123" "(\d+)":>
205 <:match "abc 123" "(\w+)\s+(\w+)" "$2$1":>
206 <:match "abc 123" "(\w+)X(\w+)" "$2$1":>
207 <:match "abc 123" "(\w+)X(\w+)" "$2$1" "default":>
208 TEMPLATE
209 123
210 123abc
211
212 default
213 EXPECTED
214
215 template_test "replace", $top, <<'TEMPLATE', <<EXPECTED;
216 <:replace "abc123" "(\d+)" "XXX" :>
217 <:replace "!!abc 123!!" "(\w+)\s+(\w+)" "$2$1":>
218 <:replace "abc 123" "(\w+)" "XXX" g:>
219 <:replace "abc 123" "X" "$1" :>
220 <:replace "abc
221 123
222 xyz" "\n" "\\n" g:>
223 TEMPLATE
224 abcXXX
225 !!123abc!!
226 XXX XXX
227 abc 123
228 abc\\n123\\nxyz
229 EXPECTED
230
231 template_test "cases", $top, <<'TEMPLATE', <<EXPECTED;
232 <:lc "AbC123 XYZ":>
233 <:uc "aBc123 xyz":>
234 <:lcfirst "AbC123 XYZ":>
235 <:ucfirst "aBc123 xyz":>
236 <:capitalize "alpha beta gamma":>
237 <:capitalize "'one day, but don't', 'we know'":>
238 <:capitalize "IBM stock soars":>
239 TEMPLATE
240 abc123 xyz
241 ABC123 XYZ
242 abC123 XYZ
243 ABc123 xyz
244 Alpha Beta Gamma
245 'One Day, But Don't', 'We Know'
246 Ibm Stock Soars
247 EXPECTED
248
249 template_test "arithmetic", $top, <<'TEMPLATE', <<EXPECTED;
250 <:arithmetic 2+2:>
251 <:arithmetic 2+[add 1 1]:>
252 <:arithmetic d2:1.234+1.542:>
253 <:arithmetic 2+ [add 1 2] + [undefinedtag x] + [add 1 1] + [undefinedtag2]:>
254 TEMPLATE
255 4
256 4
257 2.78
258 <:arithmetic 2+ [add 1 2] + [undefinedtag x] + [add 1 1] + [undefinedtag2]:>
259 EXPECTED
260
261 template_test "nobodytext", $kids[0], <<'TEMPLATE', <<EXPECTED;
262 <:nobodytext article body:>
263 TEMPLATE
264 One - alpha, beta, gamma, delta, epsilon
265 EXPECTED
266
267 {
268   my $formatted = dh_strftime_sql_datetime("%a %d/%m/%Y", $parent->lastModified);
269   template_test "date", $parent, <<'TEMPLATE', <<EXPECTED;
270 <:date "%a %d/%m/%Y" article lastModified:>
271 TEMPLATE
272 $formatted
273 EXPECTED
274 }
275
276 use POSIX;
277 template_test "today", $parent, <<'TEMPLATE', strftime("%Y-%m-%d %d-%b-%Y\n", localtime);
278 <:today "%Y-%m-%d":> <:today:>
279 TEMPLATE
280
281 SKIP:
282 {
283   eval {
284     require Date::Format;
285   };
286
287   $@
288     and skip("No Date::Format", 3);
289
290   my $today = Date::Format::strftime("%a %o %B %Y", [ localtime ]);
291   my $mod = dh_strftime_sql_datetime("%A %o %B %Y", $parent->lastModified);
292   template_test "date/today w/Date::Format", $parent, <<'TEMPLATE', <<EXPECTED;
293 <:date "%A %o %B %Y" article lastModified:> <:today "%a %o %B %Y":>
294 TEMPLATE
295 $mod $today
296 EXPECTED
297 }
298
299 template_test "strepeats", $parent, <<'TEMPLATE', <<EXPECTED;
300 <:iterator begin strepeats [arithmetic 1+1]:><:strepeat index:> <:strepeat value:>
301 <:iterator end strepeats:>
302 TEMPLATE
303 0 1
304 1 2
305
306 EXPECTED
307
308 template_test "strepeats2", $parent, <<'TEMPLATE', <<EXPECTED;
309 <:iterator begin strepeats [arithmetic 1+1] 5:><:strepeat index:> <:strepeat value:>
310 <:iterator end strepeats:>
311 TEMPLATE
312 0 2
313 1 3
314 2 4
315 3 5
316
317 EXPECTED
318
319 {
320   my $mod_iso = dh_strftime_sql_datetime("%FT%T%z", $parent->lastModified);
321   (my $mod_iso2 = $mod_iso) =~ s/(\d\d)$/:$1/;
322   template_test "quotedreplace", $parent, <<'TEMPLATE', <<EXPECTED;
323 <:date "%FT%T%z" article lastModified:>
324 <meta name="DC.title" content="<:article title:>" />
325 <meta name="DC.date" content="<:replace [date "%FT%T%z" article lastModified] "(\d\d)$" ":$1":>" />
326 <meta name="DC.format" content="<:cfg site format "text/html":>" />
327 TEMPLATE
328 $mod_iso
329 <meta name="DC.title" content="Parent" />
330 <meta name="DC.date" content="$mod_iso2" />
331 <meta name="DC.format" content="text/html" />
332 EXPECTED
333 }
334
335 template_test "report", $parent, <<'TEMPLATE', <<EXPECTED;
336 <:report bse_test test/testrep 2:>
337 <:report bse_test test/testrep [article id]:>
338 TEMPLATE
339 Report: Test report
340 id title
341 2 [index subsection]
342 Report: Test report
343 id title
344 $parent->{id} Parent
345 EXPECTED
346
347 template_test "body", $parent, <<'TEMPLATE', <<EXPECTED;
348 <:body:>
349 TEMPLATE
350 <p>parent article <a href="$base_securl/shop/" title="The Shop" class="doclink">foo</a></p>
351 EXPECTED
352
353 # not actually generation tests, but chekcs that the is_step_ancestor works
354 ok($kids[0]->is_step_ancestor($parent->{id}),
355    "is_step_ancestor - check normal parent");
356 ok($parent->is_step_ancestor($parent->{id}),
357    "is_step_ancestor - check step parent");
358 ok(!$parent->is_step_ancestor($kids[0]),
359    "is_step_ancestor - failure check");
360
361 # and test the static tag
362 template_test "ifStepAncestor 1", $parent, <<'TEMPLATE', <<EXPECTED;
363 <:ifStepAncestor article:>Good<:or:>bad<:eif:>
364 <:ifStepAncestor 3:>Bad<:or:>Good<:eif:>
365 TEMPLATE
366 Good
367 Good
368 EXPECTED
369
370 template_test "ifStepAncestor 2", $kids[0], <<TEMPLATE, <<EXPECTED;
371 <:ifStepAncestor parent:>Good<:or:>bad<:eif:>
372 <:ifStepAncestor article:>Good<:or:>Bad<:eif:>
373 <:ifStepAncestor $kids[2]{id}:>Bad<:or:>Good<:eif:>
374 TEMPLATE
375 Good
376 Good
377 Good
378 EXPECTED
379
380 template_test "ifAnd dynamic cfg ajax", $parent, <<TEMPLATE, <<EXPECTED;
381 <:ifAnd [ifDynamic] [cfg basic ajax]:>1<:or:>0<:eif:>
382 TEMPLATE
383 0
384 EXPECTED
385
386 template_test "replace complex re", $parent, <<'TEMPLATE', <<EXPECTED;
387 <:replace "test&amp;test 01234567890123456789" ((?:&[^;]*;|[^&]){16}).* $1...:>
388 TEMPLATE
389 test&amp;test 012345...
390 EXPECTED
391
392 template_test "summary", $kids[0], <<'TEMPLATE', <<EXPECTED;
393 <:summary article:>
394 <:summary article 14:>
395 TEMPLATE
396 One - alpha, beta, gamma, delta,...
397 One - alpha,...
398 EXPECTED
399
400 template_test "ifUnderThreshold parent children", $parent, <<'TEMPLATE', <<EXPECTED;
401 <:ifUnderThreshold:>1<:or:>0<:eif:>
402 <:ifUnderThreshold children:>1<:or:>0<:eif:>
403 TEMPLATE
404 0
405 0
406 EXPECTED
407
408 template_test "ifUnderThreshold parent allkids", $parent, <<'TEMPLATE', <<EXPECTED;
409 <:ifUnderThreshold allkids:>1<:or:>0<:eif:>
410 TEMPLATE
411 0
412 EXPECTED
413
414 template_test "ifUnderThreshold parent stepkids", $parent, <<'TEMPLATE', <<EXPECTED;
415 <:ifUnderThreshold stepkids:>1<:or:>0<:eif:>
416 TEMPLATE
417 1
418 EXPECTED
419
420 template_test "ifUnderThreshold child children", $kids[0], <<'TEMPLATE', <<EXPECTED;
421 <:ifUnderThreshold:>1<:or:>0<:eif:>
422 <:ifUnderThreshold children:>1<:or:>0<:eif:>
423 TEMPLATE
424 1
425 1
426 EXPECTED
427
428 template_test "ifUnderThreshold child allkids", $kids[0], <<'TEMPLATE', <<EXPECTED;
429 <:ifUnderThreshold allkids:>1<:or:>0<:eif:>
430 TEMPLATE
431 1
432 EXPECTED
433
434 template_test "ifUnderThreshold child stepkids", $kids[0], <<'TEMPLATE', <<EXPECTED;
435 <:ifUnderThreshold stepkids:>1<:or:>0<:eif:>
436 TEMPLATE
437 1
438 EXPECTED
439
440 template_test "noreplace undefined", $parent, <<'TEMPLATE', <<'EXPECTED';
441 <:switch:><:case Dynallkids_of2 dynofallkid filter: [listed] != 2 :><:if Or [ifAncestor dynofallkid] [ifEq [cgi catid] [dynofallkid id]]:>
442 contentA
443   <:iterator begin dynallkids_of2 dynofallkid filter: [listed] != 2 && [generator] =~ /Catalog/ :>
444 contentB
445 <:ifEq [dynarticle id] [dynofallkid2 id]:> focus<:or:><:eif:>
446 <:ifAncestor dynofallkid2:> hilite<:or:><:eif:>
447 <:ifFirstDynofallkid2:> first<:or:><:eif:>
448 <:ifLastDynofallkid2:> last<:or:><:eif:>
449 <:ifDynallkids_of3 dynofallkid2 filter: [listed] != 2 :> parent<:or:><:eif:>
450 <:ifDynofallkid2 titleAlias:><:dynofallkid2 titleAlias:><:or:><:dynofallkid2 title:><:eif:>
451 <:iterator separator dynallkids_of2:>
452 <:iterator end dynallkids_of2:>
453 <:or Or:><:eif Or:><:endswitch:>
454 TEMPLATE
455 <:switch:><:case Dynallkids_of2 dynofallkid filter: [listed] != 2 :><:if Or [ifAncestor dynofallkid] [ifEq [cgi catid] [dynofallkid id]]:>
456 contentA
457   <:iterator begin dynallkids_of2 dynofallkid filter: [listed] != 2 && [generator] =~ /Catalog/ :>
458 contentB
459 <:ifEq [dynarticle id] [dynofallkid2 id]:> focus<:or:><:eif:>
460 <:ifAncestor dynofallkid2:> hilite<:or:><:eif:>
461 <:ifFirstDynofallkid2:> first<:or:><:eif:>
462 <:ifLastDynofallkid2:> last<:or:><:eif:>
463 <:ifDynallkids_of3 dynofallkid2 filter: [listed] != 2 :> parent<:or:><:eif:>
464 <:ifDynofallkid2 titleAlias:><:dynofallkid2 titleAlias:><:or:><:dynofallkid2 title:><:eif:>
465 <:iterator separator dynallkids_of2:>
466 <:iterator end dynallkids_of2:>
467 <:or Or:><:eif Or:><:endswitch:>
468 EXPECTED
469
470 ############################################################
471 # dynamic stuff
472 require BSE::Dynamic::Article;
473 require BSE::Request::Test;
474 my $req = BSE::Request::Test->new(cfg => $cfg);
475 my $gen = BSE::Dynamic::Article->new($req);
476
477 dyn_template_test "dynallkidsof", $parent, <<TEMPLATE, <<EXPECTED;
478 <:iterator begin dynallkids_of $parent->{id} filter: [title] =~ /o/i :><:
479 dynofallkid title:> <:next_dynofallkid title:> <:previous_dynofallkid title:>
480   next: <:ifNextDynofallkid:>Y<:or:>N<:eif:>
481   previous: <:ifPreviousDynofallkid:>Y<:or:>N<:eif:>
482 <:iterator end dynallkids_of:>
483 TEMPLATE
484 Two One 
485   next: Y
486   previous: N
487 One  Two
488   next: N
489   previous: Y
490
491 EXPECTED
492
493 dyn_template_test "dynallkidsof nested", $parent, <<TEMPLATE, <<EXPECTED;
494 <:iterator begin dynallkids_of $parent->{id} filter: [title] =~ /o/i :><:
495 dynofallkid title:><:iterator begin dynallkids_of2 dynofallkid:>
496   <:dynofallkid2 title:><:iterator end dynallkids_of2:>
497 <:iterator end dynallkids_of:>
498 TEMPLATE
499 Two
500   Grandkid
501 One
502
503 EXPECTED
504
505 dyn_template_test "dynallkidsof nested filtered cond", $parent, <<TEMPLATE, <<EXPECTED;
506 <:iterator begin dynallkids_of dynarticle:><:dynofallkid title:><:if Dynallkids_of2 dynofallkid filter: [title] =~ /G/:><:iterator begin dynallkids_of2 dynofallkid filter: [title] =~ /G/:>
507   <:dynofallkid2 title:><:iterator end dynallkids_of2:><:or Dynallkids_of2:>
508   No G kids<:eif Dynallkids_of2:>
509 <:iterator end dynallkids_of:>
510 TEMPLATE
511 Parent
512   No G kids
513 Three
514   No G kids
515 Two
516   Grandkid
517 One
518   No G kids
519
520 EXPECTED
521
522 dyn_template_test "dynallkids_of move_dynofallkid", $parent, <<TEMPLATE, <<EXPECTED;
523 <:iterator begin dynallkids_of dynarticle:><:dynofallkid title:><:move_dynofallkid:>
524 <:iterator end dynallkids_of:>
525 TEMPLATE
526 Parent
527 Three
528 Two
529 One
530
531 EXPECTED
532
533 ############################################################
534 # Cleanup
535
536 BSE::Admin::StepParents->del($parent, $parent);
537 $grandkid->remove($cfg);
538 for my $kid (reverse @kids) {
539   my $name = $kid->{title};
540   my $kidid = $kid->{id};
541   $kid->remove($cfg);
542   ok(1, "removing kid $name ($kidid)");
543 }
544 $parent->remove($cfg);
545 ok(1, "removed parent");
546
547 sub add_article {
548   my (%parms) = @_;
549
550   my $article = bse_make_article(cfg => $cfg, parentid => -1, %parms);
551
552   $article;
553 }
554
555 sub template_test($$$$) {
556   my ($tag, $article, $template, $expected) = @_;
557
558   #diag "Template >$template<";
559   my $gen = 
560     eval {
561       (my $filename = $article->{generator}) =~ s!::!/!g;
562       $filename .= ".pm";
563       require $filename;
564       $article->{generator}->new(cfg => $cfg, top => $article);
565     };
566   ok($gen, "$tag: created generator $article->{generator}");
567   diag $@ unless $gen;
568   my $content;
569  SKIP: {
570     skip "$tag: couldn't make generator", 1 unless $gen;
571     eval {
572       $content =
573         $gen->generate_low($template, $article, 'Articles', 0);
574     };
575     ok($content, "$tag: generate content");
576     diag $@ unless $content;
577   }
578  SKIP: {
579      skip "$tag: couldn't gen content", 1 unless $content;
580      is($content, $expected, "$tag: comparing");
581    }
582 }
583
584 sub dyn_template_test($$$$) {
585   my ($tag, $article, $template, $expected) = @_;
586
587   #diag "Template >$template<";
588   my $gen = 
589     eval {
590       my $gen_class = $article->{generator};
591       $gen_class =~ s/.*\W//;
592       $gen_class = "BSE::Dynamic::".$gen_class;
593       (my $filename = $gen_class) =~ s!::!/!g;
594       $filename .= ".pm";
595       require $filename;
596       $gen_class->new($req);
597     };
598   ok($gen, "$tag: created generator $article->{generator}");
599   diag $@ unless $gen;
600
601   # get the template - always regen it
602   my $work_template = _generate_dyn_template($article, $template);
603
604   my $result;
605  SKIP: {
606     skip "$tag: couldn't make generator", 1 unless $gen;
607     eval {
608       $result =
609         $gen->generate($article, $work_template);
610     };
611     ok($result, "$tag: generate content");
612     diag $@ unless $result;
613   }
614  SKIP: {
615      skip "$tag: couldn't gen content", 1 unless $result;
616      is($result->{content}, $expected, "$tag: comparing");
617    }
618 }
619
620 sub _generate_dyn_template {
621   my ($article, $template) = @_;
622
623   my $articles = 'Articles';
624   my $genname = $article->{generator};
625   eval "use $genname";
626   $@ && die $@;
627   my $gen = $genname->new(articles=>$articles, cfg=>$cfg, top=>$article);
628
629   return $gen->generate_low($template, $article, $articles, 0);
630 }