DoesTesting.pm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package Test::Selenium::Remote::Role::DoesTesting;
  2. # ABSTRACT: Role to cope with everything that is related to testing (could
  3. # be reused in both testing classes)
  4. use Moo::Role;
  5. use Test::Builder;
  6. use Try::Tiny;
  7. use Scalar::Util 'blessed';
  8. use List::Util qw/any/;
  9. use namespace::clean;
  10. requires qw(func_list has_args);
  11. has _builder => (
  12. is => 'lazy',
  13. builder => sub { return Test::Builder->new() },
  14. handles => [qw/is_eq isnt_eq like unlike ok croak/],
  15. );
  16. # get back the key value from an already coerced finder (default finder)
  17. sub _get_finder_key {
  18. my $self = shift;
  19. my $finder_value = shift;
  20. foreach my $k ( keys %{ $self->FINDERS } ) {
  21. return $k if ( $self->FINDERS->{$k} eq $finder_value );
  22. }
  23. return;
  24. }
  25. # main method for non ok tests
  26. sub _check_method {
  27. my $self = shift;
  28. my $method = shift;
  29. my $method_to_test = shift;
  30. $method = "get_$method";
  31. my @args = @_;
  32. my $rv;
  33. try {
  34. my $num_of_args = $self->has_args($method);
  35. my @r_args = splice( @args, 0, $num_of_args );
  36. $rv = $self->$method(@r_args);
  37. }
  38. catch {
  39. $self->croak($_);
  40. };
  41. return $self->$method_to_test( $rv, @args );
  42. }
  43. # main method for _ok tests
  44. # a bit hacked so that find_no_element_ok can also be processed
  45. sub _check_ok {
  46. my $self = shift;
  47. my $method = shift;
  48. my @args = @_;
  49. my ( $rv, $num_of_args, @r_args );
  50. try {
  51. $num_of_args = $self->has_args($method);
  52. @r_args = splice( @args, 0, $num_of_args );
  53. if ( $method =~ m/^find(_no|_child)?_element/ ) {
  54. # case find_element_ok was called with no arguments
  55. if ( scalar(@r_args) - $num_of_args == 1 ) {
  56. push @r_args, $self->_get_finder_key( $self->default_finder );
  57. }
  58. else {
  59. if ( scalar(@r_args) == $num_of_args ) {
  60. # case find_element was called with no finder but
  61. # a test description
  62. my $finder = $r_args[ $num_of_args - 1 ];
  63. my @FINDERS = keys( %{ $self->FINDERS } );
  64. unless ( any { $finder eq $_ } @FINDERS ) {
  65. $r_args[ $num_of_args - 1 ] =
  66. $self->_get_finder_key( $self->default_finder );
  67. push @args, $finder;
  68. }
  69. }
  70. }
  71. }
  72. # quick hack to fit 'find_no_element' into check_ok logic
  73. if ( $method eq 'find_no_element' ) {
  74. # If we use `find_element` and find nothing, the error
  75. # handler is incorrectly invoked. Doing a `find_elements`
  76. # and checking that it returns an empty array does not
  77. # invoke the error_handler. See
  78. # https://github.com/gempesaw/Selenium-Remote-Driver/issues/253
  79. my $elements = $self->find_elements(@r_args);
  80. if ( @{$elements} ) {
  81. $rv = $elements->[0];
  82. }
  83. else {
  84. $rv = 1; # empty list means success
  85. }
  86. }
  87. else {
  88. $rv = $self->$method(@r_args); # a true $rv means success
  89. }
  90. }
  91. catch {
  92. if ($method eq 'find_no_element') {
  93. $rv = 1; # an exception from find_elements() means success
  94. }
  95. else {
  96. $self->croak($_);
  97. }
  98. };
  99. # test description might have been explicitly passed
  100. my $test_name = pop @args;
  101. # generic test description when no explicit test description was passed
  102. if ( ! defined $test_name ) {
  103. $test_name = $num_of_args > 0 ?
  104. join( ' ', $method, map { q{'$_'} } @r_args )
  105. :
  106. $method;
  107. }
  108. # case when find_no_element found an element, we should croak
  109. if ( $method eq 'find_no_element' ) {
  110. if ( blessed($rv) && $rv->isa('Selenium::Remote::WebElement') ) {
  111. $self->croak($test_name);
  112. }
  113. }
  114. return $self->ok( $rv, $test_name );
  115. }
  116. # build the subs with the correct arg set
  117. sub _build_sub {
  118. my $self = shift;
  119. my $meth_name = shift;
  120. # e.g. for $meth_name = 'find_no_element_ok':
  121. # $meth_comp = 'ok'
  122. # $meth_without_comp = 'find_no_element'
  123. my @meth_elements = split '_', $meth_name;
  124. my $meth_comp = pop @meth_elements;
  125. my $meth_without_comp = join '_', @meth_elements;
  126. # handle the ok testing methods
  127. if ( $meth_comp eq 'ok' ) {
  128. return sub {
  129. my $self = shift;
  130. local $Test::Builder::Level = $Test::Builder::Level + 2;
  131. return $self->_check_ok($meth_without_comp, @_);
  132. };
  133. }
  134. # find the Test::More comparator method
  135. my %comparators = (
  136. is => 'is_eq',
  137. isnt => 'isnt_eq',
  138. like => 'like',
  139. unlike => 'unlike',
  140. );
  141. # croak on unknown comparator methods
  142. if ( ! exists $comparators{$meth_comp} ) {
  143. return sub {
  144. my $self = shift;
  145. return $self->croak("Sub $meth_name could not be defined");
  146. };
  147. }
  148. # handle check in _check_method()
  149. return sub {
  150. my $self = shift;
  151. local $Test::Builder::Level = $Test::Builder::Level + 2;
  152. return $self->_check_method( $meth_without_comp, $comparators{$meth_comp}, @_ );
  153. };
  154. }
  155. 1;
  156. =head1 NAME
  157. Selenium::Remote::Role::DoesTesting - Role implementing the common logic used for testing
  158. =cut