]> git.imager.perl.org - bse.git/blame - t/BSE/Test.pm
flash a notice on article deletion
[bse.git] / t / BSE / Test.pm
CommitLineData
5ba09b9b
TC
1package BSE::Test;
2use strict;
3use vars qw(@ISA @EXPORT @EXPORT_OK);
5bbf7309
TC
4use Exporter 'import';
5@EXPORT = qw(base_url fetch_ok make_url skip make_ua);
c0bf9781 6@EXPORT_OK = qw(base_url base_securl make_ua fetch_url fetch_ok make_url skip
de30d08a 7 make_post check_form post_ok check_content follow_ok
9b89923c 8 follow_refresh_ok click_ok config test_actions);
5bbf7309
TC
9use lib 'site/cgi-bin/modules';
10use BSE::Cfg;
5ba09b9b 11
5bbf7309 12my $conffile = $ENV{BSETEST} || 'install.cfg';
5ba09b9b 13
5bbf7309
TC
14my $cfg = BSE::Cfg->new
15 (
16 path => "site/cgi-bin",
17 extra_file => $conffile,
18 );
d2730773 19
5bbf7309
TC
20sub config {
21 $cfg;
5ba09b9b 22}
5ba09b9b 23
5bbf7309
TC
24sub base_url {
25 $cfg->entryVar("site", "url");
6e3d2da5
TC
26}
27
5bbf7309
TC
28sub base_securl {
29 $cfg->entryVar("site", "secureurl");
30}
3bc94f98 31
5bbf7309
TC
32sub base_dir {
33 $cfg->entryVar("paths", "siteroot");
34}
3bc94f98 35
5bbf7309
TC
36sub mysql_name {
37 $cfg->entry("binaries", "mysql", "mysql");
9168c88c
TC
38}
39
5bbf7309
TC
40sub test_dsn {
41 $cfg->entry("db", "dsn");
42}
2a295ea9 43
5bbf7309
TC
44sub test_dbuser {
45 $cfg->entry("db", "user");
2a295ea9
TC
46}
47
5bbf7309
TC
48sub test_dbpass {
49 $cfg->entry("db", "password");
50}
5ba09b9b 51
5bbf7309
TC
52sub test_dbclass {
53 $cfg->entry("db", "class", "BSE::DB::Mysql");
54}
5ba09b9b 55
5bbf7309
TC
56sub test_sessionclass {
57 $cfg->entry("basic", "session_class", "Apache::Session::Mysql");
5ba09b9b
TC
58}
59
5bbf7309
TC
60sub test_perl {
61 $cfg->entry("paths", "perl", $^X);
62}
5ba09b9b 63
5bbf7309
TC
64sub test_conffile {
65 $conffile;
5ba09b9b
TC
66}
67
68sub make_ua {
aefcabcb 69 require WWW::Mechanize;
5ba09b9b 70 require "HTTP/Cookies.pm";
0da5d199 71 my $ua = WWW::Mechanize->new(onerror => undef);
5ba09b9b
TC
72 $ua->cookie_jar(HTTP::Cookies->new);
73
74 $ua;
75}
76
77# in scalar context returns content
78# in list context returns ($content, $code)
79# $post is any data to be part of a post request
80sub fetch_url {
81 my ($ua, $url, $method, $post) = @_;
82
83 $method ||= 'GET';
3bc94f98
TC
84 my $hdrs = HTTP::Headers->new;
85 $hdrs->header(Content_Length => length $post) if $post;
86 my $req = HTTP::Request->new($method, $url, $hdrs);
87 $req->content($post) if $post;
5ba09b9b
TC
88 my $resp = $ua->request($req);
89 if (wantarray) {
90 return ($resp->content(), $resp->code(), $resp->is_success,
91 $resp->headers_as_string());
92 }
93 else {
94 return $resp->is_success() ? $resp->content() : undef;
95 }
96}
97
98sub make_url {
99 my ($base, @data) = @_;
100
101 require "URI/Escape.pm";
102 my @pairs;
103 while (my ($key, $value) = splice(@data, 0, 2)) {
104 push(@pairs, "$key=".URI::uri_escape($value));
105 }
106 return $base."?".join("&",@pairs);
107}
108
7063727a
TC
109sub check_content {
110 my ($content, $note, $match) = @_;
5bbf7309
TC
111 my $tb = Test::Builder->new;
112 local $Test::Builder::Level = $Test::Builder::Level + 1;
113
114 return $tb->like($content, qr/$match/s, "$note: match");
7063727a
TC
115}
116
3bc94f98
TC
117sub _check_fetch {
118 my ($content, $code, $ok, $headers,
119 $note, $match, $headmatch) = @_;
5ba09b9b 120
5bbf7309
TC
121 my $tb = Test::Builder->new;
122 local $Test::Builder::Level = $Test::Builder::Level + 1;
123
3bc94f98 124 my $good = $ok;
5bbf7309
TC
125 $tb->ok($ok, "$note: fetch ($code)");
126 SKIP:
127 {
128 my $count = 0;
129 $count++ if $match;
130 $count++ if $headmatch;
131 $ok or skip("$note: fetch failed", $count) if $count;
5ba09b9b 132 if ($match) {
5bbf7309
TC
133 unless ($tb->like($content, qr/$match/s, "$note: match")) {
134 #print "# wanted /$match/ got:\n";
135 #my $copy = $content;
136 #$copy =~ s/^/# /gm;
137 #$copy .= "\n" unless $copy =~ /\n\z/;
138 #print $copy;
3bc94f98 139 $good = 0;
5ba09b9b
TC
140 }
141 }
142 if ($headmatch) {
5bbf7309
TC
143 unless ($tb->like($headers, qr/$headmatch/s, "$note: headmatch")) {
144 #print "# wanted /$headmatch/ got:\n";
145 #my $copy = $headers;
146 #$copy =~ s/^/# /gm;
147 #$copy .= "\n" unless $copy =~ /\n\z/;
148 #print $copy;
3bc94f98 149 $good = 0;
5ba09b9b
TC
150 }
151 }
152 }
3bc94f98
TC
153
154 if (wantarray) {
155 return ($content, $code, $good, $headers);
156 }
157 else {
158 return $good ? $content : undef;
159 }
160}
161
162sub make_post {
163 my (@data) = @_;
164
165 require "URI/Escape.pm";
166 my @pairs;
167 while (my ($key, $value) = splice(@data, 0, 2)) {
168 push(@pairs, "$key=".URI::Escape::uri_escape($value));
169 }
170 return join("&",@pairs);
171}
172
173sub post_ok {
174 my ($ua, $note, $url, $data, $match, $headmatch) = @_;
175
176 $data = make_post(@$data) if ref $data;
177
178 my ($content, $code, $ok, $headers) = fetch_url($ua, $url, POST=>$data);
179
180 return _check_fetch($content, $code, $ok, $headers,
181 $note, $match, $headmatch)
182}
183
184sub fetch_ok {
185 my ($ua, $note, $url, $match, $headmatch) = @_;
186
ebe779a5
TC
187 my $resp = $ua->get($url);
188 my $ok = $resp->is_success;
7063727a
TC
189 return _check_fetch($ua->{content}, $ua->{status}, $ok,
190 $ua->{res}->headers_as_string, $note,
191 $match, $headmatch)
3bc94f98
TC
192}
193
de30d08a
TC
194sub follow_ok {
195 my ($ua, $note, $link, $match, $headmatch) = @_;
196
ebe779a5
TC
197 my $ok;
198 if (ref $link) {
199 my $resp = $ua->follow_link(%$link);
200 $ok = $resp->is_success;
201 }
202 else {
0da5d199 203 $ok = $ua->follow_link(text_regex => qr/\Q$link/);
ebe779a5 204 }
de30d08a
TC
205
206 return _check_fetch($ua->{content}, $ua->{status}, $ok,
207 $ua->{res}->headers_as_string, $note,
208 $match, $headmatch)
209}
210
211sub follow_refresh_ok {
212 my ($ua, $note, $match, $headmatch) = @_;
213
214 my $skip = 1;
215 ++$skip if $match;
216 ++$headmatch if $headmatch;
217 my $refresh = $ua->response->header('Refresh');
218 if (ok($refresh, "$note - refresh header")) {
219 my $url;
220 if ($refresh =~ /^\s*\d+\s*;\s*url=\"([^\"]+)\"/
221 or $refresh =~ /^\s*\d+\s*;\s*url\s*=\s*(\S+)/) {
222 $url = $1;
223 $url = URI->new_abs($url, $ua->uri);
224 }
225 else {
226 $url = $ua->uri;
227 }
228 print "# refresh to $url\n";
229 fetch_ok($ua, "$note - fetch", $url);
230 }
231 else {
232 skip("$note - skipped, not a refresh", $skip);
233 }
234}
235
236sub click_ok {
237 my ($ua, $note, $name, $match, $headmatch) = @_;
238
5bbf7309
TC
239 local $Test::Builder::Level = $Test::Builder::Level + 1;
240 my $tb = Test::Builder->new;
241
242 my $ok = $tb->ok($ua->click($name), "$note - click");
de30d08a
TC
243 return _check_fetch($ua->{content}, $ua->{status}, $ok,
244 $ua->{res}->headers_as_string, $note,
245 $match, $headmatch)
246}
247
3bc94f98
TC
248sub check_form {
249 my ($content, $note, %checks) = @_;
250
251 require 'HTML/Parser.pm';
252 require 'HTML/Entities.pm';
253 my $in;
254 my $keep;
255 my $saved = '';
256 my %todo = %checks;
257 my $inselect;
258 my $selname;
259 my $checked_sel_value;
260 my $textname;
261 my %values;
262
5bbf7309
TC
263 my $tb = Test::Builder->new;
264 local $Test::Builder::Level = $Test::Builder::Level + 1;
265
3bc94f98
TC
266 my $text =
267 sub {
268 my ($t) = @_;
269 $saved .= $t if $keep;
270 };
271 my $start =
272 sub {
273 my ($tagname, $attr) = @_;
274
275 if ($tagname eq 'input') {
276 my $name = $attr->{name};
277 if ($name && $todo{$name}) {
5bbf7309 278 $tb->ok(1, "$note - $name - field is present");
3bc94f98
TC
279 $values{$name} = $attr->{$name};
280 if (defined $todo{$name}[0]) {
281 my $cvalue = $checks{$name}[0];
282 my $fvalue = $attr->{value};
283 if (defined $fvalue) {
5bbf7309 284 $tb->ok($cvalue eq $fvalue, "$note - $name - comparing values");
3bc94f98
TC
285 }
286 else {
5bbf7309 287 $tb->ok(0, "$note - $name - value is not present");
3bc94f98
TC
288 }
289 }
290 if (defined $todo{$name}[1]) {
291 my $ttype = $todo{$name}[1];
292 my $ftype = $attr->{type};
293 if (defined $ftype) {
5bbf7309 294 $tb->ok($ttype eq $ftype, "$note - $name - comparing types");
3bc94f98
TC
295 }
296 else {
5bbf7309 297 $tb->ok(0, "$note - $name - type not present");
3bc94f98
TC
298 }
299 }
300 delete $todo{$name};
301 }
302 }
303 elsif ($tagname eq 'select') {
304 $selname = $attr->{name};
305
306 if ($todo{$selname}) {
5bbf7309 307 $tb->ok(1, "$note - $selname - field is present");
3bc94f98
TC
308 $inselect = 1;
309 if (defined $todo{$selname}[1]) {
310 $checked_sel_value = 0;
311 my $ttype = $todo{$selname}[1];
5bbf7309 312 $tb->ok ($ttype eq 'select', "$note - $selname - checking type (select)");
3bc94f98
TC
313 }
314 }
315 }
316 elsif ($tagname eq 'option' && $inselect) {
317 unless (exists $attr->{value}) {
318 print "# warning - option in select $selname missing value\n";
319 }
320 if (exists $attr->{selected}) {
321 $checked_sel_value = 1;
322 $values{$selname} = $attr->{value};
323 if (defined $todo{$selname}[0]) {
324 my $fvalue = $attr->{value};
325 my $tvalue = $todo{$selname}[0];
326 if (defined $fvalue) {
5bbf7309 327 $tb->ok($fvalue eq $tvalue, "$note - $selname - checking value ($fvalue vs $tvalue)");
3bc94f98
TC
328 }
329 else {
5bbf7309 330 $tb->ok(0, "$note - $selname - no value supplied");
3bc94f98
TC
331 }
332 }
333 }
334 }
335 elsif ($tagname eq 'textarea') {
336 $textname = $attr->{name};
337 $saved = '';
338 ++$keep;
339 }
340 };
341 my $end =
342 sub {
343 my ($tagname) = @_;
344
345 if ($tagname eq 'select' && $inselect) {
346 if (!$checked_sel_value) {
5bbf7309 347 $tb->ok(0, "$note - $selname - no value selected");
3bc94f98
TC
348 }
349 delete $todo{$selname};
350 }
351 elsif ($tagname eq 'textarea') {
352 $keep = 0;
353 if ($todo{$textname}) {
354 my $fvalue = HTML::Entities::decode_entities($saved);
355 $values{$textname} = $fvalue;
5bbf7309 356 $tb->ok(1, "$note - $textname - field exists");
3bc94f98
TC
357 if (defined $todo{$textname}[0]) {
358 my $tvalue = $todo{$textname}[0];
5bbf7309 359 $tb->ok($tvalue eq $fvalue, "$note - $textname - checking value($tvalue vs $fvalue)");
3bc94f98
TC
360 }
361 if (defined $todo{$textname}[1]) {
5bbf7309 362 $tb->ok ($todo{$textname}[1] eq 'textarea',
3bc94f98
TC
363 "$note - $textname - check field type");
364 }
365 delete $todo{$textname};
366 }
367 }
368 };
369 my $p = HTML::Parser->new( text_h => [ $text, "dtext" ],
370 start_h => [ $start, "tagname, attr" ],
371 end_h => [ $end, "tagname" ]);
372 $p->parse($content);
373 $p->eof;
374 for my $name (keys %todo) {
5bbf7309 375 $tb->ok(0, "$note - $name - field doesn't exist");
3bc94f98
TC
376 my $count = 0;
377 ++$count if defined $todo{$name}[0];
378 ++$count if defined $todo{$name}[1];
5bbf7309
TC
379 SKIP: {
380 skip("$note - $name - no field", $count) if $count;
381 }
3bc94f98
TC
382 }
383
384 return %values;
5ba09b9b
TC
385}
386
9b89923c
TC
387# test that all actions have methods for a given dispatcher class
388sub test_actions {
389 my ($class) = @_;
390
391 my $tb = Test::Builder->new;
5bbf7309 392 local $Test::Builder::Level = $Test::Builder::Level + 1;
9b89923c
TC
393
394 my $obj = $class->new;
395 my $actions = $obj->actions;
396 my @bad;
397 for my $action (sort keys %$actions) {
398 my $method = "req_$action";
399 unless ($obj->can($method)) {
400 push @bad, $action;
401 }
402 }
403 $tb->ok(!@bad, "check all actions have a method for $class");
404 print STDERR "No method found for $class action $_\n" for @bad;
405
406 return !@bad;
407}
408
5ba09b9b
TC
4091;
410