Driver.pm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. package Test::Selenium::Remote::Driver;
  2. # ABSTRACT: Useful testing subclass for Selenium::Remote::Driver
  3. use Moo;
  4. use Test::Selenium::Remote::WebElement;
  5. use Test::LongString;
  6. use IO::Socket;
  7. use Sub::Install;
  8. use Try::Tiny;
  9. extends 'Selenium::Remote::Driver';
  10. # move_mouse_to_location_ok # TODO # move_to_ok # TODO
  11. has func_list => (
  12. is => 'lazy',
  13. builder => sub {
  14. return [
  15. 'alert_text_is', 'alert_text_isnt', 'alert_text_like',
  16. 'alert_text_unlike', 'current_window_handle_is',
  17. 'current_window_handle_isnt', 'current_window_handle_like',
  18. 'current_window_handle_unlike', 'window_handles_is',
  19. 'window_handles_isnt', 'window_handles_like',
  20. 'window_handles_unlike', 'window_size_is', 'window_size_isnt',
  21. 'window_size_like', 'window_size_unlike', 'window_position_is',
  22. 'window_position_isnt', 'window_position_like',
  23. 'window_position_unlike', 'current_url_is', 'current_url_isnt',
  24. 'current_url_like', 'current_url_unlike', 'title_is',
  25. 'title_isnt', 'title_like', 'title_unlike', 'active_element_is',
  26. 'active_element_isnt', 'active_element_like',
  27. 'active_element_unlike', 'send_keys_to_active_element_ok',
  28. 'send_keys_to_alert_ok', 'send_keys_to_prompt_ok',
  29. 'send_modifier_ok', 'accept_alert_ok', 'dismiss_alert_ok',
  30. 'get_ok', 'go_back_ok', 'go_forward_ok', 'add_cookie_ok',
  31. 'get_page_source_ok', 'find_element_ok', 'find_elements_ok',
  32. 'find_child_element_ok', 'find_child_elements_ok',
  33. 'find_no_element_ok',
  34. 'compare_elements_ok', 'click_ok', 'double_click_ok',
  35. 'body_like',
  36. ];
  37. },
  38. );
  39. sub has_args {
  40. my $self = shift;
  41. my $fun_name = shift;
  42. my $hash_fun_args = {
  43. 'find_element' => 2,
  44. 'find_no_element' => 2,
  45. 'find_child_element' => 3,
  46. 'find_child_elements' => 3,
  47. 'find_element' => 2,
  48. 'find_elements' => 2,
  49. 'compare_elements' => 2,
  50. 'get' => 1,
  51. };
  52. return ( $hash_fun_args->{$fun_name} // 0 );
  53. }
  54. with 'Test::Selenium::Remote::Role::DoesTesting';
  55. has verbose => (
  56. is => 'rw',
  57. );
  58. sub BUILD {
  59. my $self = shift;
  60. foreach my $method_name ( @{ $self->func_list } ) {
  61. unless ( defined( __PACKAGE__->can($method_name) ) ) {
  62. my $sub = $self->_build_sub($method_name);
  63. Sub::Install::install_sub(
  64. { code => $sub,
  65. into => __PACKAGE__,
  66. as => $method_name
  67. }
  68. );
  69. }
  70. }
  71. }
  72. =head1 NAME
  73. Test::Selenium::Remote::Driver
  74. =head1 DESCRIPTION
  75. A subclass of L<Selenium::Remote::Driver>. which provides useful testing
  76. functions.
  77. This is an I<experimental> addition to the Selenium::Remote::Driver
  78. distribution, and some interfaces may change.
  79. =head1 Methods
  80. =head2 new ( %opts )
  81. This will create a new Test::Selenium::Remote::Driver object, which subclasses
  82. L<Selenium::Remote::Driver>. This subclass provides useful testing
  83. functions. It is modeled on L<Test::WWW::Selenium>.
  84. Environment vars can be used to specify options to pass to
  85. L<Selenium::Remote::Driver>. ENV vars are prefixed with C<TWD_>.
  86. ( After the old fork name, "Test::WebDriver" )
  87. Set the Selenium server address with C<$TWD_HOST> and C<$TWD_PORT>.
  88. Pick which browser is used using the C<$TWD_BROWSER>, C<$TWD_VERSION>,
  89. C<$TWD_PLATFORM>, C<$TWD_JAVASCRIPT>, C<$TWD_EXTRA_CAPABILITIES>.
  90. See L<Selenium::Driver::Remote> for the meanings of these options.
  91. =cut
  92. sub BUILDARGS {
  93. my ( $class, %p ) = @_;
  94. for my $opt (
  95. qw/remote_server_addr port browser_name version platform
  96. javascript auto_close extra_capabilities/
  97. )
  98. {
  99. $p{$opt} //= $ENV{ 'TWD_' . uc($opt) };
  100. }
  101. $p{browser_name} //= $ENV{TWD_BROWSER}; # ykwim
  102. $p{remote_server_addr} //= $ENV{TWD_HOST}; # ykwim
  103. $p{webelement_class} //= 'Test::Selenium::Remote::WebElement';
  104. return \%p;
  105. }
  106. =head2 server_is_running( $host, $port )
  107. Returns true if a Selenium server is running. The host and port
  108. parameters are optional, and default to C<localhost:4444>.
  109. Environment vars C<TWD_HOST> and C<TWD_PORT> can also be used to
  110. determine the server to check.
  111. =cut
  112. sub server_is_running {
  113. my $class_or_self = shift;
  114. my $host = $ENV{TWD_HOST} || shift || 'localhost';
  115. my $port = $ENV{TWD_PORT} || shift || 4444;
  116. return ( $host, $port )
  117. if IO::Socket::INET->new(
  118. PeerAddr => $host,
  119. PeerPort => $port,
  120. );
  121. return;
  122. }
  123. =head1 Testing Methods
  124. The following testing methods are available. For
  125. more documentation, see the related test methods in L<Selenium::Remote::Driver>
  126. (And feel free to submit a patch to flesh out the documentation for these here).
  127. alert_text_is
  128. alert_text_isnt
  129. alert_text_like
  130. alert_text_unlike
  131. current_window_handle_is
  132. current_window_handle_isnt
  133. current_window_handle_like
  134. current_window_handle_unlike
  135. window_handles_is
  136. window_handles_isnt
  137. window_handles_like
  138. window_handles_unlike
  139. window_size_is
  140. window_size_isnt
  141. window_size_like
  142. window_size_unlike
  143. window_position_is
  144. window_position_isnt
  145. window_position_like
  146. window_position_unlike
  147. current_url_is
  148. current_url_isnt
  149. current_url_like
  150. current_url_unlike
  151. title_is
  152. title_isnt
  153. title_like
  154. title_unlike
  155. active_element_is
  156. active_element_isnt
  157. active_element_like
  158. active_element_unlike
  159. # Basically the same as 'content_like()', but content_like() supports multiple regex's.
  160. page_source_is
  161. page_source_isnt
  162. page_source_like
  163. page_source_unlike
  164. send_keys_to_active_element_ok
  165. send_keys_to_alert_ok
  166. send_keys_to_prompt_ok
  167. send_modifier_ok
  168. accept_alert_ok
  169. dismiss_alert_ok
  170. move_mouse_to_location_ok # TODO
  171. move_to_ok # TODO
  172. get_ok
  173. go_back_ok
  174. go_forward_ok
  175. add_cookie_ok
  176. get_page_source_ok
  177. find_element_ok($search_target)
  178. find_element_ok($search_target)
  179. find_elements_ok
  180. find_child_element_ok
  181. find_child_elements_ok
  182. compare_elements_ok
  183. click_ok
  184. double_click_ok
  185. =cut
  186. # function composing a find_element with locator with a webelement test
  187. sub _find_element_with_action {
  188. my $self = shift;
  189. my $method = shift;
  190. my ( $locator, $locator_strategy, $params, $desc ) = @_;
  191. # case 4 args
  192. if ($desc) {
  193. $self->croak('Invalid locator strategy')
  194. unless ( $self->FINDERS->{$locator_strategy} );
  195. }
  196. else {
  197. if ($params) {
  198. # means that we called it the 'old way' (no locator strategy)
  199. if ( !defined( $self->FINDERS->{$locator_strategy} ) ) {
  200. $desc = $params;
  201. $params = $locator_strategy;
  202. $locator_strategy =
  203. $self->_get_finder_key( $self->default_finder );
  204. }
  205. }
  206. else {
  207. # means it was called with no locator strategy and no desc
  208. if ($locator_strategy) {
  209. if ( !defined( $self->FINDERS->{$locator_strategy} ) ) {
  210. $params = $locator_strategy;
  211. $locator_strategy =
  212. $self->_get_finder_key( $self->default_finder );
  213. }
  214. }
  215. else {
  216. $self->croak('Not enough arguments');
  217. }
  218. }
  219. }
  220. unless ($desc) {
  221. $desc = $method;
  222. $desc .= "'" . join( " ", ( $params // '' ) ) . "'";
  223. }
  224. return $self->find_element( $locator, $locator_strategy )
  225. ->$method( $params, $desc );
  226. }
  227. =head2 $twd->type_element_ok($search_target [,$locator], $keys, [, $desc ]);
  228. $twd->type_element_ok( $search_target [,$locator], $keys [, $desc ] );
  229. Use L<Selenium::Remote::Driver/find_element> to resolve the C<$search_target>
  230. to a web element and an optional locator, and then type C<$keys> into it, providing an optional test
  231. label.
  232. =cut
  233. sub type_element_ok {
  234. my $self = shift;
  235. my $method = 'send_keys_ok';
  236. return $self->_find_element_with_action( $method, @_ );
  237. }
  238. =head2 $twd->element_text_is($search_target[,$finder],$expected_text [,$desc]);
  239. $twd->element_text_is($search_target[,$finder],$expected_text [,$desc]);
  240. =cut
  241. sub element_text_is {
  242. my $self = shift;
  243. my $method = 'text_is';
  244. return $self->_find_element_with_action( $method, @_ );
  245. }
  246. =head2 $twd->element_value_is($search_target[,$finder],$expected_value [,$desc]);
  247. $twd->element_value_is($search_target[,$finder],$expected_value [,$desc]);
  248. =cut
  249. sub element_value_is {
  250. my $self = shift;
  251. my $method = 'value_is';
  252. return $self->_find_element_with_action( $method, @_ );
  253. }
  254. =head2 $twd->click_element_ok($search_target [,$desc]);
  255. $twd->click_element_ok($search_target [,$desc]);
  256. Find an element and then click on it.
  257. =cut
  258. sub click_element_ok {
  259. my $self = shift;
  260. my $method = 'click_ok';
  261. return $self->_find_element_with_action( $method, @_ );
  262. }
  263. =head2 $twd->clear_element_ok($search_target [,$desc]);
  264. $twd->clear_element_ok($search_target [,$desc]);
  265. Find an element and then clear on it.
  266. =cut
  267. sub clear_element_ok {
  268. my $self = shift;
  269. my $method = 'clear_ok';
  270. return $self->_find_element_with_action( $method, @_ );
  271. }
  272. =head2 $twd->is_element_displayed_ok($search_target [,$desc]);
  273. $twd->is_element_displayed_ok($search_target [,$desc]);
  274. Find an element and check to confirm that it is displayed. (visible)
  275. =cut
  276. sub is_element_displayed_ok {
  277. my $self = shift;
  278. my $method = 'is_displayed_ok';
  279. return $self->_find_element_with_action( $method, @_ );
  280. }
  281. =head2 $twd->is_element_enabled_ok($search_target [,$desc]);
  282. $twd->is_element_enabled_ok($search_target [,$desc]);
  283. Find an element and check to confirm that it is enabled.
  284. =cut
  285. sub is_element_enabled_ok {
  286. my $self = shift;
  287. my $method = 'is_enabled_ok';
  288. return $self->_find_element_with_action( $method, @_ );
  289. }
  290. =head2 $twd->find_element_ok($search_target [,$finder, $desc ]);
  291. $twd->find_element_ok( $search_target [,$finder, $desc ] );
  292. Returns true if C<$search_target> is successfully found on the page. C<$search_target>
  293. is passed to L<Selenium::Remote::Driver/find_element> using a finder or the C<default_finder>
  294. if none passed.
  295. See there for more details on the format for C<find_element_ok()>.
  296. =cut
  297. =head2 $twd->find_no_element_ok($search_target [,$finder, $desc ]);
  298. $twd->find_no_element_ok( $search_target [,$finder, $desc ] );
  299. Returns true if C<$search_target> is I<not> found on the page. C<$search_target>
  300. is passed to L<Selenium::Remote::Driver/find_element> using a finder or the
  301. C<default_finder> if none passed.See there for more details on the format.
  302. for C<find_no_element_ok()>.
  303. =cut
  304. =head2 $twd->content_like( $regex [, $desc ] )
  305. $twd->content_like( $regex [, $desc ] )
  306. $twd->content_like( [$regex_1, $regex_2] [, $desc ] )
  307. Tells if the content of the page matches I<$regex>. If an arrayref of regex's
  308. are provided, one 'test' is run for each regex against the content of the
  309. current page.
  310. A default description of 'Content is like "$regex"' will be provided if there
  311. is no description.
  312. =cut
  313. sub content_like {
  314. my $self = shift;
  315. my $regex = shift;
  316. my $desc = shift;
  317. local $Test::Builder::Level = $Test::Builder::Level + 1;
  318. my $content = $self->get_page_source();
  319. my $ret;
  320. if ( not ref $regex eq 'ARRAY' ) {
  321. $desc = qq{Content is like "$regex"} if ( not defined $desc );
  322. $ret = like_string( $content, $regex, $desc );
  323. if ( !$ret ) {
  324. $self->error_handler->($self,"Failed to find $regex");
  325. }
  326. return $ret;
  327. }
  328. elsif ( ref $regex eq 'ARRAY' ) {
  329. for my $re (@$regex) {
  330. $desc = qq{Content is like "$re"} if ( not defined $desc );
  331. $ret = like_string( $content, $re, $desc );
  332. if ( !$ret ) {
  333. $self->error_handler->($self,"Failed to find $re");
  334. }
  335. }
  336. }
  337. }
  338. =head2 $twd->content_unlike( $regex [, $desc ] )
  339. $twd->content_unlike( $regex [, $desc ] )
  340. $twd->content_unlike( [$regex_1, $regex_2] [, $desc ] )
  341. Tells if the content of the page does NOT match I<$regex>. If an arrayref of regex's
  342. are provided, one 'test' is run for each regex against the content of the
  343. current page.
  344. A default description of 'Content is unlike "$regex"' will be provided if there
  345. is no description.
  346. =cut
  347. sub content_unlike {
  348. my $self = shift;
  349. my $regex = shift;
  350. my $desc = shift;
  351. local $Test::Builder::Level = $Test::Builder::Level + 1;
  352. my $content = $self->get_page_source();
  353. my $ret;
  354. if ( not ref $regex eq 'ARRAY' ) {
  355. $desc = qq{Content is unlike "$regex"} if ( not defined $desc );
  356. $ret = unlike_string( $content, $regex, $desc );
  357. if ( !$ret ) {
  358. $self->error_handler->($self,"Failed to find $regex");
  359. }
  360. }
  361. elsif ( ref $regex eq 'ARRAY' ) {
  362. for my $re (@$regex) {
  363. $desc = qq{Content is unlike "$re"} if ( not defined $desc );
  364. $ret = unlike_string( $content, $re, $desc );
  365. if ( !$ret ) {
  366. $self->error_handler->($self,"Failed to find $re");
  367. }
  368. }
  369. }
  370. }
  371. =head2 $twd->body_text_like( $regex [, $desc ] )
  372. $twd->body_text_like( $regex [, $desc ] )
  373. $twd->body_text_like( [$regex_1, $regex_2] [, $desc ] )
  374. Tells if the text of the page (as returned by C<< get_body() >>) matches
  375. I<$regex>. If an arrayref of regex's are provided, one 'test' is run for each
  376. regex against the content of the current page.
  377. A default description of 'Content is like "$regex"' will be provided if there
  378. is no description.
  379. To also match the HTML see, C<< content_unlike() >>.
  380. =cut
  381. sub body_text_like {
  382. my $self = shift;
  383. my $regex = shift;
  384. my $desc = shift;
  385. local $Test::Builder::Level = $Test::Builder::Level + 1;
  386. my $text = $self->get_body();
  387. my $ret;
  388. if ( not ref $regex eq 'ARRAY' ) {
  389. $desc = qq{Text is like "$regex"} if ( not defined $desc );
  390. $ret = like_string( $text, $regex, $desc );
  391. if ( !$ret ) {
  392. $self->error_handler->($self,"Failed to find $regex");
  393. }
  394. return $ret;
  395. }
  396. elsif ( ref $regex eq 'ARRAY' ) {
  397. for my $re (@$regex) {
  398. $desc = qq{Text is like "$re"} if ( not defined $desc );
  399. $ret = like_string( $text, $re, $desc );
  400. if ( !$ret ) {
  401. $self->error_handler->($self,"Failed to find $re");
  402. }
  403. }
  404. }
  405. }
  406. =head2 $twd->body_text_unlike( $regex [, $desc ] )
  407. $twd->body_text_unlike( $regex [, $desc ] )
  408. $twd->body_text_unlike( [$regex_1, $regex_2] [, $desc ] )
  409. Tells if the text of the page (as returned by C<< get_body() >>)
  410. does NOT match I<$regex>. If an arrayref of regex's
  411. are provided, one 'test' is run for each regex against the content of the
  412. current page.
  413. A default description of 'Text is unlike "$regex"' will be provided if there
  414. is no description.
  415. To also match the HTML see, C<< content_unlike() >>.
  416. =cut
  417. sub body_text_unlike {
  418. my $self = shift;
  419. my $regex = shift;
  420. my $desc = shift;
  421. local $Test::Builder::Level = $Test::Builder::Level + 1;
  422. my $text = $self->get_body();
  423. my $ret;
  424. if ( not ref $regex eq 'ARRAY' ) {
  425. $desc = qq{Text is unlike "$regex"} if ( not defined $desc );
  426. $ret = unlike_string( $text, $regex, $desc );
  427. if ( !$ret ) {
  428. $self->error_handler->($self,"Failed to find $regex");
  429. }
  430. return $ret;
  431. }
  432. elsif ( ref $regex eq 'ARRAY' ) {
  433. for my $re (@$regex) {
  434. $desc = qq{Text is unlike "$re"} if ( not defined $desc );
  435. $ret = unlike_string( $text, $re, $desc );
  436. if ( !$ret ) {
  437. $self->error_handler->($self,"Failed to find $re");
  438. }
  439. }
  440. }
  441. }
  442. #####
  443. =head2 $twd->content_contains( $str [, $desc ] )
  444. $twd->content_contains( $str [, $desc ] )
  445. $twd->content_contains( [$str_1, $str_2] [, $desc ] )
  446. Tells if the content of the page contains I<$str>. If an arrayref of strngs's
  447. are provided, one 'test' is run for each string against the content of the
  448. current page.
  449. A default description of 'Content contains "$str"' will be provided if there
  450. is no description.
  451. =cut
  452. sub content_contains {
  453. my $self = shift;
  454. my $str = shift;
  455. my $desc = shift;
  456. local $Test::Builder::Level = $Test::Builder::Level + 1;
  457. my $content = $self->get_page_source();
  458. my $ret;
  459. if ( not ref $str eq 'ARRAY' ) {
  460. $desc = qq{Content contains "$str"} if ( not defined $desc );
  461. $ret = contains_string( $content, $str, $desc );
  462. if ( !$ret ) {
  463. $self->error_handler->($self,"Failed to find $str");
  464. }
  465. return $ret;
  466. }
  467. elsif ( ref $str eq 'ARRAY' ) {
  468. for my $s (@$str) {
  469. $desc = qq{Content contains "$s"} if ( not defined $desc );
  470. $ret = contains_string( $content, $s, $desc );
  471. if ( !$ret ) {
  472. $self->error_handler->($self,"Failed to find $s");
  473. }
  474. }
  475. }
  476. }
  477. =head2 $twd->content_lacks( $str [, $desc ] )
  478. $twd->content_lacks( $str [, $desc ] )
  479. $twd->content_lacks( [$str_1, $str_2] [, $desc ] )
  480. Tells if the content of the page does NOT contain I<$str>. If an arrayref of strings
  481. are provided, one 'test' is run for each string against the content of the
  482. current page.
  483. A default description of 'Content lacks "$str"' will be provided if there
  484. is no description.
  485. =cut
  486. sub content_lacks {
  487. my $self = shift;
  488. my $str = shift;
  489. my $desc = shift;
  490. local $Test::Builder::Level = $Test::Builder::Level + 1;
  491. my $content = $self->get_page_source();
  492. my $ret;
  493. if ( not ref $str eq 'ARRAY' ) {
  494. $desc = qq{Content lacks "$str"} if ( not defined $desc );
  495. $ret = lacks_string( $content, $str, $desc );
  496. if ( !$ret ) {
  497. $self->error_handler->($self,"Failed to find $str");
  498. }
  499. return $ret;
  500. }
  501. elsif ( ref $str eq 'ARRAY' ) {
  502. for my $s (@$str) {
  503. $desc = qq{Content lacks "$s"} if ( not defined $desc );
  504. $ret = lacks_string( $content, $s, $desc );
  505. if ( !$ret ) {
  506. $self->error_handler->($self,"Failed to find $s");
  507. }
  508. }
  509. }
  510. }
  511. =head2 $twd->body_text_contains( $str [, $desc ] )
  512. $twd->body_text_contains( $str [, $desc ] )
  513. $twd->body_text_contains( [$str_1, $str_2] [, $desc ] )
  514. Tells if the text of the page (as returned by C<< get_body() >>) contains
  515. I<$str>. If an arrayref of strings are provided, one 'test' is run for each
  516. regex against the content of the current page.
  517. A default description of 'Text contains "$str"' will be provided if there
  518. is no description.
  519. To also match the HTML see, C<< content_uncontains() >>.
  520. =cut
  521. sub body_text_contains {
  522. my $self = shift;
  523. my $str = shift;
  524. my $desc = shift;
  525. local $Test::Builder::Level = $Test::Builder::Level + 1;
  526. my $text = $self->get_body();
  527. my $ret;
  528. if ( not ref $str eq 'ARRAY' ) {
  529. $desc = qq{Text contains "$str"} if ( not defined $desc );
  530. $ret = contains_string( $text, $str, $desc );
  531. if ( !$ret ) {
  532. $self->error_handler->($self,"Failed to find $str");
  533. }
  534. return $ret;
  535. }
  536. elsif ( ref $str eq 'ARRAY' ) {
  537. for my $s (@$str) {
  538. $desc = qq{Text contains "$s"} if ( not defined $desc );
  539. $ret = contains_string( $text, $s, $desc );
  540. if ( !$ret ) {
  541. $self->error_handler->($self,"Failed to find $s");
  542. }
  543. }
  544. }
  545. }
  546. =head2 $twd->body_text_lacks( $str [, $desc ] )
  547. $twd->body_text_lacks( $str [, $desc ] )
  548. $twd->body_text_lacks( [$str_1, $str_2] [, $desc ] )
  549. Tells if the text of the page (as returned by C<< get_body() >>)
  550. does NOT contain I<$str>. If an arrayref of strings
  551. are provided, one 'test' is run for each regex against the content of the
  552. current page.
  553. A default description of 'Text is lacks "$str"' will be provided if there
  554. is no description.
  555. To also match the HTML see, C<< content_lacks() >>.
  556. =cut
  557. sub body_text_lacks {
  558. my $self = shift;
  559. my $str = shift;
  560. my $desc = shift;
  561. local $Test::Builder::Level = $Test::Builder::Level + 1;
  562. my $text = $self->get_body();
  563. my $ret;
  564. if ( not ref $str eq 'ARRAY' ) {
  565. $desc = qq{Text is lacks "$str"} if ( not defined $desc );
  566. $ret = lacks_string( $text, $str, $desc );
  567. if ( !$ret ) {
  568. $self->error_handler->($self,"Failed to find $str");
  569. }
  570. return $ret;
  571. }
  572. elsif ( ref $str eq 'ARRAY' ) {
  573. for my $s (@$str) {
  574. $desc = qq{Text is lacks "$s"} if ( not defined $desc );
  575. $ret = lacks_string( $text, $s, $desc );
  576. if ( !$ret ) {
  577. $self->error_handler->($self,"Failed to find $s");
  578. }
  579. }
  580. }
  581. }
  582. 1;
  583. __END__
  584. =head1 NOTES
  585. This module was forked from Test::WebDriver 0.01.
  586. For Best Practice - I recommend subclassing Test::Selenium::Remote::Driver for your application,
  587. and then refactoring common or app specific methods into MyApp::WebDriver so that
  588. your test files do not have much duplication. As your app changes, you can update
  589. MyApp::WebDriver rather than all the individual test files.
  590. =head1 AUTHORS
  591. =over 4
  592. =item *
  593. Created by: Luke Closs <lukec@cpan.org>, but inspired by
  594. L<Test::WWW::Selenium> and its authors.
  595. =back
  596. =head1 CONTRIBUTORS
  597. Test::WebDriver work was sponsored by Prime Radiant, Inc.
  598. Mark Stosberg <mark@stosberg.com> forked it as Test::Selenium::Remote::Driver
  599. and significantly expanded it.
  600. =head1 COPYRIGHT AND LICENSE
  601. Parts Copyright (c) 2012 Prime Radiant, Inc.
  602. This program is free software; you can redistribute it and/or
  603. modify it under the same terms as Perl itself.