Jar.pm 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package Selenium::Driver::SeleniumHQ::Jar;
  2. use strict;
  3. use warnings;
  4. use v5.28;
  5. no warnings 'experimental';
  6. use feature qw/signatures/;
  7. use Carp qw{confess};
  8. use File::Basename qw{basename};
  9. use File::Path qw{make_path};
  10. use File::Spec();
  11. use XML::LibXML();
  12. use HTTP::Tiny();
  13. #ABSTRACT: Download the latest version of seleniumHQ's selenium.jar, and tell Selenium::Client how to spawn it
  14. =head1 Mode of Operation
  15. Downloads the latest Selenium JAR (or the provided driver_version).
  16. Expects java to already be installed.
  17. Spawns a selnium server on the provided port (which the caller will assign randomly)
  18. Pipes log output to ~/.selenium/perl-client/$port.log
  19. Uses a config file ~/.selenium/perl-client/$port.toml if the selenium version supports this
  20. =head1 SUBROUTINES
  21. =head2 build_spawn_opts($class,$object)
  22. Builds a command string which can run the driver binary.
  23. All driver classes must build this.
  24. =cut
  25. our $index = 'http://selenium-release.storage.googleapis.com';
  26. sub build_spawn_opts($class,$object) {
  27. $object->{driver_class} = $class;
  28. $object->{driver_interpreter} //= 'java';
  29. $object->{driver_version} //= '';
  30. $object->{log_file} //= File::Spec->catfile($object->{client_dir},"perl-client","selenium-$object->{port}.log");
  31. ($object->{driver_file}, $object->{driver_major_version}) = find_and_fetch( File::Spec->catdir($object->{client_dir},"jars"), $object->{driver_version},$object->{ua});
  32. $object->{driver_config} //= _build_config($object);
  33. #XXX port in config is currently IGNORED
  34. my @java_opts;
  35. my @config = ((qw{standalone --config}), $object->{driver_config}, '--port', $object->{port});
  36. # Handle older seleniums that are WC3 compliant
  37. if ( $object->{driver_major_version} < 4 ) {
  38. $object->{prefix} = '/wd/hub';
  39. @java_opts = qw{-Dwebedriver.gecko.driver=geckodriver -Dwebdriver.chrome.driver=chromedriver};
  40. @config = ();
  41. }
  42. # Build command string
  43. # XXX relies on gecko/chromedriver in $PATH
  44. $object->{command} //= [
  45. $object->{driver_interpreter},
  46. @java_opts,
  47. qw{-jar},
  48. $object->{driver_file},
  49. @config,
  50. ];
  51. return $object;
  52. }
  53. sub _build_config($self) {
  54. my $dir = File::Spec->catdir($self->{client_dir},"perl-client");
  55. make_path( $dir ) unless -d $dir;
  56. my $file = File::Spec->catfile($dir,"config-$self->{port}.toml");
  57. return $file if -f $file;
  58. # TODO add some self-signed SSL to this
  59. my $config = <<~EOF;
  60. [node]
  61. detect-drivers = true
  62. [server]
  63. allow-cors = true
  64. hostname = "localhost"
  65. max-threads = 36
  66. port = --PORT--
  67. [logging]
  68. enable = true
  69. log-encoding = UTF-8
  70. log-file = --REPLACE--
  71. plain-logs = true
  72. structured-logs = false
  73. tracing = true
  74. EOF
  75. #XXX double escape backslash because windows; like YAML, TOML is a poor choice always
  76. #XXX so, you'll die if there are backslashes in your username or homedir choice (lunatic)
  77. my $log_corrected = $self->{log_file};
  78. $log_corrected =~ s/\\/\\\\/g;
  79. $config =~ s/--REPLACE--/\"$log_corrected\"/gm;
  80. $config =~ s/--PORT--/$self->{port}/gm;
  81. File::Slurper::write_text($file, $config);
  82. return $file;
  83. }
  84. =head2 find_and_fetch($dir STRING, $version STRING, $user_agent HTTP::Tiny)
  85. Does an index lookup of the various selenium JARs available and returns either the latest one
  86. or the version provided. Stores the JAR in the provided directory.
  87. =cut
  88. sub find_and_fetch($dir, $version='', $ua='') {
  89. $ua ||= HTTP::Tiny->new();
  90. my $res = $ua->get($index);
  91. confess "$res->{reason} :\n$res->{content}\n" unless $res->{success};
  92. my $parsed = XML::LibXML->load_xml(string => $res->{content});
  93. #XXX - XPATH NO WORKY, HURR DURR
  94. my @files;
  95. foreach my $element ($parsed->findnodes('//*')) {
  96. my $contents = $element->getChildrenByTagName("Contents");
  97. my @candidates = sort { $b cmp $a } grep { m/selenium-server/ && m/\.jar$/ } map {
  98. $_->getChildrenByTagName('Key')->to_literal().'';
  99. } @$contents;
  100. push(@files,@candidates);
  101. }
  102. @files = grep { m/\Q$version\E/ } @files if $version;
  103. my $jar = shift @files;
  104. my $url = "$index/$jar";
  105. make_path( $dir ) unless -d $dir;
  106. my $fname = File::Spec->catfile($dir, basename($jar));
  107. my ($v) = $fname =~ m/-(\d)\.\d\.\d.*\.jar$/;
  108. return ($fname,$v) if -f $fname;
  109. $res = $ua->mirror($url, $fname);
  110. confess "$res->{reason} :\n$res->{content}\n" unless $res->{success};
  111. return ($fname,$v);
  112. }
  113. 1;