testrail-report 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #! /usr/bin/env perl
  2. # ABSTRACT: Upload your TAP results to TestRail after they've finished
  3. # PODNAME: testrail-report
  4. =head1 SYNOPSIS
  5. testrail-report [OPTIONS] tapfile
  6. prove -v sometest.t > results.tap && testrail-report [OPTIONS] results.tap
  7. prove -v sometest.t | testrail-report [OPTIONS]
  8. prove -PTestRail='http://some.testlink.install/,someUser,somePassword,someProject,someRun,0,step_results' sometest.t
  9. =head1 DESCRIPTION
  10. testrail-report - report raw TAP results to a TestRail install
  11. USAGE:
  12. =head2 PARAMETERS:
  13. =head3 MANDATORY PARAMETERS
  14. --project [someproject] : associate results (if any) with theprovided project name.
  15. --run [somerun] : associates results (if any) with the provided run name.
  16. IF none of these options are provided, you will be asked to type
  17. these in as needed, supposing you are not redirecting input
  18. (such as piping into this command).
  19. =head3 CONFIG OVERRIDES
  20. In your \$HOME, put a file called .testrailrc with key=value
  21. syntax separated by newlines. Valid Keys are: apiurl,user,password
  22. =head3 CONFIG OPTIONS
  23. These override the config, if present. If neither are used, you will be prompted.
  24. --apiurl [url] : full URL to get to TestRail index document
  25. --password [key] : Your TestRail Password.
  26. --user [name] : Your TestRail User Name.
  27. =head3 BEHAVIOR
  28. --case-ok : Whether to consider each OK to correspond to a test in TestRail
  29. --step-results [name] : 'System Name' of a 'step_results' type field to set for your tests.
  30. These options are mutually exclusive. If neither is set, the
  31. overall result of the test will be used as the pass/fail for the test.
  32. =head2 PROVE PLUGIN:
  33. passing -PTestRail=apiurl,user,pass,project,run to prove will
  34. automatically upload your test results while the test is running if
  35. real-time results are desired.
  36. See L<App::Prove::Plugin::TestRail> for more information.
  37. =head2 REQUIREMENTS:
  38. Your TestRail install must have 3 custom statuses with the internal
  39. names 'skip', 'todo_pass', and 'todo_fail', to represent those
  40. states which TAP can have.
  41. =head2 TESTING OPTIONS:
  42. --mock don't do any real HTTP requests.
  43. =cut
  44. use strict;
  45. use warnings;
  46. use Getopt::Long;
  47. use Term::ANSIColor qw(colorstrip);
  48. use Test::Rail::Parser;
  49. use IO::Interactive::Tiny ();
  50. print "testrail-report\n----------------------\n";
  51. sub help {
  52. print "testrail-report - report raw TAP results to a TestRail install
  53. USAGE:
  54. testrail-report [OPTIONS] tapfile
  55. prove -v sometest.t > results.tap && testrail-report [OPTIONS] \\
  56. results.tap
  57. prove -v sometest.t | testrail-report [OPTIONS]
  58. prove -PTestRail='http://some.testlink.install/','someUser',\\
  59. 'somePassword' sometest.t
  60. PARAMETERS:
  61. [MANDATORY PARAMETERS]
  62. --project [someproject] : associate results (if any) with the
  63. provided project name.
  64. --run [somerun] : associates results (if any) with the provided run
  65. name.
  66. IF none of these options are provided, you will be asked to type
  67. these in as needed, supposing you are not redirecting input
  68. (such as piping into this command).
  69. [CONFIG OVERRIDES]
  70. In your \$HOME, put a file called .testrailrc with key=value
  71. syntax separated by newlines. Valid Keys are: apiurl,user,password
  72. [CONFIG OPTIONS] - These override the config, if present.
  73. If neither are used, you will be prompted.
  74. --apiurl [url] : full URL to get to TestRail index document
  75. --password [key] : Your TestRail Password.
  76. --user [name] : Your TestRail User Name.
  77. [BEHAVIOR]
  78. --case-ok : Whether to consider each OK to correspond to
  79. a test in TestRail
  80. --step-results [name] : 'System Name' of a 'step_results' type field
  81. to set for your tests.
  82. These options are mutually exclusive. If neither is set, the
  83. overall result of the test will be used as the pass/fail for the test.
  84. PROVE PLUGIN:
  85. passing -PTestRail=apiurl,user,pass,project,run to prove will
  86. automatically upload your test results while the test is running if
  87. real-time results are desired.
  88. See App::Prove::Plugin::TestRail for more information.
  89. REQUIREMENTS:
  90. Your TestRail install must have 3 custom statuses with the internal
  91. names 'skip', 'todo_pass', and 'todo_fail', to represent those
  92. states which TAP can have.
  93. TESTING OPTIONS:
  94. --mock don't do any real HTTP requests.
  95. ";
  96. exit 0;
  97. }
  98. sub userInput {
  99. $| = 1;
  100. my $rt = <STDIN>;
  101. chomp $rt;
  102. return $rt;
  103. }
  104. sub parseConfig {
  105. my $results = {};
  106. my $arr =[];
  107. open(my $fh, '<', $ENV{"HOME"} . '/.testrailrc') or return (undef,undef,undef);#couldn't open!
  108. while (<$fh>) {
  109. chomp;
  110. @$arr = split(/=/,$_);
  111. if (scalar(@$arr) != 2) {
  112. warn("Could not parse $_ in tlreport config\n");
  113. next;
  114. }
  115. $results->{lc($arr->[0])} = $arr->[1];
  116. }
  117. close($fh);
  118. return ($results->{'apiurl'},$results->{'password'},$results->{'user'});
  119. }
  120. #Main loop------------
  121. my ($help,$apiurl,$user,$password,$project,$run,$case_per_ok,$step_results,$mock);
  122. #parse switches
  123. GetOptions(
  124. 'run=s' => \$run,
  125. 'apiurl=s' => \$apiurl,
  126. 'password=s' => \$password,
  127. 'user=s' => \$user,
  128. 'project=s' => \$project,
  129. 'case-ok' => \$case_per_ok,
  130. 'step-results=s' => \$step_results,
  131. 'mock' => \$mock,
  132. 'help' => \$help
  133. );
  134. if ($help) { help(); }
  135. #Parse config file if we are missing api url/key or user
  136. if (-e $ENV{"HOME"} . '/.testrailrc' && (!$apiurl || !$password || !$user) ) {
  137. ($apiurl,$password,$user) = parseConfig();
  138. }
  139. #XXX not even close to optimized, don't slurp in the future
  140. #If argument is passed use it instead of stdin
  141. my $file = $ARGV[0];
  142. die "No Such File $file" if ($file && !-e $file);
  143. my ($fh,$fcontents,@files);
  144. if ($file) {
  145. open($fh,'<',$file);
  146. while (<$fh>) {
  147. $_ = colorstrip($_); #strip prove brain damage
  148. s/^\s*//g; #Fix more brain damage
  149. if ($_ =~ /(.*)\s\.\.$/) {
  150. #File marker from default prove
  151. unless ($_ =~ /^[ok|not ok] - /) {
  152. push(@files,$fcontents) if $fcontents;
  153. $fcontents = '';
  154. }
  155. }
  156. $fcontents .= $_;
  157. }
  158. close($fh);
  159. push(@files,$fcontents) if $fcontents;
  160. } else {
  161. #Just read STDIN, print help if no file was passed
  162. if (IO::Interactive::Tiny::is_interactive()) {
  163. print "ERROR: no file passed, and no data piped in! See --help for usage.\n";
  164. exit(1);
  165. }
  166. if ( !$run || !$apiurl || !$password || !$user || !$project ) {
  167. print "ERROR: Interactive mode not allowed when piping input. See --help for options.\n";
  168. exit(1);
  169. }
  170. while (<>) {
  171. $_ = colorstrip($_); #strip prove brain damage
  172. s/^\s*//g; #Fix prove brain damage
  173. if ($_ =~ /(.*)\s\.\.$/) {
  174. #File marker from default prove
  175. unless ($_ =~ /^[ok|not ok] - /) {
  176. push(@files,$fcontents) if $fcontents;
  177. $fcontents = '';
  178. }
  179. }
  180. $fcontents .= $_;
  181. }
  182. help() if !$fcontents; #Nothing passed to stdin!
  183. push(@files,$fcontents) if $fcontents;
  184. }
  185. #Interrogate user if they didn't provide info
  186. if (!$apiurl) {
  187. print "Type the API endpoint url for your testLink install below:\n";
  188. $apiurl = userInput();
  189. }
  190. if (!$user) {
  191. print "Type your testLink user name below:\n";
  192. $user = userInput();
  193. }
  194. if (!$password) {
  195. print "Type the password for your testLink user below:\n";
  196. $password = userInput();
  197. }
  198. if (!$apiurl || !$password || !$user) {
  199. print "ERROR: api url, username and password cannot be blank.\n";
  200. exit 1;
  201. }
  202. #Interrogate user if they didn't provide info
  203. if (!$project) {
  204. print "Type the name of the project you are testing under:\n";
  205. $project = userInput();
  206. }
  207. # Interrogate user if options were not passed
  208. if (!$run) {
  209. print "Type the name of the existing run you would like to run against:\n";
  210. $run = userInput();
  211. }
  212. my $debug = 0;
  213. my $browser;
  214. if ($mock) {
  215. use Test::LWP::UserAgent::TestRailMock;
  216. $browser = $Test::LWP::UserAgent::TestRailMock::mockObject;
  217. $debug = 1;
  218. }
  219. my $tap;
  220. foreach my $phil (@files) {
  221. print "Processing $phil...\n";
  222. $tap = Test::Rail::Parser->new({
  223. 'tap' => $phil,
  224. 'apiurl' => $apiurl,
  225. 'user' => $user,
  226. 'pass' => $password,
  227. 'run' => $run,
  228. 'project' => $project,
  229. 'case_per_ok' => $case_per_ok,
  230. 'step_results' => $step_results,
  231. 'debug' => $debug,
  232. 'browser' => $browser,
  233. 'merge' => 1
  234. });
  235. $tap->run();
  236. }
  237. print "Done.\n";
  238. #all done
  239. 0;
  240. __END__
  241. =head1 SEE ALSO
  242. L<TestRail::API>
  243. L<App::Prove::Plugin::TestRail>
  244. L<TAP::Parser>
  245. =head1 SPECIAL THANKS
  246. Thanks to cPanel Inc, for graciously funding the creation of this module.