Waiter.pm 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package Selenium::Waiter;
  2. # ABSTRACT: Provides a utility wait_until function
  3. use Try::Tiny;
  4. require Exporter;
  5. our @ISA = qw/Exporter/;
  6. our @EXPORT = qw/wait_until/;
  7. =head1 SYNOPSIS
  8. use Selenium::Waiter qw/wait_until/;
  9. my $d = Selenium::Remote::Driver->new;
  10. my $div = wait_until { $d->find_element('div', css') };
  11. =func wait_until
  12. Exported by default, it takes a BLOCK (required) and optionally a
  13. hash of configuration params. It uses a prototype to take its
  14. arguments, so usage looks look like:
  15. use Selenium::Waiter;
  16. my $div = wait_until { $driver->find_element('div', css') };
  17. The above snippet will search for C<css=div> for thirty seconds; if it
  18. ever finds the element, it will immediately return. More generally,
  19. Once the BLOCK returns anything truthy, the C<wait_until> will stop
  20. evaluating and the return of the BLOCK will be returned to you. If the
  21. BLOCK never returns a truthy value, we'll wait until the elapsed time
  22. has increased past the timeout and then return an empty string C<''>.
  23. B<Achtung!> Please make sure that the BLOCK you pass in can be
  24. executed in a timely fashion. For Webdriver, that means that you
  25. should set the appropriate C<implicit_wait> timeout low (a second or
  26. less!) so that we can rerun the assert sub repeatedly. We don't do
  27. anything fancy behind the scenes: we just execute the BLOCK you pass
  28. in and sleep between iterations. If your BLOCK actively blocks for
  29. thirty seconds, like a C<find_element> would do with an
  30. C<implicit_wait> of 30 seconds, we won't be able to help you at all -
  31. that blocking behavior is on the webdriver server side, and is out of
  32. our control. We'd run one iteration, get blocked for thirty seconds,
  33. and return control to you at that point.
  34. =head4 Dying
  35. PLEASE check the return value before proceeding, as we unwisely
  36. suppress any attempts your BLOCK may make to die or croak. The BLOCK
  37. you pass is called in a L<Try::Tiny/try>, and if any of the
  38. invocations of your function throw and the BLOCK never becomes true,
  39. we'll carp exactly once at the end immediately before returning
  40. false. We overwrite the death message from each iteration, so at the
  41. end, you'll only see the most recent death message.
  42. # warns once after thirty seconds: "kept from dying";
  43. wait_until { die 'kept from dying' };
  44. The output of C<die>s from each iteration can be exposed if you wish
  45. to see the massacre:
  46. # carps: "kept from dying" once a second for thirty seconds
  47. wait_until { die 'kept from dying' } debug => 1;
  48. =head4 Timeouts and Intervals
  49. You can also customize the timeout, and/or the retry interval between
  50. iterations.
  51. # prints hi three four times at 0, 3, 6, and 9 seconds
  52. wait_until { print 'hi'; '' } timeout => 10, interval => 3;
  53. =cut
  54. sub wait_until (&%) {
  55. my $assert = shift;
  56. my $args = {
  57. timeout => 30,
  58. interval => 1,
  59. debug => 0,
  60. @_
  61. };
  62. my $start = time;
  63. my $timeout_not_elapsed = sub {
  64. my $elapsed = time - $start;
  65. return $elapsed < $args->{timeout};
  66. };
  67. my $exception = '';
  68. while ($timeout_not_elapsed->()) {
  69. my $try_ret = try {
  70. my $assert_ret = $assert->();
  71. return $assert_ret if $assert_ret;
  72. }
  73. catch {
  74. $exception = $_;
  75. warn $_ if $args->{debug};
  76. return '';
  77. }
  78. finally {
  79. sleep($args->{interval});
  80. };
  81. return $try_ret if $try_ret;
  82. }
  83. # No need to repeat ourselves if we're already debugging.
  84. warn $exception if $exception && ! $args->{debug};
  85. return '';
  86. }
  87. 1;