Waiter.pm 3.9 KB

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